source: mainline/kernel/generic/src/mm/frame.c@ 65f3117

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 65f3117 was 65f3117, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 2 years ago

Make bootstrap stack statically, rather than dynamically allocated

With aligment requirements being part of the language now, it is
simple to allocate the extra stack area in kernel data, and we
don't need to go to so much trouble with manual allocation.
It also makes it slightly more straightforward to use the stack
from assembly, without having to dig through a saved context
structure.

  • Property mode set to 100644
File size: 36.8 KB
RevLine 
[f761f1eb]1/*
[df4ed85]2 * Copyright (c) 2001-2005 Jakub Jermar
3 * Copyright (c) 2005 Sergey Bondari
[5f0f29ce]4 * Copyright (c) 2009 Martin Decky
[f761f1eb]5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
[174156fd]31/** @addtogroup kernel_generic_mm
[b45c443]32 * @{
33 */
34
[9179d0a]35/**
[b45c443]36 * @file
[5f0f29ce]37 * @brief Physical frame allocator.
[9179d0a]38 *
39 * This file contains the physical frame allocator and memory zone management.
[b0c2075]40 * The frame allocator is built on top of the two-level bitmap structure.
[9179d0a]41 *
42 */
43
[d99c1d2]44#include <typedefs.h>
[f761f1eb]45#include <mm/frame.h>
[89bcb520]46#include <mm/reserve.h>
[20d50a1]47#include <mm/as.h>
[f761f1eb]48#include <panic.h>
[63e27ef]49#include <assert.h>
[5c9a08b]50#include <adt/list.h>
[1a1744e]51#include <synch/mutex.h>
52#include <synch/condvar.h>
[18e0a6c]53#include <arch/asm.h>
[9c0a9b3]54#include <arch.h>
[bab75df6]55#include <stdio.h>
[b2fa1204]56#include <log.h>
[9ebc238]57#include <align.h>
[085d973]58#include <mm/slab.h>
[bb68433]59#include <bitops.h>
[93165be]60#include <macros.h>
[d630139]61#include <config.h>
[933cadf]62#include <str.h>
[1066041]63#include <proc/thread.h> /* THREAD */
[18e0a6c]64
[e49e234]65zones_t zones;
[f761f1eb]66
[1a1744e]67/*
68 * Synchronization primitives used to sleep when there is no memory
69 * available.
70 */
[da1bafb]71static mutex_t mem_avail_mtx;
72static condvar_t mem_avail_cv;
73static size_t mem_avail_req = 0; /**< Number of frames requested. */
74static size_t mem_avail_gen = 0; /**< Generation counter. */
[a294ad0]75
[deaf8d5]76/** Initialize frame structure.
[84dd253]77 *
[5f0f29ce]78 * @param frame Frame structure to be initialized.
79 *
[f761f1eb]80 */
[8df5f20]81_NO_TRACE static void frame_initialize(frame_t *frame)
[f761f1eb]82{
[b0c2075]83 frame->refcount = 0;
84 frame->parent = NULL;
[f761f1eb]85}
86
[d1582b50]87/*
88 * Zones functions
89 */
[085d973]90
[deaf8d5]91/** Insert-sort zone into zones list.
[bb68433]92 *
[5f0f29ce]93 * Assume interrupts are disabled and zones lock is
94 * locked.
95 *
96 * @param base Base frame of the newly inserted zone.
97 * @param count Number of frames of the newly inserted zone.
98 *
99 * @return Zone number on success, -1 on error.
100 *
[84dd253]101 */
[8df5f20]102_NO_TRACE static size_t zones_insert_zone(pfn_t base, size_t count,
[e2650d3]103 zone_flags_t flags)
[f761f1eb]104{
[71eef11]105 if (zones.count + 1 == ZONES_MAX) {
[b2fa1204]106 log(LF_OTHER, LVL_ERROR, "Maximum zone count %u exceeded!",
107 ZONES_MAX);
[98000fb]108 return (size_t) -1;
[71eef11]109 }
[a35b458]110
[98000fb]111 size_t i;
[b6b576c]112 for (i = 0; i < zones.count; i++) {
[5f0f29ce]113 /* Check for overlap */
[e2650d3]114 if (overlaps(zones.info[i].base, zones.info[i].count,
115 base, count)) {
[a35b458]116
[e2650d3]117 /*
118 * If the overlaping zones are of the same type
119 * and the new zone is completely within the previous
120 * one, then quietly ignore the new zone.
121 *
122 */
[a35b458]123
[e2650d3]124 if ((zones.info[i].flags != flags) ||
125 (!iswithin(zones.info[i].base, zones.info[i].count,
126 base, count))) {
[b2fa1204]127 log(LF_OTHER, LVL_WARN,
128 "Zone (%p, %p) overlaps "
129 "with previous zone (%p %p)!",
[7e752b2]130 (void *) PFN2ADDR(base), (void *) PFN2ADDR(count),
131 (void *) PFN2ADDR(zones.info[i].base),
132 (void *) PFN2ADDR(zones.info[i].count));
[e2650d3]133 }
[a35b458]134
[98000fb]135 return (size_t) -1;
[085d973]136 }
[5f0f29ce]137 if (base < zones.info[i].base)
[bb68433]138 break;
[085d973]139 }
[a35b458]140
[bb68433]141 /* Move other zones up */
[b0c2075]142 for (size_t j = zones.count; j > i; j--)
[e49e234]143 zones.info[j] = zones.info[j - 1];
[a35b458]144
[bb68433]145 zones.count++;
[a35b458]146
[bb68433]147 return i;
[f761f1eb]148}
[fcacfb7]149
[5f0f29ce]150/** Get total available frames.
151 *
152 * Assume interrupts are disabled and zones lock is
153 * locked.
[71eef11]154 *
[5f0f29ce]155 * @return Total number of available frames.
[71eef11]156 *
[eef75f6]157 */
[8df5f20]158_NO_TRACE static size_t frame_total_free_get_internal(void)
[085d973]159{
[98000fb]160 size_t total = 0;
161 size_t i;
[8d308b9]162
[5f0f29ce]163 for (i = 0; i < zones.count; i++)
164 total += zones.info[i].free_count;
[a35b458]165
[5f0f29ce]166 return total;
167}
[8d308b9]168
[8df5f20]169_NO_TRACE size_t frame_total_free_get(void)
[8d308b9]170{
171 size_t total;
172
173 irq_spinlock_lock(&zones.lock, true);
174 total = frame_total_free_get_internal();
175 irq_spinlock_unlock(&zones.lock, true);
176
177 return total;
178}
179
[e49e234]180/** Find a zone with a given frames.
[5f0f29ce]181 *
182 * Assume interrupts are disabled and zones lock is
183 * locked.
184 *
185 * @param frame Frame number contained in zone.
[e49e234]186 * @param count Number of frames to look for.
[5f0f29ce]187 * @param hint Used as zone hint.
188 *
189 * @return Zone index or -1 if not found.
190 *
191 */
[8df5f20]192_NO_TRACE size_t find_zone(pfn_t frame, size_t count, size_t hint)
[5f0f29ce]193{
[6c441cf8]194 if (hint >= zones.count)
[085d973]195 hint = 0;
[a35b458]196
[98000fb]197 size_t i = hint;
[085d973]198 do {
[3bacee1]199 if ((zones.info[i].base <= frame) &&
200 (zones.info[i].base + zones.info[i].count >= frame + count))
[5f0f29ce]201 return i;
[a35b458]202
[085d973]203 i++;
204 if (i >= zones.count)
205 i = 0;
[a35b458]206
[5f7a0ef]207 } while (i != hint);
[a35b458]208
[98000fb]209 return (size_t) -1;
[085d973]210}
[328f2934]211
[b0c2075]212/** @return True if zone can allocate specified number of frames */
[8df5f20]213_NO_TRACE static bool zone_can_alloc(zone_t *zone, size_t count,
[b0c2075]214 pfn_t constraint)
[bb68433]215{
[b0c2075]216 /*
217 * The function bitmap_allocate_range() does not modify
218 * the bitmap if the last argument is NULL.
219 */
[a35b458]220
[e6c4b94]221 return ((zone->flags & ZONE_AVAILABLE) &&
[b0c2075]222 bitmap_allocate_range(&zone->bitmap, count, zone->base,
[f72906c]223 FRAME_LOWPRIO, constraint, NULL));
224}
225
226/** Find a zone that can allocate specified number of frames
227 *
228 * This function searches among all zones. Assume interrupts are
229 * disabled and zones lock is locked.
230 *
231 * @param count Number of free frames we are trying to find.
232 * @param flags Required flags of the zone.
233 * @param constraint Indication of bits that cannot be set in the
234 * physical frame number of the first allocated frame.
235 * @param hint Preferred zone.
236 *
237 * @return Zone that can allocate specified number of frames.
238 * @return -1 if no zone can satisfy the request.
239 *
240 */
[8df5f20]241_NO_TRACE static size_t find_free_zone_all(size_t count, zone_flags_t flags,
[f72906c]242 pfn_t constraint, size_t hint)
243{
244 for (size_t pos = 0; pos < zones.count; pos++) {
245 size_t i = (pos + hint) % zones.count;
[a35b458]246
[f72906c]247 /* Check whether the zone meets the search criteria. */
248 if (!ZONE_FLAGS_MATCH(zones.info[i].flags, flags))
249 continue;
[a35b458]250
[f72906c]251 /* Check if the zone can satisfy the allocation request. */
252 if (zone_can_alloc(&zones.info[i], count, constraint))
253 return i;
254 }
[a35b458]255
[f72906c]256 return (size_t) -1;
257}
258
259/** Check if frame range priority memory
260 *
261 * @param pfn Starting frame.
262 * @param count Number of frames.
263 *
264 * @return True if the range contains only priority memory.
265 *
266 */
[8df5f20]267_NO_TRACE static bool is_high_priority(pfn_t base, size_t count)
[f72906c]268{
269 return (base + count <= FRAME_LOWPRIO);
270}
271
272/** Find a zone that can allocate specified number of frames
273 *
274 * This function ignores zones that contain only high-priority
275 * memory. Assume interrupts are disabled and zones lock is locked.
276 *
277 * @param count Number of free frames we are trying to find.
278 * @param flags Required flags of the zone.
279 * @param constraint Indication of bits that cannot be set in the
280 * physical frame number of the first allocated frame.
281 * @param hint Preferred zone.
282 *
283 * @return Zone that can allocate specified number of frames.
284 * @return -1 if no low-priority zone can satisfy the request.
285 *
286 */
[8df5f20]287_NO_TRACE static size_t find_free_zone_lowprio(size_t count, zone_flags_t flags,
[f72906c]288 pfn_t constraint, size_t hint)
[1b20da0]289{
[f72906c]290 for (size_t pos = 0; pos < zones.count; pos++) {
291 size_t i = (pos + hint) % zones.count;
[a35b458]292
[f72906c]293 /* Skip zones containing only high-priority memory. */
294 if (is_high_priority(zones.info[i].base, zones.info[i].count))
295 continue;
[a35b458]296
[f72906c]297 /* Check whether the zone meets the search criteria. */
298 if (!ZONE_FLAGS_MATCH(zones.info[i].flags, flags))
299 continue;
[a35b458]300
[f72906c]301 /* Check if the zone can satisfy the allocation request. */
302 if (zone_can_alloc(&zones.info[i], count, constraint))
303 return i;
304 }
[a35b458]305
[f72906c]306 return (size_t) -1;
[bb68433]307}
308
[b0c2075]309/** Find a zone that can allocate specified number of frames
[5f0f29ce]310 *
311 * Assume interrupts are disabled and zones lock is
312 * locked.
[fcacfb7]313 *
[b0c2075]314 * @param count Number of free frames we are trying to find.
315 * @param flags Required flags of the target zone.
316 * @param constraint Indication of bits that cannot be set in the
317 * physical frame number of the first allocated frame.
[f72906c]318 * @param hint Preferred zone.
319 *
320 * @return Zone that can allocate specified number of frames.
321 * @return -1 if no zone can satisfy the request.
[fcacfb7]322 *
323 */
[8df5f20]324_NO_TRACE static size_t find_free_zone(size_t count, zone_flags_t flags,
[b0c2075]325 pfn_t constraint, size_t hint)
[fcacfb7]326{
[085d973]327 if (hint >= zones.count)
328 hint = 0;
[a35b458]329
[f72906c]330 /*
331 * Prefer zones with low-priority memory over
332 * zones with high-priority memory.
333 */
[a35b458]334
[f72906c]335 size_t znum = find_free_zone_lowprio(count, flags, constraint, hint);
336 if (znum != (size_t) -1)
337 return znum;
[a35b458]338
[f72906c]339 /* Take all zones into account */
340 return find_free_zone_all(count, flags, constraint, hint);
[fcacfb7]341}
342
[d1582b50]343/*
344 * Zone functions
345 */
[6e8b3c8]346
[b0c2075]347/** Return frame from zone. */
[8df5f20]348_NO_TRACE static frame_t *zone_get_frame(zone_t *zone, size_t index)
[bb68433]349{
[63e27ef]350 assert(index < zone->count);
[a35b458]351
[b0c2075]352 return &zone->frames[index];
[6e8b3c8]353}
354
[deaf8d5]355/** Allocate frame in particular zone.
[085d973]356 *
[5f0f29ce]357 * Assume zone is locked and is available for allocation.
[9a68b34d]358 * Panics if allocation is impossible.
359 *
[b0c2075]360 * @param zone Zone to allocate from.
361 * @param count Number of frames to allocate
362 * @param constraint Indication of bits that cannot be set in the
363 * physical frame number of the first allocated frame.
[085d973]364 *
[5f0f29ce]365 * @return Frame index in zone.
[9a68b34d]366 *
[085d973]367 */
[8df5f20]368_NO_TRACE static size_t zone_frame_alloc(zone_t *zone, size_t count,
[b0c2075]369 pfn_t constraint)
[085d973]370{
[63e27ef]371 assert(zone->flags & ZONE_AVAILABLE);
[81d3612]372 assert(zone->free_count >= count);
[a35b458]373
[b0c2075]374 /* Allocate frames from zone */
[566da7f8]375 size_t index = (size_t) -1;
[b0c2075]376 int avail = bitmap_allocate_range(&zone->bitmap, count, zone->base,
[f72906c]377 FRAME_LOWPRIO, constraint, &index);
[a35b458]378
[0705fc5]379 (void) avail;
[63e27ef]380 assert(avail);
381 assert(index != (size_t) -1);
[a35b458]382
[b0c2075]383 /* Update frame reference count */
384 for (size_t i = 0; i < count; i++) {
385 frame_t *frame = zone_get_frame(zone, index + i);
[a35b458]386
[63e27ef]387 assert(frame->refcount == 0);
[b0c2075]388 frame->refcount = 1;
389 }
[a35b458]390
[085d973]391 /* Update zone information. */
[b0c2075]392 zone->free_count -= count;
393 zone->busy_count += count;
[a35b458]394
[b0c2075]395 return index;
[085d973]396}
397
[deaf8d5]398/** Free frame from zone.
[085d973]399 *
[5f0f29ce]400 * Assume zone is locked and is available for deallocation.
401 *
[b0c2075]402 * @param zone Pointer to zone from which the frame is to be freed.
403 * @param index Frame index relative to zone.
[eb3d379]404 *
[b0c2075]405 * @return Number of freed frames.
[89bcb520]406 *
[085d973]407 */
[8df5f20]408_NO_TRACE static size_t zone_frame_free(zone_t *zone, size_t index)
[085d973]409{
[63e27ef]410 assert(zone->flags & ZONE_AVAILABLE);
[a35b458]411
[b0c2075]412 frame_t *frame = zone_get_frame(zone, index);
[63e27ef]413 assert(frame->refcount > 0);
[a35b458]414
[085d973]415 if (!--frame->refcount) {
[81d3612]416 assert(zone->busy_count > 0);
417
[64f3d3b]418 bitmap_set(&zone->bitmap, index, 0);
[a35b458]419
[d3dfa42]420 /* Update zone information. */
[b0c2075]421 zone->free_count++;
422 zone->busy_count--;
[a35b458]423
[5df1963]424 return 1;
[085d973]425 }
[a35b458]426
[5df1963]427 return 0;
[085d973]428}
429
[deaf8d5]430/** Mark frame in zone unavailable to allocation. */
[8df5f20]431_NO_TRACE static void zone_mark_unavailable(zone_t *zone, size_t index)
[085d973]432{
[63e27ef]433 assert(zone->flags & ZONE_AVAILABLE);
[a35b458]434
[b0c2075]435 frame_t *frame = zone_get_frame(zone, index);
[81d3612]436 assert(frame->refcount <= 1);
437
[b0c2075]438 if (frame->refcount > 0)
[bb68433]439 return;
[a35b458]440
[81d3612]441 assert(zone->free_count > 0);
442
[cd3b380]443 frame->refcount = 1;
[b0c2075]444 bitmap_set_range(&zone->bitmap, index, 1);
[a35b458]445
[085d973]446 zone->free_count--;
[d060900]447 reserve_force_alloc(1);
[085d973]448}
449
[81d3612]450/** Mark frame in zone available to allocation. */
451_NO_TRACE static void zone_mark_available(zone_t *zone, size_t index)
452{
453 assert(zone->flags & ZONE_AVAILABLE);
454
455 frame_t *frame = zone_get_frame(zone, index);
456 assert(frame->refcount == 1);
457
458 frame->refcount = 0;
459 bitmap_set_range(&zone->bitmap, index, 0);
460
461 zone->free_count++;
462}
463
[5f0f29ce]464/** Merge two zones.
[bb68433]465 *
[5f0f29ce]466 * Assume z1 & z2 are locked and compatible and zones lock is
467 * locked.
[bb68433]468 *
[b0c2075]469 * @param z1 First zone to merge.
470 * @param z2 Second zone to merge.
471 * @param old_z1 Original data of the first zone.
472 * @param confdata Merged zone configuration data.
[eb3d379]473 *
[bb68433]474 */
[8df5f20]475_NO_TRACE static void zone_merge_internal(size_t z1, size_t z2, zone_t *old_z1,
[b0c2075]476 void *confdata)
[bb68433]477{
[63e27ef]478 assert(zones.info[z1].flags & ZONE_AVAILABLE);
479 assert(zones.info[z2].flags & ZONE_AVAILABLE);
480 assert(zones.info[z1].flags == zones.info[z2].flags);
481 assert(zones.info[z1].base < zones.info[z2].base);
482 assert(!overlaps(zones.info[z1].base, zones.info[z1].count,
[5f0f29ce]483 zones.info[z2].base, zones.info[z2].count));
[a35b458]484
[5f0f29ce]485 /* Difference between zone bases */
486 pfn_t base_diff = zones.info[z2].base - zones.info[z1].base;
[81d3612]487 pfn_t gap = base_diff - zones.info[z1].count;
[a35b458]488
[5f0f29ce]489 zones.info[z1].count = base_diff + zones.info[z2].count;
490 zones.info[z1].free_count += zones.info[z2].free_count;
491 zones.info[z1].busy_count += zones.info[z2].busy_count;
[a35b458]492
[b0c2075]493 bitmap_initialize(&zones.info[z1].bitmap, zones.info[z1].count,
[c5396c1]494 confdata + (sizeof(frame_t) * zones.info[z1].count));
[adb252c0]495 bitmap_clear_range(&zones.info[z1].bitmap, 0, zones.info[z1].count);
[a35b458]496
[b0c2075]497 zones.info[z1].frames = (frame_t *) confdata;
[a35b458]498
[b0c2075]499 /*
500 * Copy frames and bits from both zones to preserve parents, etc.
[bb68433]501 */
[a35b458]502
[b0c2075]503 for (size_t i = 0; i < old_z1->count; i++) {
504 bitmap_set(&zones.info[z1].bitmap, i,
505 bitmap_get(&old_z1->bitmap, i));
506 zones.info[z1].frames[i] = old_z1->frames[i];
[bb68433]507 }
[a35b458]508
[b0c2075]509 for (size_t i = 0; i < zones.info[z2].count; i++) {
510 bitmap_set(&zones.info[z1].bitmap, base_diff + i,
511 bitmap_get(&zones.info[z2].bitmap, i));
512 zones.info[z1].frames[base_diff + i] =
513 zones.info[z2].frames[i];
[bb68433]514 }
[81d3612]515
516 /*
517 * Mark the gap between the original zones as unavailable.
518 */
519
520 for (size_t i = 0; i < gap; i++) {
[5ecac7e]521 frame_initialize(&zones.info[z1].frames[old_z1->count + i]);
[81d3612]522 zone_mark_unavailable(&zones.info[z1], old_z1->count + i);
523 }
[bb68433]524}
525
[deaf8d5]526/** Return old configuration frames into the zone.
[bb68433]527 *
[5f0f29ce]528 * We have two cases:
529 * - The configuration data is outside the zone
530 * -> do nothing (perhaps call frame_free?)
531 * - The configuration data was created by zone_create
532 * or updated by reduce_region -> free every frame
533 *
534 * @param znum The actual zone where freeing should occur.
535 * @param pfn Old zone configuration frame.
536 * @param count Old zone frame count.
[874878a]537 *
[bb68433]538 */
[8df5f20]539_NO_TRACE static void return_config_frames(size_t znum, pfn_t pfn, size_t count)
[bb68433]540{
[63e27ef]541 assert(zones.info[znum].flags & ZONE_AVAILABLE);
[a35b458]542
[98000fb]543 size_t cframes = SIZE2FRAMES(zone_conf_size(count));
[a35b458]544
[b0c2075]545 if ((pfn < zones.info[znum].base) ||
546 (pfn >= zones.info[znum].base + zones.info[znum].count))
[bb68433]547 return;
[a35b458]548
[b0c2075]549 for (size_t i = 0; i < cframes; i++)
[81d3612]550 zone_mark_available(&zones.info[znum],
[5f0f29ce]551 pfn - zones.info[znum].base + i);
[874878a]552}
553
[deaf8d5]554/** Merge zones z1 and z2.
[bb68433]555 *
[5f0f29ce]556 * The merged zones must be 2 zones with no zone existing in between
557 * (which means that z2 = z1 + 1). Both zones must be available zones
558 * with the same flags.
559 *
560 * When you create a new zone, the frame allocator configuration does
561 * not to be 2^order size. Once the allocator is running it is no longer
562 * possible, merged configuration data occupies more space :-/
563 *
[bb68433]564 */
[98000fb]565bool zone_merge(size_t z1, size_t z2)
[bb68433]566{
[da1bafb]567 irq_spinlock_lock(&zones.lock, true);
[a35b458]568
[5f0f29ce]569 bool ret = true;
[a35b458]570
[b0c2075]571 /*
572 * We can join only 2 zones with none existing inbetween,
[5f0f29ce]573 * the zones have to be available and with the same
574 * set of flags
575 */
[e6c4b94]576 if ((z1 >= zones.count) || (z2 >= zones.count) || (z2 - z1 != 1) ||
577 (zones.info[z1].flags != zones.info[z2].flags)) {
[5f0f29ce]578 ret = false;
[bb68433]579 goto errout;
[5f0f29ce]580 }
[a35b458]581
[5f0f29ce]582 pfn_t cframes = SIZE2FRAMES(zone_conf_size(
[3bacee1]583 zones.info[z2].base - zones.info[z1].base +
584 zones.info[z2].count));
[a35b458]585
[5f0f29ce]586 /* Allocate merged zone data inside one of the zones */
587 pfn_t pfn;
[b0c2075]588 if (zone_can_alloc(&zones.info[z1], cframes, 0)) {
589 pfn = zones.info[z1].base +
590 zone_frame_alloc(&zones.info[z1], cframes, 0);
591 } else if (zone_can_alloc(&zones.info[z2], cframes, 0)) {
592 pfn = zones.info[z2].base +
593 zone_frame_alloc(&zones.info[z2], cframes, 0);
[5f0f29ce]594 } else {
595 ret = false;
596 goto errout;
597 }
[a35b458]598
[5f0f29ce]599 /* Preserve original data from z1 */
600 zone_t old_z1 = zones.info[z1];
[a35b458]601
[5f0f29ce]602 /* Do zone merging */
[b0c2075]603 zone_merge_internal(z1, z2, &old_z1, (void *) PA2KA(PFN2ADDR(pfn)));
[a35b458]604
[bb68433]605 /* Subtract zone information from busy frames */
[5f0f29ce]606 zones.info[z1].busy_count -= cframes;
[a35b458]607
[5f0f29ce]608 /* Free old zone information */
609 return_config_frames(z1,
610 ADDR2PFN(KA2PA((uintptr_t) old_z1.frames)), old_z1.count);
611 return_config_frames(z1,
612 ADDR2PFN(KA2PA((uintptr_t) zones.info[z2].frames)),
613 zones.info[z2].count);
[a35b458]614
[e49e234]615 /* Move zones down */
[b0c2075]616 for (size_t i = z2 + 1; i < zones.count; i++)
[b6b576c]617 zones.info[i - 1] = zones.info[i];
[a35b458]618
[bb68433]619 zones.count--;
[a35b458]620
[bb68433]621errout:
[da1bafb]622 irq_spinlock_unlock(&zones.lock, true);
[a35b458]623
[5f0f29ce]624 return ret;
[bb68433]625}
626
[5f0f29ce]627/** Merge all mergeable zones into one big zone.
628 *
629 * It is reasonable to do this on systems where
630 * BIOS reports parts in chunks, so that we could
631 * have 1 zone (it's faster).
[bb68433]632 *
633 */
634void zone_merge_all(void)
635{
[b0c2075]636 size_t i = 1;
[a35b458]637
[5f0f29ce]638 while (i < zones.count) {
[b0c2075]639 if (!zone_merge(i - 1, i))
[5f0f29ce]640 i++;
[bb68433]641 }
642}
643
[deaf8d5]644/** Create new frame zone.
[085d973]645 *
[b0c2075]646 * @param zone Zone to construct.
647 * @param start Physical address of the first frame within the zone.
648 * @param count Count of frames in zone.
649 * @param flags Zone flags.
650 * @param confdata Configuration data of the zone.
[5f0f29ce]651 *
652 * @return Initialized zone.
[085d973]653 *
654 */
[8df5f20]655_NO_TRACE static void zone_construct(zone_t *zone, pfn_t start, size_t count,
[b0c2075]656 zone_flags_t flags, void *confdata)
[085d973]657{
[5f0f29ce]658 zone->base = start;
659 zone->count = count;
660 zone->flags = flags;
661 zone->free_count = count;
662 zone->busy_count = 0;
[a35b458]663
[e6c4b94]664 if (flags & ZONE_AVAILABLE) {
[5f0f29ce]665 /*
[b0c2075]666 * Initialize frame bitmap (located after the array of
667 * frame_t structures in the configuration space).
[5f0f29ce]668 */
[a35b458]669
[c5396c1]670 bitmap_initialize(&zone->bitmap, count, confdata +
671 (sizeof(frame_t) * count));
[adb252c0]672 bitmap_clear_range(&zone->bitmap, 0, count);
[a35b458]673
[b0c2075]674 /*
675 * Initialize the array of frame_t structures.
676 */
[a35b458]677
[b0c2075]678 zone->frames = (frame_t *) confdata;
[a35b458]679
[b0c2075]680 for (size_t i = 0; i < count; i++)
681 frame_initialize(&zone->frames[i]);
682 } else {
[c5396c1]683 bitmap_initialize(&zone->bitmap, 0, NULL);
[5f0f29ce]684 zone->frames = NULL;
[b0c2075]685 }
[085d973]686}
687
[deaf8d5]688/** Compute configuration data size for zone.
[eb3d379]689 *
[5f0f29ce]690 * @param count Size of zone in frames.
691 *
692 * @return Size of zone configuration info (in bytes).
693 *
[eb3d379]694 */
[bbfdf62]695size_t zone_conf_size(size_t count)
[085d973]696{
[c5396c1]697 return (count * sizeof(frame_t) + bitmap_size(count));
[085d973]698}
699
[8bdcffa]700/** Allocate external configuration frames from low memory. */
701pfn_t zone_external_conf_alloc(size_t count)
702{
[b0c2075]703 size_t frames = SIZE2FRAMES(zone_conf_size(count));
[a35b458]704
[b0c2075]705 return ADDR2PFN((uintptr_t)
706 frame_alloc(frames, FRAME_LOWMEM | FRAME_ATOMIC, 0));
[8bdcffa]707}
708
[deaf8d5]709/** Create and add zone to system.
[085d973]710 *
[5f0f29ce]711 * @param start First frame number (absolute).
712 * @param count Size of zone in frames.
713 * @param confframe Where configuration frames are supposed to be.
[b0c2075]714 * Automatically checks that we will not disturb the
[5f0f29ce]715 * kernel and possibly init. If confframe is given
716 * _outside_ this zone, it is expected, that the area is
717 * already marked BUSY and big enough to contain
718 * zone_conf_size() amount of data. If the confframe is
719 * inside the area, the zone free frame information is
720 * modified not to include it.
721 *
722 * @return Zone number or -1 on error.
723 *
[085d973]724 */
[da1bafb]725size_t zone_create(pfn_t start, size_t count, pfn_t confframe,
726 zone_flags_t flags)
[085d973]727{
[da1bafb]728 irq_spinlock_lock(&zones.lock, true);
[a35b458]729
[e6c4b94]730 if (flags & ZONE_AVAILABLE) { /* Create available zone */
[b0c2075]731 /*
732 * Theoretically we could have NULL here, practically make sure
[5f0f29ce]733 * nobody tries to do that. If some platform requires, remove
734 * the assert
735 */
[3bacee1]736 assert(confframe != ADDR2PFN((uintptr_t) NULL));
[a35b458]737
[40c8c17]738 /* Update the known end of physical memory. */
739 config.physmem_end = max(config.physmem_end, PFN2ADDR(start + count));
[a35b458]740
[b0c2075]741 /*
742 * If confframe is supposed to be inside our zone, then make sure
[5f0f29ce]743 * it does not span kernel & init
744 */
[98000fb]745 size_t confcount = SIZE2FRAMES(zone_conf_size(count));
[a35b458]746
[5f0f29ce]747 if ((confframe >= start) && (confframe < start + count)) {
748 for (; confframe < start + count; confframe++) {
749 uintptr_t addr = PFN2ADDR(confframe);
750 if (overlaps(addr, PFN2ADDR(confcount),
751 KA2PA(config.base), config.kernel_size))
752 continue;
[a35b458]753
[5f0f29ce]754 bool overlap = false;
[b0c2075]755 for (size_t i = 0; i < init.cnt; i++) {
[5f0f29ce]756 if (overlaps(addr, PFN2ADDR(confcount),
[32817cc]757 init.tasks[i].paddr,
[5f0f29ce]758 init.tasks[i].size)) {
759 overlap = true;
760 break;
761 }
[b0c2075]762 }
[a35b458]763
[5f0f29ce]764 if (overlap)
765 continue;
[a35b458]766
[5f0f29ce]767 break;
768 }
[a35b458]769
[b0c2075]770 if (confframe >= start + count)
771 panic("Cannot find configuration data for zone.");
[085d973]772 }
[a35b458]773
[e2650d3]774 size_t znum = zones_insert_zone(start, count, flags);
[98000fb]775 if (znum == (size_t) -1) {
[da1bafb]776 irq_spinlock_unlock(&zones.lock, true);
[98000fb]777 return (size_t) -1;
[5f0f29ce]778 }
[a35b458]779
[b0c2075]780 void *confdata = (void *) PA2KA(PFN2ADDR(confframe));
781 zone_construct(&zones.info[znum], start, count, flags, confdata);
[a35b458]782
[5f0f29ce]783 /* If confdata in zone, mark as unavailable */
784 if ((confframe >= start) && (confframe < start + count)) {
[b0c2075]785 for (size_t i = confframe; i < confframe + confcount; i++)
[5f0f29ce]786 zone_mark_unavailable(&zones.info[znum],
787 i - zones.info[znum].base);
[085d973]788 }
[a35b458]789
[da1bafb]790 irq_spinlock_unlock(&zones.lock, true);
[a35b458]791
[5f0f29ce]792 return znum;
793 }
[a35b458]794
[5f0f29ce]795 /* Non-available zone */
[e2650d3]796 size_t znum = zones_insert_zone(start, count, flags);
[98000fb]797 if (znum == (size_t) -1) {
[da1bafb]798 irq_spinlock_unlock(&zones.lock, true);
[98000fb]799 return (size_t) -1;
[5f0f29ce]800 }
[a35b458]801
[b0c2075]802 zone_construct(&zones.info[znum], start, count, flags, NULL);
[a35b458]803
[da1bafb]804 irq_spinlock_unlock(&zones.lock, true);
[a35b458]805
[bb68433]806 return znum;
[085d973]807}
808
[d1582b50]809/*
810 * Frame functions
811 */
[085d973]812
[deaf8d5]813/** Set parent of frame. */
[98000fb]814void frame_set_parent(pfn_t pfn, void *data, size_t hint)
[085d973]815{
[da1bafb]816 irq_spinlock_lock(&zones.lock, true);
[a35b458]817
[98000fb]818 size_t znum = find_zone(pfn, 1, hint);
[a35b458]819
[63e27ef]820 assert(znum != (size_t) -1);
[a35b458]821
[5f0f29ce]822 zone_get_frame(&zones.info[znum],
823 pfn - zones.info[znum].base)->parent = data;
[a35b458]824
[da1bafb]825 irq_spinlock_unlock(&zones.lock, true);
[085d973]826}
827
[98000fb]828void *frame_get_parent(pfn_t pfn, size_t hint)
[085d973]829{
[da1bafb]830 irq_spinlock_lock(&zones.lock, true);
[a35b458]831
[98000fb]832 size_t znum = find_zone(pfn, 1, hint);
[a35b458]833
[63e27ef]834 assert(znum != (size_t) -1);
[a35b458]835
[5f0f29ce]836 void *res = zone_get_frame(&zones.info[znum],
837 pfn - zones.info[znum].base)->parent;
[a35b458]838
[da1bafb]839 irq_spinlock_unlock(&zones.lock, true);
[a35b458]840
[085d973]841 return res;
842}
843
[482f968]844static size_t try_find_zone(size_t count, bool lowmem,
845 pfn_t frame_constraint, size_t hint)
846{
847 if (!lowmem) {
848 size_t znum = find_free_zone(count,
849 ZONE_HIGHMEM | ZONE_AVAILABLE, frame_constraint, hint);
850 if (znum != (size_t) -1)
851 return znum;
852 }
853
854 return find_free_zone(count, ZONE_LOWMEM | ZONE_AVAILABLE,
855 frame_constraint, hint);
856}
857
[b0c2075]858/** Allocate frames of physical memory.
[085d973]859 *
[b0c2075]860 * @param count Number of continuous frames to allocate.
861 * @param flags Flags for host zone selection and address processing.
862 * @param constraint Indication of physical address bits that cannot be
863 * set in the address of the first allocated frame.
864 * @param pzone Preferred zone.
[085d973]865 *
[5f0f29ce]866 * @return Physical address of the allocated frame.
[9a68b34d]867 *
[085d973]868 */
[b0c2075]869uintptr_t frame_alloc_generic(size_t count, frame_flags_t flags,
[8cbf1c3]870 uintptr_t constraint, size_t *pzone)
[085d973]871{
[63e27ef]872 assert(count > 0);
[a35b458]873
[98000fb]874 size_t hint = pzone ? (*pzone) : 0;
[b0c2075]875 pfn_t frame_constraint = ADDR2PFN(constraint);
[a35b458]876
[89bcb520]877 /*
878 * If not told otherwise, we must first reserve the memory.
879 */
[b0c2075]880 if (!(flags & FRAME_NO_RESERVE))
881 reserve_force_alloc(count);
[a35b458]882
[085d973]883loop:
[da1bafb]884 irq_spinlock_lock(&zones.lock, true);
[a35b458]885
[482f968]886 // TODO: Print diagnostic if neither is explicitly specified.
887 bool lowmem = (flags & FRAME_LOWMEM) || !(flags & FRAME_HIGHMEM);
888
[085d973]889 /*
890 * First, find suitable frame zone.
891 */
[482f968]892 size_t znum = try_find_zone(count, lowmem, frame_constraint, hint);
[a35b458]893
[b0c2075]894 /*
895 * If no memory, reclaim some slab memory,
896 * if it does not help, reclaim all.
897 */
[98000fb]898 if ((znum == (size_t) -1) && (!(flags & FRAME_NO_RECLAIM))) {
[da1bafb]899 irq_spinlock_unlock(&zones.lock, true);
[98000fb]900 size_t freed = slab_reclaim(0);
[da1bafb]901 irq_spinlock_lock(&zones.lock, true);
[a35b458]902
[5f0f29ce]903 if (freed > 0)
[482f968]904 znum = try_find_zone(count, lowmem,
[b0c2075]905 frame_constraint, hint);
[a35b458]906
[98000fb]907 if (znum == (size_t) -1) {
[da1bafb]908 irq_spinlock_unlock(&zones.lock, true);
[085d973]909 freed = slab_reclaim(SLAB_RECLAIM_ALL);
[da1bafb]910 irq_spinlock_lock(&zones.lock, true);
[a35b458]911
[5f0f29ce]912 if (freed > 0)
[482f968]913 znum = try_find_zone(count, lowmem,
[b0c2075]914 frame_constraint, hint);
[085d973]915 }
916 }
[a35b458]917
[98000fb]918 if (znum == (size_t) -1) {
[1a1744e]919 if (flags & FRAME_ATOMIC) {
[da1bafb]920 irq_spinlock_unlock(&zones.lock, true);
[a35b458]921
[89bcb520]922 if (!(flags & FRAME_NO_RESERVE))
[b0c2075]923 reserve_free(count);
[a35b458]924
[8cbf1c3]925 return 0;
[1a1744e]926 }
[a35b458]927
[8d308b9]928 size_t avail = frame_total_free_get_internal();
[a35b458]929
[da1bafb]930 irq_spinlock_unlock(&zones.lock, true);
[a35b458]931
[05411e8]932 if (!THREAD)
[adb252c0]933 panic("Cannot wait for %zu frames to become available "
934 "(%zu available).", count, avail);
[a35b458]935
[5f0f29ce]936 /*
937 * Sleep until some frames are available again.
938 */
[a35b458]939
[5f0f29ce]940#ifdef CONFIG_DEBUG
[b2fa1204]941 log(LF_OTHER, LVL_DEBUG,
942 "Thread %" PRIu64 " waiting for %zu frames "
943 "%zu available.", THREAD->tid, count, avail);
[5f0f29ce]944#endif
[a35b458]945
[905721b]946 /*
[b0c2075]947 * Since the mem_avail_mtx is an active mutex, we need to
948 * disable interrupts to prevent deadlock with TLB shootdown.
[905721b]949 */
950 ipl_t ipl = interrupts_disable();
[1bb3766]951 mutex_lock(&mem_avail_mtx);
[a35b458]952
[5f0f29ce]953 if (mem_avail_req > 0)
[b0c2075]954 mem_avail_req = min(mem_avail_req, count);
[5f0f29ce]955 else
[b0c2075]956 mem_avail_req = count;
[a35b458]957
[98000fb]958 size_t gen = mem_avail_gen;
[a35b458]959
[5f0f29ce]960 while (gen == mem_avail_gen)
[1bb3766]961 condvar_wait(&mem_avail_cv, &mem_avail_mtx);
[a35b458]962
[1bb3766]963 mutex_unlock(&mem_avail_mtx);
[905721b]964 interrupts_restore(ipl);
[a35b458]965
[1a1744e]966#ifdef CONFIG_DEBUG
[b2fa1204]967 log(LF_OTHER, LVL_DEBUG, "Thread %" PRIu64 " woken up.",
968 THREAD->tid);
[1a1744e]969#endif
[a35b458]970
[085d973]971 goto loop;
972 }
[a35b458]973
[b0c2075]974 pfn_t pfn = zone_frame_alloc(&zones.info[znum], count,
975 frame_constraint) + zones.info[znum].base;
[a35b458]976
[da1bafb]977 irq_spinlock_unlock(&zones.lock, true);
[a35b458]978
[5f0f29ce]979 if (pzone)
980 *pzone = znum;
[a35b458]981
[8cbf1c3]982 return PFN2ADDR(pfn);
[085d973]983}
984
[b0c2075]985uintptr_t frame_alloc(size_t count, frame_flags_t flags, uintptr_t constraint)
[0139747]986{
[b0c2075]987 return frame_alloc_generic(count, flags, constraint, NULL);
[0139747]988}
989
[5df1963]990/** Free frames of physical memory.
[085d973]991 *
[5df1963]992 * Find respective frame structures for supplied physical frames.
993 * Decrement each frame reference count. If it drops to zero, mark
994 * the frames as available.
[5f0f29ce]995 *
[5df1963]996 * @param start Physical Address of the first frame to be freed.
997 * @param count Number of frames to free.
[c32e6bc]998 * @param flags Flags to control memory reservation.
[085d973]999 *
1000 */
[5df1963]1001void frame_free_generic(uintptr_t start, size_t count, frame_flags_t flags)
[085d973]1002{
[5df1963]1003 size_t freed = 0;
[a35b458]1004
[5df1963]1005 irq_spinlock_lock(&zones.lock, true);
[a35b458]1006
[5df1963]1007 for (size_t i = 0; i < count; i++) {
1008 /*
1009 * First, find host frame zone for addr.
1010 */
1011 pfn_t pfn = ADDR2PFN(start) + i;
1012 size_t znum = find_zone(pfn, 1, 0);
[a35b458]1013
[63e27ef]1014 assert(znum != (size_t) -1);
[a35b458]1015
[5df1963]1016 freed += zone_frame_free(&zones.info[znum],
1017 pfn - zones.info[znum].base);
1018 }
[a35b458]1019
[da1bafb]1020 irq_spinlock_unlock(&zones.lock, true);
[a35b458]1021
[1a1744e]1022 /*
1023 * Signal that some memory has been freed.
[5df1963]1024 * Since the mem_avail_mtx is an active mutex,
1025 * we need to disable interruptsto prevent deadlock
1026 * with TLB shootdown.
[905721b]1027 */
[a35b458]1028
[905721b]1029 ipl_t ipl = interrupts_disable();
[1bb3766]1030 mutex_lock(&mem_avail_mtx);
[a35b458]1031
[5f0f29ce]1032 if (mem_avail_req > 0)
[5df1963]1033 mem_avail_req -= min(mem_avail_req, freed);
[a35b458]1034
[5f0f29ce]1035 if (mem_avail_req == 0) {
1036 mem_avail_gen++;
1037 condvar_broadcast(&mem_avail_cv);
1038 }
[a35b458]1039
[1bb3766]1040 mutex_unlock(&mem_avail_mtx);
[905721b]1041 interrupts_restore(ipl);
[a35b458]1042
[89bcb520]1043 if (!(flags & FRAME_NO_RESERVE))
[5df1963]1044 reserve_free(freed);
[085d973]1045}
1046
[5df1963]1047void frame_free(uintptr_t frame, size_t count)
[c32e6bc]1048{
[5df1963]1049 frame_free_generic(frame, count, 0);
[c32e6bc]1050}
1051
[5df1963]1052void frame_free_noreserve(uintptr_t frame, size_t count)
[e608cbe]1053{
[5df1963]1054 frame_free_generic(frame, count, FRAME_NO_RESERVE);
[e608cbe]1055}
1056
[f3ac636]1057/** Add reference to frame.
1058 *
1059 * Find respective frame structure for supplied PFN and
1060 * increment frame reference count.
1061 *
[5f0f29ce]1062 * @param pfn Frame number of the frame to be freed.
1063 *
[f3ac636]1064 */
[8df5f20]1065_NO_TRACE void frame_reference_add(pfn_t pfn)
[f3ac636]1066{
[da1bafb]1067 irq_spinlock_lock(&zones.lock, true);
[a35b458]1068
[f3ac636]1069 /*
1070 * First, find host frame zone for addr.
1071 */
[0b4a67a]1072 size_t znum = find_zone(pfn, 1, 0);
[a35b458]1073
[63e27ef]1074 assert(znum != (size_t) -1);
[a35b458]1075
[5f0f29ce]1076 zones.info[znum].frames[pfn - zones.info[znum].base].refcount++;
[a35b458]1077
[da1bafb]1078 irq_spinlock_unlock(&zones.lock, true);
[f3ac636]1079}
[085d973]1080
[da1bafb]1081/** Mark given range unavailable in frame zones.
1082 *
1083 */
[8df5f20]1084_NO_TRACE void frame_mark_unavailable(pfn_t start, size_t count)
[085d973]1085{
[da1bafb]1086 irq_spinlock_lock(&zones.lock, true);
[a35b458]1087
[b0c2075]1088 for (size_t i = 0; i < count; i++) {
[98000fb]1089 size_t znum = find_zone(start + i, 1, 0);
[a35b458]1090
[98000fb]1091 if (znum == (size_t) -1) /* PFN not found */
[085d973]1092 continue;
[a35b458]1093
[5f0f29ce]1094 zone_mark_unavailable(&zones.info[znum],
1095 start + i - zones.info[znum].base);
[085d973]1096 }
[a35b458]1097
[da1bafb]1098 irq_spinlock_unlock(&zones.lock, true);
[085d973]1099}
1100
[da1bafb]1101/** Initialize physical memory management.
1102 *
1103 */
[085d973]1104void frame_init(void)
1105{
1106 if (config.cpu_active == 1) {
1107 zones.count = 0;
[da1bafb]1108 irq_spinlock_initialize(&zones.lock, "frame.zones.lock");
[1bb3766]1109 mutex_initialize(&mem_avail_mtx, MUTEX_ACTIVE);
1110 condvar_initialize(&mem_avail_cv);
[085d973]1111 }
[a35b458]1112
[085d973]1113 /* Tell the architecture to create some memory */
[ddcc8a0]1114 frame_low_arch_init();
[a35b458]1115
[085d973]1116 if (config.cpu_active == 1) {
[4638401]1117 frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
1118 SIZE2FRAMES(config.kernel_size));
[a35b458]1119
[b0c2075]1120 for (size_t i = 0; i < init.cnt; i++)
1121 frame_mark_unavailable(ADDR2PFN(init.tasks[i].paddr),
[4638401]1122 SIZE2FRAMES(init.tasks[i].size));
[a35b458]1123
[61e90dd]1124 if (ballocs.size)
[4638401]1125 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
1126 SIZE2FRAMES(ballocs.size));
[a35b458]1127
[b0c2075]1128 /*
1129 * Blacklist first frame, as allocating NULL would
[5f0f29ce]1130 * fail in some places
1131 */
[bffa0b06]1132 frame_mark_unavailable(0, 1);
[085d973]1133 }
[a35b458]1134
[ddcc8a0]1135 frame_high_arch_init();
[085d973]1136}
1137
[ad12b5ea]1138/** Adjust bounds of physical memory region according to low/high memory split.
1139 *
[b0c2075]1140 * @param low[in] If true, the adjustment is performed to make the region
1141 * fit in the low memory. Otherwise the adjustment is
1142 * performed to make the region fit in the high memory.
1143 * @param basep[inout] Pointer to a variable which contains the region's base
1144 * address and which may receive the adjusted base address.
1145 * @param sizep[inout] Pointer to a variable which contains the region's size
1146 * and which may receive the adjusted size.
1147 *
1148 * @return True if the region still exists even after the adjustment.
1149 * @return False otherwise.
1150 *
[ad12b5ea]1151 */
1152bool frame_adjust_zone_bounds(bool low, uintptr_t *basep, size_t *sizep)
1153{
[2cc7f16]1154 uintptr_t limit = KA2PA(config.identity_base) + config.identity_size;
[a35b458]1155
[ad12b5ea]1156 if (low) {
1157 if (*basep > limit)
1158 return false;
[a35b458]1159
[ad12b5ea]1160 if (*basep + *sizep > limit)
1161 *sizep = limit - *basep;
1162 } else {
1163 if (*basep + *sizep <= limit)
1164 return false;
[a35b458]1165
[ad12b5ea]1166 if (*basep <= limit) {
1167 *sizep -= limit - *basep;
1168 *basep = limit;
1169 }
1170 }
[a35b458]1171
[ad12b5ea]1172 return true;
1173}
1174
[da1bafb]1175/** Return total size of all zones.
1176 *
1177 */
[9dae191e]1178uint64_t zones_total_size(void)
[deaf8d5]1179{
[da1bafb]1180 irq_spinlock_lock(&zones.lock, true);
[a35b458]1181
[5f0f29ce]1182 uint64_t total = 0;
[a35b458]1183
[b0c2075]1184 for (size_t i = 0; i < zones.count; i++)
[5f0f29ce]1185 total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
[a35b458]1186
[da1bafb]1187 irq_spinlock_unlock(&zones.lock, true);
[a35b458]1188
[71eef11]1189 return total;
1190}
1191
[9dae191e]1192void zones_stats(uint64_t *total, uint64_t *unavail, uint64_t *busy,
1193 uint64_t *free)
[516adce]1194{
[63e27ef]1195 assert(total != NULL);
1196 assert(unavail != NULL);
1197 assert(busy != NULL);
1198 assert(free != NULL);
[a35b458]1199
[da1bafb]1200 irq_spinlock_lock(&zones.lock, true);
[a35b458]1201
[9dae191e]1202 *total = 0;
1203 *unavail = 0;
1204 *busy = 0;
1205 *free = 0;
[a35b458]1206
[b0c2075]1207 for (size_t i = 0; i < zones.count; i++) {
[9dae191e]1208 *total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
[a35b458]1209
[e6c4b94]1210 if (zones.info[i].flags & ZONE_AVAILABLE) {
[9dae191e]1211 *busy += (uint64_t) FRAMES2SIZE(zones.info[i].busy_count);
1212 *free += (uint64_t) FRAMES2SIZE(zones.info[i].free_count);
1213 } else
1214 *unavail += (uint64_t) FRAMES2SIZE(zones.info[i].count);
[516adce]1215 }
[a35b458]1216
[da1bafb]1217 irq_spinlock_unlock(&zones.lock, true);
[516adce]1218}
1219
[da1bafb]1220/** Prints list of zones.
1221 *
1222 */
[9dae191e]1223void zones_print_list(void)
[deaf8d5]1224{
[5f0f29ce]1225#ifdef __32_BITS__
[74c5a1ca]1226 printf("[nr] [base addr] [frames ] [flags ] [free frames ] [busy frames ]\n");
[2b8b0ca]1227#endif
1228
1229#ifdef __64_BITS__
[74c5a1ca]1230 printf("[nr] [base address ] [frames ] [flags ] [free frames ] [busy frames ]\n");
[2b8b0ca]1231#endif
[a35b458]1232
[000350f8]1233 /*
1234 * Because printing may require allocation of memory, we may not hold
[f72906c]1235 * the frame allocator locks when printing zone statistics. Therefore,
[000350f8]1236 * we simply gather the statistics under the protection of the locks and
1237 * print the statistics when the locks have been released.
1238 *
1239 * When someone adds/removes zones while we are printing the statistics,
1240 * we may end up with inaccurate output (e.g. a zone being skipped from
1241 * the listing).
1242 */
[a35b458]1243
[f72906c]1244 size_t free_lowmem = 0;
1245 size_t free_highmem = 0;
1246 size_t free_highprio = 0;
[a35b458]1247
[3bacee1]1248 for (size_t i = 0; ; i++) {
[da1bafb]1249 irq_spinlock_lock(&zones.lock, true);
[a35b458]1250
[000350f8]1251 if (i >= zones.count) {
[da1bafb]1252 irq_spinlock_unlock(&zones.lock, true);
[000350f8]1253 break;
1254 }
[a35b458]1255
[f72906c]1256 pfn_t fbase = zones.info[i].base;
1257 uintptr_t base = PFN2ADDR(fbase);
[98000fb]1258 size_t count = zones.info[i].count;
[5f0f29ce]1259 zone_flags_t flags = zones.info[i].flags;
[98000fb]1260 size_t free_count = zones.info[i].free_count;
1261 size_t busy_count = zones.info[i].busy_count;
[a35b458]1262
[e6c4b94]1263 bool available = ((flags & ZONE_AVAILABLE) != 0);
[f72906c]1264 bool lowmem = ((flags & ZONE_LOWMEM) != 0);
1265 bool highmem = ((flags & ZONE_HIGHMEM) != 0);
1266 bool highprio = is_high_priority(fbase, count);
[a35b458]1267
[f72906c]1268 if (available) {
1269 if (lowmem)
1270 free_lowmem += free_count;
[a35b458]1271
[f72906c]1272 if (highmem)
1273 free_highmem += free_count;
[a35b458]1274
[f72906c]1275 if (highprio) {
1276 free_highprio += free_count;
1277 } else {
1278 /*
1279 * Walk all frames of the zone and examine
1280 * all high priority memory to get accurate
1281 * statistics.
1282 */
[a35b458]1283
[f72906c]1284 for (size_t index = 0; index < count; index++) {
1285 if (is_high_priority(fbase + index, 0)) {
1286 if (!bitmap_get(&zones.info[i].bitmap, index))
1287 free_highprio++;
1288 } else
1289 break;
1290 }
1291 }
1292 }
[a35b458]1293
[f72906c]1294 irq_spinlock_unlock(&zones.lock, true);
[a35b458]1295
[7e752b2]1296 printf("%-4zu", i);
[a35b458]1297
[2b8b0ca]1298#ifdef __32_BITS__
[7e752b2]1299 printf(" %p", (void *) base);
[2b8b0ca]1300#endif
[a35b458]1301
[2b8b0ca]1302#ifdef __64_BITS__
[7e752b2]1303 printf(" %p", (void *) base);
[e49e234]1304#endif
[a35b458]1305
[e6c4b94]1306 printf(" %12zu %c%c%c%c%c ", count,
1307 available ? 'A' : '-',
1308 (flags & ZONE_RESERVED) ? 'R' : '-',
1309 (flags & ZONE_FIRMWARE) ? 'F' : '-',
1310 (flags & ZONE_LOWMEM) ? 'L' : '-',
1311 (flags & ZONE_HIGHMEM) ? 'H' : '-');
[a35b458]1312
[5f0f29ce]1313 if (available)
[7e752b2]1314 printf("%14zu %14zu",
[5f0f29ce]1315 free_count, busy_count);
[a35b458]1316
[5f0f29ce]1317 printf("\n");
[dfd9186]1318 }
[a35b458]1319
[f72906c]1320 printf("\n");
[a35b458]1321
[f72906c]1322 uint64_t size;
1323 const char *size_suffix;
[a35b458]1324
[f72906c]1325 bin_order_suffix(FRAMES2SIZE(free_lowmem), &size, &size_suffix,
1326 false);
1327 printf("Available low memory: %zu frames (%" PRIu64 " %s)\n",
1328 free_lowmem, size, size_suffix);
[a35b458]1329
[f72906c]1330 bin_order_suffix(FRAMES2SIZE(free_highmem), &size, &size_suffix,
1331 false);
1332 printf("Available high memory: %zu frames (%" PRIu64 " %s)\n",
1333 free_highmem, size, size_suffix);
[a35b458]1334
[f72906c]1335 bin_order_suffix(FRAMES2SIZE(free_highprio), &size, &size_suffix,
1336 false);
1337 printf("Available high priority: %zu frames (%" PRIu64 " %s)\n",
1338 free_highprio, size, size_suffix);
[dfd9186]1339}
1340
[abbc16e]1341/** Prints zone details.
[96cacc1]1342 *
[5f0f29ce]1343 * @param num Zone base address or zone number.
1344 *
[96cacc1]1345 */
[98000fb]1346void zone_print_one(size_t num)
[deaf8d5]1347{
[da1bafb]1348 irq_spinlock_lock(&zones.lock, true);
[98000fb]1349 size_t znum = (size_t) -1;
[a35b458]1350
[b0c2075]1351 for (size_t i = 0; i < zones.count; i++) {
[5f0f29ce]1352 if ((i == num) || (PFN2ADDR(zones.info[i].base) == num)) {
1353 znum = i;
[bb68433]1354 break;
1355 }
1356 }
[a35b458]1357
[98000fb]1358 if (znum == (size_t) -1) {
[da1bafb]1359 irq_spinlock_unlock(&zones.lock, true);
[bb68433]1360 printf("Zone not found.\n");
[2ec725f]1361 return;
[dfd9186]1362 }
[a35b458]1363
[f72906c]1364 size_t free_lowmem = 0;
1365 size_t free_highmem = 0;
1366 size_t free_highprio = 0;
[a35b458]1367
[f72906c]1368 pfn_t fbase = zones.info[znum].base;
1369 uintptr_t base = PFN2ADDR(fbase);
[b0c2075]1370 zone_flags_t flags = zones.info[znum].flags;
1371 size_t count = zones.info[znum].count;
1372 size_t free_count = zones.info[znum].free_count;
1373 size_t busy_count = zones.info[znum].busy_count;
[a35b458]1374
[e6c4b94]1375 bool available = ((flags & ZONE_AVAILABLE) != 0);
[f72906c]1376 bool lowmem = ((flags & ZONE_LOWMEM) != 0);
1377 bool highmem = ((flags & ZONE_HIGHMEM) != 0);
1378 bool highprio = is_high_priority(fbase, count);
[a35b458]1379
[f72906c]1380 if (available) {
1381 if (lowmem)
1382 free_lowmem = free_count;
[a35b458]1383
[f72906c]1384 if (highmem)
1385 free_highmem = free_count;
[a35b458]1386
[f72906c]1387 if (highprio) {
1388 free_highprio = free_count;
1389 } else {
1390 /*
1391 * Walk all frames of the zone and examine
1392 * all high priority memory to get accurate
1393 * statistics.
1394 */
[a35b458]1395
[f72906c]1396 for (size_t index = 0; index < count; index++) {
1397 if (is_high_priority(fbase + index, 0)) {
1398 if (!bitmap_get(&zones.info[znum].bitmap, index))
1399 free_highprio++;
1400 } else
1401 break;
1402 }
1403 }
1404 }
[a35b458]1405
[f72906c]1406 irq_spinlock_unlock(&zones.lock, true);
[a35b458]1407
[933cadf]1408 uint64_t size;
1409 const char *size_suffix;
[a35b458]1410
[933cadf]1411 bin_order_suffix(FRAMES2SIZE(count), &size, &size_suffix, false);
[a35b458]1412
[f72906c]1413 printf("Zone number: %zu\n", znum);
1414 printf("Zone base address: %p\n", (void *) base);
1415 printf("Zone size: %zu frames (%" PRIu64 " %s)\n", count,
[933cadf]1416 size, size_suffix);
[f72906c]1417 printf("Zone flags: %c%c%c%c%c\n",
[e6c4b94]1418 available ? 'A' : '-',
1419 (flags & ZONE_RESERVED) ? 'R' : '-',
1420 (flags & ZONE_FIRMWARE) ? 'F' : '-',
1421 (flags & ZONE_LOWMEM) ? 'L' : '-',
1422 (flags & ZONE_HIGHMEM) ? 'H' : '-');
[a35b458]1423
[5f0f29ce]1424 if (available) {
[933cadf]1425 bin_order_suffix(FRAMES2SIZE(busy_count), &size, &size_suffix,
1426 false);
[f72906c]1427 printf("Allocated space: %zu frames (%" PRIu64 " %s)\n",
[933cadf]1428 busy_count, size, size_suffix);
[a35b458]1429
[933cadf]1430 bin_order_suffix(FRAMES2SIZE(free_count), &size, &size_suffix,
1431 false);
[f72906c]1432 printf("Available space: %zu frames (%" PRIu64 " %s)\n",
[933cadf]1433 free_count, size, size_suffix);
[a35b458]1434
[f72906c]1435 bin_order_suffix(FRAMES2SIZE(free_lowmem), &size, &size_suffix,
1436 false);
1437 printf("Available low memory: %zu frames (%" PRIu64 " %s)\n",
1438 free_lowmem, size, size_suffix);
[a35b458]1439
[f72906c]1440 bin_order_suffix(FRAMES2SIZE(free_highmem), &size, &size_suffix,
1441 false);
1442 printf("Available high memory: %zu frames (%" PRIu64 " %s)\n",
1443 free_highmem, size, size_suffix);
[a35b458]1444
[f72906c]1445 bin_order_suffix(FRAMES2SIZE(free_highprio), &size, &size_suffix,
1446 false);
1447 printf("Available high priority: %zu frames (%" PRIu64 " %s)\n",
1448 free_highprio, size, size_suffix);
[5f0f29ce]1449 }
[dfd9186]1450}
1451
[cc73a8a1]1452/** @}
[b45c443]1453 */
Note: See TracBrowser for help on using the repository browser.