This source file includes following definitions.
- __cvmx_bootmem_desc_get
- cvmx_bootmem_phy_set_size
- cvmx_bootmem_phy_set_next
- cvmx_bootmem_phy_get_size
- cvmx_bootmem_phy_get_next
- cvmx_bootmem_alloc_range
- cvmx_bootmem_alloc_address
- cvmx_bootmem_alloc_named_range
- cvmx_bootmem_alloc_named
- cvmx_bootmem_lock
- cvmx_bootmem_unlock
- cvmx_bootmem_init
- cvmx_bootmem_phy_alloc
- __cvmx_bootmem_phy_free
- cvmx_bootmem_phy_named_block_find
- cvmx_bootmem_alloc_named_range_once
- cvmx_bootmem_find_named_block
- cvmx_bootmem_phy_named_block_free
- cvmx_bootmem_free_named
- cvmx_bootmem_phy_named_block_alloc
- cvmx_bootmem_get_desc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 #include <linux/export.h>
34 #include <linux/kernel.h>
35
36 #include <asm/octeon/cvmx.h>
37 #include <asm/octeon/cvmx-spinlock.h>
38 #include <asm/octeon/cvmx-bootmem.h>
39
40
41
42
43 static struct cvmx_bootmem_desc *cvmx_bootmem_desc;
44
45
46
47
48
49
50
51
52 #define SIZEOF_FIELD(s, field) sizeof(((s *)NULL)->field)
53
54
55
56
57
58
59
60
61
62
63
64
65 #define CVMX_BOOTMEM_NAMED_GET_FIELD(addr, field) \
66 __cvmx_bootmem_desc_get(addr, \
67 offsetof(struct cvmx_bootmem_named_block_desc, field), \
68 SIZEOF_FIELD(struct cvmx_bootmem_named_block_desc, field))
69
70
71
72
73
74
75
76
77
78
79
80
81
82 static inline uint64_t __cvmx_bootmem_desc_get(uint64_t base, int offset,
83 int size)
84 {
85 base = (1ull << 63) | (base + offset);
86 switch (size) {
87 case 4:
88 return cvmx_read64_uint32(base);
89 case 8:
90 return cvmx_read64_uint64(base);
91 default:
92 return 0;
93 }
94 }
95
96
97
98
99
100
101
102 #define NEXT_OFFSET 0
103 #define SIZE_OFFSET 8
104
105 static void cvmx_bootmem_phy_set_size(uint64_t addr, uint64_t size)
106 {
107 cvmx_write64_uint64((addr + SIZE_OFFSET) | (1ull << 63), size);
108 }
109
110 static void cvmx_bootmem_phy_set_next(uint64_t addr, uint64_t next)
111 {
112 cvmx_write64_uint64((addr + NEXT_OFFSET) | (1ull << 63), next);
113 }
114
115 static uint64_t cvmx_bootmem_phy_get_size(uint64_t addr)
116 {
117 return cvmx_read64_uint64((addr + SIZE_OFFSET) | (1ull << 63));
118 }
119
120 static uint64_t cvmx_bootmem_phy_get_next(uint64_t addr)
121 {
122 return cvmx_read64_uint64((addr + NEXT_OFFSET) | (1ull << 63));
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 static void *cvmx_bootmem_alloc_range(uint64_t size, uint64_t alignment,
139 uint64_t min_addr, uint64_t max_addr)
140 {
141 int64_t address;
142 address =
143 cvmx_bootmem_phy_alloc(size, min_addr, max_addr, alignment, 0);
144
145 if (address > 0)
146 return cvmx_phys_to_ptr(address);
147 else
148 return NULL;
149 }
150
151 void *cvmx_bootmem_alloc_address(uint64_t size, uint64_t address,
152 uint64_t alignment)
153 {
154 return cvmx_bootmem_alloc_range(size, alignment, address,
155 address + size);
156 }
157
158 void *cvmx_bootmem_alloc_named_range(uint64_t size, uint64_t min_addr,
159 uint64_t max_addr, uint64_t align,
160 char *name)
161 {
162 int64_t addr;
163
164 addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr,
165 align, name, 0);
166 if (addr >= 0)
167 return cvmx_phys_to_ptr(addr);
168 else
169 return NULL;
170 }
171
172 void *cvmx_bootmem_alloc_named(uint64_t size, uint64_t alignment, char *name)
173 {
174 return cvmx_bootmem_alloc_named_range(size, 0, 0, alignment, name);
175 }
176 EXPORT_SYMBOL(cvmx_bootmem_alloc_named);
177
178 void cvmx_bootmem_lock(void)
179 {
180 cvmx_spinlock_lock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock));
181 }
182
183 void cvmx_bootmem_unlock(void)
184 {
185 cvmx_spinlock_unlock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock));
186 }
187
188 int cvmx_bootmem_init(void *mem_desc_ptr)
189 {
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204 if (!cvmx_bootmem_desc) {
205 #if defined(CVMX_ABI_64)
206
207 cvmx_bootmem_desc = cvmx_phys_to_ptr(CAST64(mem_desc_ptr));
208 #else
209 cvmx_bootmem_desc = (struct cvmx_bootmem_desc *) mem_desc_ptr;
210 #endif
211 }
212
213 return 0;
214 }
215
216
217
218
219
220
221
222
223
224 int64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min,
225 uint64_t address_max, uint64_t alignment,
226 uint32_t flags)
227 {
228
229 uint64_t head_addr;
230 uint64_t ent_addr;
231
232 uint64_t prev_addr = 0;
233 uint64_t new_ent_addr = 0;
234 uint64_t desired_min_addr;
235
236 #ifdef DEBUG
237 cvmx_dprintf("cvmx_bootmem_phy_alloc: req_size: 0x%llx, "
238 "min_addr: 0x%llx, max_addr: 0x%llx, align: 0x%llx\n",
239 (unsigned long long)req_size,
240 (unsigned long long)address_min,
241 (unsigned long long)address_max,
242 (unsigned long long)alignment);
243 #endif
244
245 if (cvmx_bootmem_desc->major_version > 3) {
246 cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
247 "version: %d.%d at addr: %p\n",
248 (int)cvmx_bootmem_desc->major_version,
249 (int)cvmx_bootmem_desc->minor_version,
250 cvmx_bootmem_desc);
251 goto error_out;
252 }
253
254
255
256
257
258
259
260
261
262
263 if (!req_size)
264 goto error_out;
265
266
267 req_size = (req_size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) &
268 ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1);
269
270
271
272
273
274
275
276 if (address_min && !address_max)
277 address_max = address_min + req_size;
278 else if (!address_min && !address_max)
279 address_max = ~0ull;
280
281
282
283
284
285
286 if (alignment < CVMX_BOOTMEM_ALIGNMENT_SIZE)
287 alignment = CVMX_BOOTMEM_ALIGNMENT_SIZE;
288
289
290
291
292
293
294 if (alignment)
295 address_min = ALIGN(address_min, alignment);
296
297
298
299
300
301
302 if (req_size > address_max - address_min)
303 goto error_out;
304
305
306
307 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
308 cvmx_bootmem_lock();
309 head_addr = cvmx_bootmem_desc->head_addr;
310 ent_addr = head_addr;
311 for (; ent_addr;
312 prev_addr = ent_addr,
313 ent_addr = cvmx_bootmem_phy_get_next(ent_addr)) {
314 uint64_t usable_base, usable_max;
315 uint64_t ent_size = cvmx_bootmem_phy_get_size(ent_addr);
316
317 if (cvmx_bootmem_phy_get_next(ent_addr)
318 && ent_addr > cvmx_bootmem_phy_get_next(ent_addr)) {
319 cvmx_dprintf("Internal bootmem_alloc() error: ent: "
320 "0x%llx, next: 0x%llx\n",
321 (unsigned long long)ent_addr,
322 (unsigned long long)
323 cvmx_bootmem_phy_get_next(ent_addr));
324 goto error_out;
325 }
326
327
328
329
330
331
332 usable_base =
333 ALIGN(max(address_min, ent_addr), alignment);
334 usable_max = min(address_max, ent_addr + ent_size);
335
336
337
338
339
340 desired_min_addr = usable_base;
341
342
343
344
345 if (!((ent_addr + ent_size) > usable_base
346 && ent_addr < address_max
347 && req_size <= usable_max - usable_base))
348 continue;
349
350
351
352
353
354
355 if (flags & CVMX_BOOTMEM_FLAG_END_ALLOC) {
356 desired_min_addr = usable_max - req_size;
357
358
359
360
361 desired_min_addr &= ~(alignment - 1);
362 }
363
364
365 if (desired_min_addr == ent_addr) {
366 if (req_size < ent_size) {
367
368
369
370
371 new_ent_addr = ent_addr + req_size;
372 cvmx_bootmem_phy_set_next(new_ent_addr,
373 cvmx_bootmem_phy_get_next(ent_addr));
374 cvmx_bootmem_phy_set_size(new_ent_addr,
375 ent_size -
376 req_size);
377
378
379
380
381
382 cvmx_bootmem_phy_set_next(ent_addr,
383 new_ent_addr);
384 }
385
386
387
388
389
390 if (prev_addr)
391 cvmx_bootmem_phy_set_next(prev_addr,
392 cvmx_bootmem_phy_get_next(ent_addr));
393 else
394
395
396
397
398 cvmx_bootmem_desc->head_addr =
399 cvmx_bootmem_phy_get_next(ent_addr);
400
401 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
402 cvmx_bootmem_unlock();
403 return desired_min_addr;
404 }
405
406
407
408
409
410
411
412
413
414
415
416 new_ent_addr = desired_min_addr;
417 cvmx_bootmem_phy_set_next(new_ent_addr,
418 cvmx_bootmem_phy_get_next
419 (ent_addr));
420 cvmx_bootmem_phy_set_size(new_ent_addr,
421 cvmx_bootmem_phy_get_size
422 (ent_addr) -
423 (desired_min_addr -
424 ent_addr));
425 cvmx_bootmem_phy_set_size(ent_addr,
426 desired_min_addr - ent_addr);
427 cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr);
428
429 }
430 error_out:
431
432 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
433 cvmx_bootmem_unlock();
434 return -1;
435 }
436
437 int __cvmx_bootmem_phy_free(uint64_t phy_addr, uint64_t size, uint32_t flags)
438 {
439 uint64_t cur_addr;
440 uint64_t prev_addr = 0;
441 int retval = 0;
442
443 #ifdef DEBUG
444 cvmx_dprintf("__cvmx_bootmem_phy_free addr: 0x%llx, size: 0x%llx\n",
445 (unsigned long long)phy_addr, (unsigned long long)size);
446 #endif
447 if (cvmx_bootmem_desc->major_version > 3) {
448 cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
449 "version: %d.%d at addr: %p\n",
450 (int)cvmx_bootmem_desc->major_version,
451 (int)cvmx_bootmem_desc->minor_version,
452 cvmx_bootmem_desc);
453 return 0;
454 }
455
456
457 if (!size)
458 return 0;
459
460 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
461 cvmx_bootmem_lock();
462 cur_addr = cvmx_bootmem_desc->head_addr;
463 if (cur_addr == 0 || phy_addr < cur_addr) {
464
465 if (cur_addr && phy_addr + size > cur_addr)
466 goto bootmem_free_done;
467 else if (phy_addr + size == cur_addr) {
468
469 cvmx_bootmem_phy_set_next(phy_addr,
470 cvmx_bootmem_phy_get_next
471 (cur_addr));
472 cvmx_bootmem_phy_set_size(phy_addr,
473 cvmx_bootmem_phy_get_size
474 (cur_addr) + size);
475 cvmx_bootmem_desc->head_addr = phy_addr;
476
477 } else {
478
479 cvmx_bootmem_phy_set_next(phy_addr, cur_addr);
480 cvmx_bootmem_phy_set_size(phy_addr, size);
481 cvmx_bootmem_desc->head_addr = phy_addr;
482 }
483 retval = 1;
484 goto bootmem_free_done;
485 }
486
487
488 while (cur_addr && phy_addr > cur_addr) {
489 prev_addr = cur_addr;
490 cur_addr = cvmx_bootmem_phy_get_next(cur_addr);
491 }
492
493 if (!cur_addr) {
494
495
496
497
498
499 if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) ==
500 phy_addr) {
501 cvmx_bootmem_phy_set_size(prev_addr,
502 cvmx_bootmem_phy_get_size
503 (prev_addr) + size);
504 } else {
505 cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
506 cvmx_bootmem_phy_set_size(phy_addr, size);
507 cvmx_bootmem_phy_set_next(phy_addr, 0);
508 }
509 retval = 1;
510 goto bootmem_free_done;
511 } else {
512
513
514
515
516 if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) ==
517 phy_addr) {
518
519 cvmx_bootmem_phy_set_size(prev_addr,
520 cvmx_bootmem_phy_get_size
521 (prev_addr) + size);
522 if (phy_addr + size == cur_addr) {
523
524 cvmx_bootmem_phy_set_size(prev_addr,
525 cvmx_bootmem_phy_get_size(cur_addr) +
526 cvmx_bootmem_phy_get_size(prev_addr));
527 cvmx_bootmem_phy_set_next(prev_addr,
528 cvmx_bootmem_phy_get_next(cur_addr));
529 }
530 retval = 1;
531 goto bootmem_free_done;
532 } else if (phy_addr + size == cur_addr) {
533
534 cvmx_bootmem_phy_set_size(phy_addr,
535 cvmx_bootmem_phy_get_size
536 (cur_addr) + size);
537 cvmx_bootmem_phy_set_next(phy_addr,
538 cvmx_bootmem_phy_get_next
539 (cur_addr));
540 cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
541 retval = 1;
542 goto bootmem_free_done;
543 }
544
545
546 cvmx_bootmem_phy_set_size(phy_addr, size);
547 cvmx_bootmem_phy_set_next(phy_addr, cur_addr);
548 cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
549
550 }
551 retval = 1;
552
553 bootmem_free_done:
554 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
555 cvmx_bootmem_unlock();
556 return retval;
557
558 }
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573 static struct cvmx_bootmem_named_block_desc *
574 cvmx_bootmem_phy_named_block_find(char *name, uint32_t flags)
575 {
576 unsigned int i;
577 struct cvmx_bootmem_named_block_desc *named_block_array_ptr;
578
579 #ifdef DEBUG
580 cvmx_dprintf("cvmx_bootmem_phy_named_block_find: %s\n", name);
581 #endif
582
583
584
585
586 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
587 cvmx_bootmem_lock();
588
589
590 named_block_array_ptr = (struct cvmx_bootmem_named_block_desc *)
591 cvmx_phys_to_ptr(cvmx_bootmem_desc->named_block_array_addr);
592
593 #ifdef DEBUG
594 cvmx_dprintf
595 ("cvmx_bootmem_phy_named_block_find: named_block_array_ptr: %p\n",
596 named_block_array_ptr);
597 #endif
598 if (cvmx_bootmem_desc->major_version == 3) {
599 for (i = 0;
600 i < cvmx_bootmem_desc->named_block_num_blocks; i++) {
601 if ((name && named_block_array_ptr[i].size
602 && !strncmp(name, named_block_array_ptr[i].name,
603 cvmx_bootmem_desc->named_block_name_len
604 - 1))
605 || (!name && !named_block_array_ptr[i].size)) {
606 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
607 cvmx_bootmem_unlock();
608
609 return &(named_block_array_ptr[i]);
610 }
611 }
612 } else {
613 cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
614 "version: %d.%d at addr: %p\n",
615 (int)cvmx_bootmem_desc->major_version,
616 (int)cvmx_bootmem_desc->minor_version,
617 cvmx_bootmem_desc);
618 }
619 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
620 cvmx_bootmem_unlock();
621
622 return NULL;
623 }
624
625 void *cvmx_bootmem_alloc_named_range_once(uint64_t size, uint64_t min_addr,
626 uint64_t max_addr, uint64_t align,
627 char *name,
628 void (*init) (void *))
629 {
630 int64_t addr;
631 void *ptr;
632 uint64_t named_block_desc_addr;
633
634 named_block_desc_addr = (uint64_t)
635 cvmx_bootmem_phy_named_block_find(name,
636 (uint32_t)CVMX_BOOTMEM_FLAG_NO_LOCKING);
637
638 if (named_block_desc_addr) {
639 addr = CVMX_BOOTMEM_NAMED_GET_FIELD(named_block_desc_addr,
640 base_addr);
641 return cvmx_phys_to_ptr(addr);
642 }
643
644 addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr,
645 align, name,
646 (uint32_t)CVMX_BOOTMEM_FLAG_NO_LOCKING);
647
648 if (addr < 0)
649 return NULL;
650 ptr = cvmx_phys_to_ptr(addr);
651
652 if (init)
653 init(ptr);
654 else
655 memset(ptr, 0, size);
656
657 return ptr;
658 }
659 EXPORT_SYMBOL(cvmx_bootmem_alloc_named_range_once);
660
661 struct cvmx_bootmem_named_block_desc *cvmx_bootmem_find_named_block(char *name)
662 {
663 return cvmx_bootmem_phy_named_block_find(name, 0);
664 }
665 EXPORT_SYMBOL(cvmx_bootmem_find_named_block);
666
667
668
669
670
671
672
673
674
675
676 static int cvmx_bootmem_phy_named_block_free(char *name, uint32_t flags)
677 {
678 struct cvmx_bootmem_named_block_desc *named_block_ptr;
679
680 if (cvmx_bootmem_desc->major_version != 3) {
681 cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: "
682 "%d.%d at addr: %p\n",
683 (int)cvmx_bootmem_desc->major_version,
684 (int)cvmx_bootmem_desc->minor_version,
685 cvmx_bootmem_desc);
686 return 0;
687 }
688 #ifdef DEBUG
689 cvmx_dprintf("cvmx_bootmem_phy_named_block_free: %s\n", name);
690 #endif
691
692
693
694
695
696 cvmx_bootmem_lock();
697
698 named_block_ptr =
699 cvmx_bootmem_phy_named_block_find(name,
700 CVMX_BOOTMEM_FLAG_NO_LOCKING);
701 if (named_block_ptr) {
702 #ifdef DEBUG
703 cvmx_dprintf("cvmx_bootmem_phy_named_block_free: "
704 "%s, base: 0x%llx, size: 0x%llx\n",
705 name,
706 (unsigned long long)named_block_ptr->base_addr,
707 (unsigned long long)named_block_ptr->size);
708 #endif
709 __cvmx_bootmem_phy_free(named_block_ptr->base_addr,
710 named_block_ptr->size,
711 CVMX_BOOTMEM_FLAG_NO_LOCKING);
712 named_block_ptr->size = 0;
713
714 }
715
716 cvmx_bootmem_unlock();
717 return named_block_ptr != NULL;
718 }
719
720 int cvmx_bootmem_free_named(char *name)
721 {
722 return cvmx_bootmem_phy_named_block_free(name, 0);
723 }
724
725 int64_t cvmx_bootmem_phy_named_block_alloc(uint64_t size, uint64_t min_addr,
726 uint64_t max_addr,
727 uint64_t alignment,
728 char *name,
729 uint32_t flags)
730 {
731 int64_t addr_allocated;
732 struct cvmx_bootmem_named_block_desc *named_block_desc_ptr;
733
734 #ifdef DEBUG
735 cvmx_dprintf("cvmx_bootmem_phy_named_block_alloc: size: 0x%llx, min: "
736 "0x%llx, max: 0x%llx, align: 0x%llx, name: %s\n",
737 (unsigned long long)size,
738 (unsigned long long)min_addr,
739 (unsigned long long)max_addr,
740 (unsigned long long)alignment,
741 name);
742 #endif
743 if (cvmx_bootmem_desc->major_version != 3) {
744 cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: "
745 "%d.%d at addr: %p\n",
746 (int)cvmx_bootmem_desc->major_version,
747 (int)cvmx_bootmem_desc->minor_version,
748 cvmx_bootmem_desc);
749 return -1;
750 }
751
752
753
754
755
756 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
757 cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
758
759
760 named_block_desc_ptr =
761 cvmx_bootmem_phy_named_block_find(NULL,
762 flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
763
764
765
766
767
768 if (cvmx_bootmem_phy_named_block_find(name,
769 flags | CVMX_BOOTMEM_FLAG_NO_LOCKING) || !named_block_desc_ptr) {
770 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
771 cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
772 return -1;
773 }
774
775
776
777
778
779
780
781
782 size = ALIGN(size, CVMX_BOOTMEM_ALIGNMENT_SIZE);
783
784 addr_allocated = cvmx_bootmem_phy_alloc(size, min_addr, max_addr,
785 alignment,
786 flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
787 if (addr_allocated >= 0) {
788 named_block_desc_ptr->base_addr = addr_allocated;
789 named_block_desc_ptr->size = size;
790 strncpy(named_block_desc_ptr->name, name,
791 cvmx_bootmem_desc->named_block_name_len);
792 named_block_desc_ptr->name[cvmx_bootmem_desc->named_block_name_len - 1] = 0;
793 }
794
795 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
796 cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
797 return addr_allocated;
798 }
799
800 struct cvmx_bootmem_desc *cvmx_bootmem_get_desc(void)
801 {
802 return cvmx_bootmem_desc;
803 }