Coverage Report

Created: 2026-10-13 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/barebox/common/resource.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
 * resource.c - barebox resource management
4
 *
5
 * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
6
 */
7
#include <common.h>
8
#include <malloc.h>
9
#include <errno.h>
10
#include <init.h>
11
#include <linux/ioport.h>
12
#include <linux/err.h>
13
#include <linux/resource_ext.h>
14
#include <asm/io.h>
15
16
#ifdef CONFIG_DEBUG_RESOURCES
17
#define res_dbg(args...)  pr_info(args)
18
#else
19
0
#define res_dbg(args...)  pr_debug(args)
20
#endif
21
22
static int init_resource(struct resource *res, const char *name)
23
0
{
24
0
  INIT_LIST_HEAD(&res->children);
25
0
  res->parent = NULL;
26
0
  res->name = xstrdup_const(name);
27
28
0
  return 0;
29
0
}
30
31
/*
32
 * request a region.
33
 * This will succeed when the requested region is completely inside
34
 * the parent resource and does not conflict with any of the child
35
 * resources.
36
 */
37
struct resource *__request_region(struct resource *parent,
38
          resource_size_t start, resource_size_t end,
39
          const char *name, unsigned flags)
40
0
{
41
0
  struct resource *r, *new;
42
43
0
  if (end < start) {
44
0
    res_dbg("request region(0x%08llx:0x%08llx): end < start\n",
45
0
        (unsigned long long)start,
46
0
        (unsigned long long)end);
47
0
    return ERR_PTR(-EINVAL);
48
0
  }
49
50
  /* outside parent resource? */
51
0
  if (start < parent->start || end > parent->end) {
52
0
    res_dbg("request region(0x%08llx:0x%08llx): outside parent resource 0x%08llx:0x%08llx\n",
53
0
        (unsigned long long)start,
54
0
        (unsigned long long)end,
55
0
        (unsigned long long)parent->start,
56
0
        (unsigned long long)parent->end);
57
0
    return ERR_PTR(-EINVAL);
58
0
  }
59
60
  /*
61
   * We keep the list of child resources ordered which helps
62
   * us searching for conflicts here.
63
   */
64
0
  list_for_each_entry(r, &parent->children, sibling) {
65
0
    if (end < r->start)
66
0
      goto ok;
67
0
    if (start > r->end)
68
0
      continue;
69
0
    res_dbg("request region(0x%08llx:0x%08llx): %s conflicts with 0x%08llx:0x%08llx (%s)\n",
70
0
        (unsigned long long)start,
71
0
        (unsigned long long)end,
72
0
        name,
73
0
        (unsigned long long)r->start,
74
0
        (unsigned long long)r->end,
75
0
        r->name);
76
0
    return ERR_PTR(-EBUSY);
77
0
  }
78
79
0
ok:
80
0
  res_dbg("request region(0x%08llx:0x%08llx): success flags=0x%x\n",
81
0
      (unsigned long long)start,
82
0
      (unsigned long long)end, flags);
83
84
0
  new = xzalloc(sizeof(*new));
85
0
  init_resource(new, name);
86
0
  new->start = start;
87
0
  new->end = end;
88
0
  new->parent = parent;
89
0
  new->flags = flags;
90
0
  list_add_tail(&new->sibling, &r->sibling);
91
92
0
  return new;
93
0
}
94
95
/**
96
 * resize_region - change the size of an allocated resource region
97
 * @res: resource to resize (may be NULL for no-op)
98
 * @size: new desired size in bytes (must be non-zero)
99
 *
100
 * Shrinking checks that no child extends past the new end.
101
 * Growing checks that the new end does not overflow, stays within
102
 * the parent region and does not collide with the next sibling.
103
 *
104
 * Return: 0 on success, negative error code on failure
105
 */
106
int resize_region(struct resource *res, resource_size_t size)
107
0
{
108
0
  struct resource *parent;
109
0
  struct resource *next;
110
0
  resource_size_t newend;
111
112
0
  if (!res)
113
0
    return 0;
114
0
  if (!size)
115
0
    return -EINVAL;
116
117
0
  if (size == resource_size(res))
118
0
    return 0;
119
120
0
  if (size < resource_size(res)) {
121
0
    struct resource *last;
122
123
0
    last = list_last_entry_or_null(&res->children,
124
0
                 struct resource, sibling);
125
0
    if (last && last->end > res->start + size - 1)
126
0
      return -EBUSY;
127
0
    res->end = res->start + size - 1;
128
0
    return 0;
129
0
  }
130
131
0
  parent = res->parent;
132
0
  if (!parent)
133
0
    return -EINVAL;
134
135
0
  if (check_add_overflow(res->start, size - 1, &newend))
136
0
    return -EINVAL;
137
138
0
  if (newend > parent->end)
139
0
    return -EINVAL;
140
141
  /*
142
   * parent->children is the list_head that anchors the ordered list of
143
   * children. If res is not the last entry, the immediate next entry is
144
   * the only sibling we must check.
145
   */
146
0
  if (res->sibling.next != &parent->children) {
147
0
    next = list_next_entry(res, sibling);
148
149
0
    if (newend >= next->start)
150
0
      return -EBUSY;
151
0
  }
152
153
0
  res->end = newend;
154
0
  return 0;
155
0
}
156
157
/*
158
 * release a region previously requested with request_*_region
159
 */
160
int release_region(struct resource *res)
161
0
{
162
0
  if (!res)
163
0
    return 0;
164
0
  if (!list_empty(&res->children))
165
0
    return -EBUSY;
166
167
0
  list_del(&res->sibling);
168
0
  free_const(res->name);
169
0
  free(res);
170
171
0
  return 0;
172
0
}
173
174
static int yes_free(struct resource *res, void *data)
175
0
{
176
0
  return 1;
177
0
}
178
179
/*
180
 * release a region previously requested with request_*_region
181
 */
182
int release_region_range(struct resource *parent,
183
       resource_size_t start, resource_size_t size,
184
       int (*should_free)(struct resource *res, void *data),
185
       void *data)
186
0
{
187
0
  resource_size_t end = start + size - 1;
188
0
  struct resource *r, *tmp;
189
0
  int ret, err = 0;
190
191
0
  if (end < parent->start || start > parent->end)
192
0
    return 0;
193
194
0
  if (!should_free)
195
0
    should_free = yes_free;
196
197
0
  list_for_each_entry_safe(r, tmp, &parent->children, sibling) {
198
0
    if (end < r->start || start > r->end)
199
0
      continue;
200
201
    /*
202
     * CASE 1: fully covered
203
     *
204
     *   r:    |----------------|
205
     *   cut: |xxxxxxxxxxxxxxxxxxx|
206
     *
207
     *   remove fully
208
     */
209
0
    if (start <= r->start && r->end <= end) {
210
0
      ret = should_free(r, data);
211
0
      if (ret < 0)
212
0
        return ret;
213
0
      if (ret == 0)
214
0
        continue;
215
216
0
      ret = release_region(r);
217
0
      if (ret)
218
0
        err = ret;
219
0
      continue;
220
0
    }
221
222
    /*
223
     * CASE 2: trim head
224
     *
225
     *   r:    |----------------|
226
     *   cut: |xxxxx|
227
     *   new pieces:
228
     *       left  = removed
229
     *       right = end+1   .. r.end
230
     */
231
0
    if (start <= r->start && r->end > end) {
232
0
      ret = should_free(r, data);
233
0
      if (ret < 0)
234
0
        return ret;
235
0
      if (ret == 0)
236
0
        continue;
237
238
0
      if (list_empty(&r->children))
239
0
        r->start = end + 1;
240
0
      else
241
0
        err = -EBUSY;
242
0
      continue;
243
0
    }
244
245
    /*
246
     * CASE 3: trim tail
247
     *
248
     *   r:    |----------------|
249
     *   cut:                |xxxxx|
250
     *   new pieces:
251
     *       left  = r.start .. start-1
252
     *       right = removed
253
     */
254
0
    if (start > r->start && r->end <= end) {
255
0
      ret = should_free(r, data);
256
0
      if (ret < 0)
257
0
        return ret;
258
0
      if (ret == 0)
259
0
        continue;
260
261
0
      if (list_empty(&r->children))
262
0
        r->end = start - 1;
263
0
      else
264
0
        err = -EBUSY;
265
0
      continue;
266
0
    }
267
268
    /*
269
     * CASE 4: split
270
     *
271
     *   r:    |----------------|
272
     *   cut:       |xxxxx|
273
     *   new pieces:
274
     *       left  = r.start .. start-1
275
     *       right = end+1   .. r.end
276
     */
277
0
    if (start > r->start && r->end > end) {
278
0
      struct resource *right;
279
280
0
      ret = should_free(r, data);
281
0
      if (ret < 0)
282
0
        return ret;
283
0
      if (ret == 0)
284
0
        continue;
285
286
0
      if (!list_empty(&r->children)) {
287
0
        err = -EBUSY;
288
0
        continue;
289
0
      }
290
291
0
      right = xzalloc(sizeof(*right));
292
0
      init_resource(right, r->name);
293
294
0
      right->start = end + 1;
295
0
      right->end = r->end;
296
0
      right->parent = parent;
297
0
      right->flags = r->flags;
298
299
0
      r->end = start - 1;
300
301
0
      list_add(&right->sibling, &r->sibling);
302
0
      continue;
303
0
    }
304
0
  }
305
306
0
  return WARN_ON(err);
307
0
}
308
309
/*
310
 * merge two adjacent sibling regions.
311
 */
312
int __merge_regions(const char *name,
313
    struct resource *resa, struct resource *resb)
314
0
{
315
0
  if (!resource_adjacent(resa, resb))
316
0
    return -EINVAL;
317
318
0
  if (resa->start < resb->start)
319
0
    resa->end = resb->end;
320
0
  else
321
0
    resa->start = resb->start;
322
323
0
  free_const(resa->name);
324
0
  resa->name = xstrdup_const(name);
325
0
  release_region(resb);
326
327
0
  return 0;
328
0
}
329
330
/* The root resource for the whole memory-mapped io space */
331
struct resource iomem_resource = {
332
  .start = 0,
333
  .end = ~(resource_size_t)0,
334
  .name = "iomem",
335
  .children = LIST_HEAD_INIT(iomem_resource.children),
336
};
337
338
/*
339
 * request a region inside the io space (memory)
340
 */
341
struct resource *request_iomem_region(const char *name,
342
    resource_size_t start, resource_size_t end)
343
0
{
344
0
  return __request_region(&iomem_resource, start, end, name, 0);
345
0
}
346
347
/* The root resource for the whole io-mapped io space */
348
struct resource ioport_resource = {
349
  .start = 0,
350
  .end = IO_SPACE_LIMIT,
351
  .name = "ioport",
352
  .children = LIST_HEAD_INIT(ioport_resource.children),
353
};
354
355
/*
356
 * request a region inside the io space (i/o port)
357
 */
358
struct resource *request_ioport_region(const char *name,
359
    resource_size_t start, resource_size_t end)
360
0
{
361
0
  struct resource *res;
362
363
0
  res = __request_region(&ioport_resource, start, end, name, 0);
364
0
  if (IS_ERR(res))
365
0
    return ERR_CAST(res);
366
367
0
  return res;
368
0
}
369
370
static struct resource *
371
resource_iter_gap(struct resource *parent, struct resource *before,
372
      struct resource *gap, struct resource *after)
373
0
{
374
0
  *gap = (struct resource) {};
375
376
0
  gap->attrs = parent->attrs;
377
0
  gap->type = parent->type;
378
0
  gap->flags = IORESOURCE_UNSET | parent->flags;
379
0
  gap->parent = parent;
380
0
  INIT_LIST_HEAD(&gap->children);
381
382
0
  if (before) {
383
0
    gap->start = before->end + 1;
384
0
    gap->sibling.prev = &before->sibling;
385
0
  } else {
386
0
    gap->start = parent->start;
387
0
    gap->sibling.prev = &parent->children;
388
0
  }
389
390
0
  if (after) {
391
0
    gap->end = after->start - 1;
392
0
    gap->sibling.next = &after->sibling;
393
0
  } else {
394
0
    gap->end = parent->end;
395
0
    gap->sibling.next = &parent->children;
396
0
  }
397
398
0
  return gap;
399
0
}
400
401
struct resource *
402
resource_iter_first(struct resource *parent, struct resource *gap)
403
0
{
404
0
  struct resource *child;
405
406
0
  if (!parent || region_is_gap(parent))
407
0
    return NULL;
408
409
0
  child = list_first_entry_or_null(&parent->children,
410
0
           struct resource, sibling);
411
0
  if (!gap || (child && parent->start == child->start))
412
0
    return child;
413
414
0
  return resource_iter_gap(parent, NULL, gap, child);
415
0
}
416
417
struct resource *
418
resource_iter_last(struct resource *parent, struct resource *gap)
419
0
{
420
0
  struct resource *child;
421
422
0
  if (!parent || region_is_gap(parent))
423
0
    return NULL;
424
425
0
  child = list_last_entry_or_null(&parent->children,
426
0
          struct resource, sibling);
427
0
  if (!gap || (child && parent->end == child->end))
428
0
    return child;
429
430
0
  return resource_iter_gap(parent, child, gap, NULL);
431
0
}
432
433
struct resource *
434
resource_iter_next(struct resource *current, struct resource *gap)
435
0
{
436
0
  struct resource *parent, *cursor, *next = NULL;
437
438
0
  if (!current || !current->parent)
439
0
    return NULL;
440
441
0
  parent = current->parent;
442
0
  if (current->end == parent->end)
443
0
    return NULL;
444
445
0
  cursor = current;
446
0
  list_for_each_entry_continue(cursor, &parent->children, sibling) {
447
0
    next = cursor;
448
0
    break;
449
0
  }
450
451
0
  if (!gap || (next && resource_adjacent(current, next)))
452
0
    return next;
453
454
0
  return resource_iter_gap(parent, current, gap, next);
455
0
}
456
457
struct resource *
458
resource_iter_prev(struct resource *current,
459
           struct resource *gap)
460
0
{
461
0
  struct resource *parent, *cursor, *prev = NULL;
462
463
0
  if (!current || !current->parent)
464
0
    return NULL;
465
466
0
  parent = current->parent;
467
0
  if (current->start == parent->start)
468
0
    return NULL;
469
470
0
  cursor = current;
471
0
  list_for_each_entry_continue_reverse(cursor, &parent->children, sibling) {
472
0
    prev = cursor;
473
0
    break;
474
0
  }
475
476
0
  if (!gap || (prev && resource_adjacent(prev, current)))
477
0
    return prev;
478
479
0
  return resource_iter_gap(parent, prev, gap, current);
480
0
}
481
482
/**
483
 * lookup_region - find the region containing a given address
484
 * @parent: parent resource to search within
485
 * @addr: address to look up
486
 * @gap: if non-NULL and addr is in a gap, the gap info is copied here
487
 *
488
 * Searches the children of @parent (and gaps between them) for the region
489
 * containing @addr.
490
 *
491
 * Return: pointer to the resource containing @addr, or @gap if @addr is in
492
 *         a gap and @gap is non-NULL, or NULL if @addr is not found or is
493
 *         in a gap and @gap is NULL
494
 */
495
struct resource *lookup_region(struct resource *parent,
496
             resource_size_t addr, struct resource *gap)
497
0
{
498
0
  for_each_resource_region(parent, region) {
499
0
    if (addr < region->start || region->end < addr)
500
0
      continue;
501
502
0
    if (!region_is_gap(region))
503
0
      return region;
504
0
    if (!gap)
505
0
      return NULL;
506
507
0
    *gap = *region;
508
0
    INIT_LIST_HEAD(&gap->sibling);
509
0
    return gap;
510
0
  }
511
512
0
  return NULL;
513
0
}
514
515
struct resource_entry *resource_list_create_entry(struct resource *res,
516
              size_t extra_size)
517
0
{
518
0
  struct resource_entry *entry;
519
520
0
  entry = kzalloc(sizeof(*entry) + extra_size, GFP_KERNEL);
521
0
  if (entry) {
522
0
    INIT_LIST_HEAD(&entry->node);
523
0
    entry->res = res ? res : &entry->__res;
524
0
  }
525
526
0
  return entry;
527
0
}
528
EXPORT_SYMBOL(resource_list_create_entry);
529
530
static const char memory_type_name[][13] = {
531
  "Reserved",
532
  "Loader Code",
533
  "Loader Data",
534
  "Boot Code",
535
  "Boot Data",
536
  "Runtime Code",
537
  "Runtime Data",
538
  "Conventional",
539
  "Unusable",
540
  "ACPI Reclaim",
541
  "ACPI Mem NVS",
542
  "MMIO",
543
  "MMIO Port",
544
  "PAL Code",
545
  "Persistent",
546
  "Unaccepted",
547
};
548
549
const char *resource_typeattr_format(char *buf, size_t size,
550
             const struct resource *res)
551
0
{
552
0
  char *pos;
553
0
  int type_len;
554
0
  u64 attr;
555
556
0
  if (!(res->flags & IORESOURCE_TYPE_VALID))
557
0
    return NULL;
558
559
0
  pos = buf;
560
0
  type_len = snprintf(pos, size, "[%-*s",
561
0
          (int)(sizeof(memory_type_name[0]) - 1),
562
0
          memory_type_name[res->type]);
563
0
  if (type_len >= size)
564
0
    return buf;
565
566
0
  pos += type_len;
567
0
  size -= type_len;
568
569
0
  attr = res->attrs;
570
0
  if (attr & ~(MEMATTR_UC | MEMATTR_WC | MEMATTR_WT |
571
0
         MEMATTR_WB | MEMATTR_UCE | MEMATTR_RO |
572
0
         MEMATTR_WP | MEMATTR_RP | MEMATTR_XP |
573
0
         MEMATTR_NV | MEMATTR_SP | MEMATTR_MORE_RELIABLE)
574
0
         )
575
0
    snprintf(pos, size, "|attr=0x%08llx]",
576
0
       (unsigned long long)attr);
577
0
  else
578
0
    snprintf(pos, size,
579
0
       "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]",
580
0
       res->runtime      ? "RUN" : "",
581
0
       attr & MEMATTR_MORE_RELIABLE  ? "MR"  : "",
582
0
       attr & MEMATTR_SP    ? "SP"  : "",
583
0
       attr & MEMATTR_NV    ? "NV"  : "",
584
0
       attr & MEMATTR_XP    ? "XP"  : "",
585
0
       attr & MEMATTR_RP    ? "RP"  : "",
586
0
       attr & MEMATTR_WP    ? "WP"  : "",
587
0
       attr & MEMATTR_RO    ? "RO"  : "",
588
0
       attr & MEMATTR_UCE    ? "UCE" : "",
589
0
       attr & MEMATTR_WB    ? "WB"  : "",
590
0
       attr & MEMATTR_WT    ? "WT"  : "",
591
0
       attr & MEMATTR_WC    ? "WC"  : "",
592
0
       attr & MEMATTR_UC    ? "UC"  : "");
593
0
  return buf;
594
0
}