1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2018-2020 Marvell International Ltd. 4 */ 5 6/* 7 * Simple allocate only memory allocator. Used to allocate memory at 8 * application start time. 9 */ 10 11#include <asm/global_data.h> 12 13#include <linux/compat.h> 14#include <linux/io.h> 15#include <linux/types.h> 16 17#include <mach/octeon-model.h> 18#include <mach/cvmx-bootmem.h> 19#include <mach/cvmx-coremask.h> 20#include <mach/cvmx-regs.h> 21 22DECLARE_GLOBAL_DATA_PTR; 23 24/** 25 * This is the physical location of a struct cvmx_bootmem_desc 26 * structure in Octeon's memory. Note that dues to addressing 27 * limits or runtime environment it might not be possible to 28 * create a C pointer to this structure. 29 */ 30static u64 cvmx_bootmem_desc_addr; 31 32/** 33 * This macro returns the size of a member of a structure. 34 * Logically it is the same as "sizeof(s::field)" in C++, but 35 * C lacks the "::" operator. 36 */ 37#define SIZEOF_FIELD(s, field) sizeof(((s *)NULL)->field) 38 39/** 40 * This macro returns a member of the struct cvmx_bootmem_desc 41 * structure. These members can't be directly addressed as 42 * they might be in memory not directly reachable. In the case 43 * where bootmem is compiled with LINUX_HOST, the structure 44 * itself might be located on a remote Octeon. The argument 45 * "field" is the member name of the struct cvmx_bootmem_desc to read. 46 * Regardless of the type of the field, the return type is always 47 * a u64. 48 */ 49#define CVMX_BOOTMEM_DESC_GET_FIELD(field) \ 50 __cvmx_bootmem_desc_get(cvmx_bootmem_desc_addr, \ 51 offsetof(struct cvmx_bootmem_desc, field), \ 52 SIZEOF_FIELD(struct cvmx_bootmem_desc, field)) 53 54/** 55 * This macro writes a member of the struct cvmx_bootmem_desc 56 * structure. These members can't be directly addressed as 57 * they might be in memory not directly reachable. In the case 58 * where bootmem is compiled with LINUX_HOST, the structure 59 * itself might be located on a remote Octeon. The argument 60 * "field" is the member name of the struct cvmx_bootmem_desc to write. 61 */ 62#define CVMX_BOOTMEM_DESC_SET_FIELD(field, value) \ 63 __cvmx_bootmem_desc_set(cvmx_bootmem_desc_addr, \ 64 offsetof(struct cvmx_bootmem_desc, field), \ 65 SIZEOF_FIELD(struct cvmx_bootmem_desc, field), \ 66 value) 67 68/** 69 * This macro returns a member of the 70 * struct cvmx_bootmem_named_block_desc structure. These members can't 71 * be directly addressed as they might be in memory not directly 72 * reachable. In the case where bootmem is compiled with 73 * LINUX_HOST, the structure itself might be located on a remote 74 * Octeon. The argument "field" is the member name of the 75 * struct cvmx_bootmem_named_block_desc to read. Regardless of the type 76 * of the field, the return type is always a u64. The "addr" 77 * parameter is the physical address of the structure. 78 */ 79#define CVMX_BOOTMEM_NAMED_GET_FIELD(addr, field) \ 80 __cvmx_bootmem_desc_get(addr, \ 81 offsetof(struct cvmx_bootmem_named_block_desc, field), \ 82 SIZEOF_FIELD(struct cvmx_bootmem_named_block_desc, field)) 83 84/** 85 * This macro writes a member of the struct cvmx_bootmem_named_block_desc 86 * structure. These members can't be directly addressed as 87 * they might be in memory not directly reachable. In the case 88 * where bootmem is compiled with LINUX_HOST, the structure 89 * itself might be located on a remote Octeon. The argument 90 * "field" is the member name of the 91 * struct cvmx_bootmem_named_block_desc to write. The "addr" parameter 92 * is the physical address of the structure. 93 */ 94#define CVMX_BOOTMEM_NAMED_SET_FIELD(addr, field, value) \ 95 __cvmx_bootmem_desc_set(addr, \ 96 offsetof(struct cvmx_bootmem_named_block_desc, field), \ 97 SIZEOF_FIELD(struct cvmx_bootmem_named_block_desc, field), \ 98 value) 99 100/** 101 * This function is the implementation of the get macros defined 102 * for individual structure members. The argument are generated 103 * by the macros inorder to read only the needed memory. 104 * 105 * @param base 64bit physical address of the complete structure 106 * @param offset Offset from the beginning of the structure to the member being 107 * accessed. 108 * @param size Size of the structure member. 109 * 110 * Return: Value of the structure member promoted into a u64. 111 */ 112static inline u64 __cvmx_bootmem_desc_get(u64 base, int offset, 113 int size) 114{ 115 base = (1ull << 63) | (base + offset); 116 switch (size) { 117 case 4: 118 return cvmx_read64_uint32(base); 119 case 8: 120 return cvmx_read64_uint64(base); 121 default: 122 return 0; 123 } 124} 125 126/** 127 * This function is the implementation of the set macros defined 128 * for individual structure members. The argument are generated 129 * by the macros in order to write only the needed memory. 130 * 131 * @param base 64bit physical address of the complete structure 132 * @param offset Offset from the beginning of the structure to the member being 133 * accessed. 134 * @param size Size of the structure member. 135 * @param value Value to write into the structure 136 */ 137static inline void __cvmx_bootmem_desc_set(u64 base, int offset, int size, 138 u64 value) 139{ 140 base = (1ull << 63) | (base + offset); 141 switch (size) { 142 case 4: 143 cvmx_write64_uint32(base, value); 144 break; 145 case 8: 146 cvmx_write64_uint64(base, value); 147 break; 148 default: 149 break; 150 } 151} 152 153/** 154 * This function returns the address of the bootmem descriptor lock. 155 * 156 * Return: 64-bit address in KSEG0 of the bootmem descriptor block 157 */ 158static inline u64 __cvmx_bootmem_get_lock_addr(void) 159{ 160 return (1ull << 63) | 161 (cvmx_bootmem_desc_addr + offsetof(struct cvmx_bootmem_desc, lock)); 162} 163 164/** 165 * This function retrieves the string name of a named block. It is 166 * more complicated than a simple memcpy() since the named block 167 * descriptor may not be directly accessible. 168 * 169 * @param addr Physical address of the named block descriptor 170 * @param str String to receive the named block string name 171 * @param len Length of the string buffer, which must match the length 172 * stored in the bootmem descriptor. 173 */ 174static void CVMX_BOOTMEM_NAMED_GET_NAME(u64 addr, char *str, int len) 175{ 176 int l = len; 177 char *ptr = str; 178 179 addr |= (1ull << 63); 180 addr += offsetof(struct cvmx_bootmem_named_block_desc, name); 181 while (l) { 182 /* 183 * With big-endian in memory byte order, this gives uniform 184 * results for the CPU in either big or Little endian mode. 185 */ 186 u64 blob = cvmx_read64_uint64(addr); 187 int sa = 56; 188 189 addr += sizeof(u64); 190 while (l && sa >= 0) { 191 *ptr++ = (char)(blob >> sa); 192 l--; 193 sa -= 8; 194 } 195 } 196 str[len] = 0; 197} 198 199/** 200 * This function stores the string name of a named block. It is 201 * more complicated than a simple memcpy() since the named block 202 * descriptor may not be directly accessible. 203 * 204 * @param addr Physical address of the named block descriptor 205 * @param str String to store into the named block string name 206 * @param len Length of the string buffer, which must match the length 207 * stored in the bootmem descriptor. 208 */ 209void CVMX_BOOTMEM_NAMED_SET_NAME(u64 addr, const char *str, int len) 210{ 211 int l = len; 212 213 addr |= (1ull << 63); 214 addr += offsetof(struct cvmx_bootmem_named_block_desc, name); 215 216 while (l) { 217 /* 218 * With big-endian in memory byte order, this gives uniform 219 * results for the CPU in either big or Little endian mode. 220 */ 221 u64 blob = 0; 222 int sa = 56; 223 224 while (l && sa >= 0) { 225 u64 c = (u8)(*str++); 226 227 l--; 228 if (l == 0) 229 c = 0; 230 blob |= c << sa; 231 sa -= 8; 232 } 233 cvmx_write64_uint64(addr, blob); 234 addr += sizeof(u64); 235 } 236} 237 238/* See header file for descriptions of functions */ 239 240/* 241 * Wrapper functions are provided for reading/writing the size and next block 242 * values as these may not be directly addressible (in 32 bit applications, for 243 * instance.) 244 * 245 * Offsets of data elements in bootmem list, must match 246 * struct cvmx_bootmem_block_header 247 */ 248#define NEXT_OFFSET 0 249#define SIZE_OFFSET 8 250 251static void cvmx_bootmem_phy_set_size(u64 addr, u64 size) 252{ 253 cvmx_write64_uint64((addr + SIZE_OFFSET) | (1ull << 63), size); 254} 255 256static void cvmx_bootmem_phy_set_next(u64 addr, u64 next) 257{ 258 cvmx_write64_uint64((addr + NEXT_OFFSET) | (1ull << 63), next); 259} 260 261static u64 cvmx_bootmem_phy_get_size(u64 addr) 262{ 263 return cvmx_read64_uint64((addr + SIZE_OFFSET) | (1ull << 63)); 264} 265 266static u64 cvmx_bootmem_phy_get_next(u64 addr) 267{ 268 return cvmx_read64_uint64((addr + NEXT_OFFSET) | (1ull << 63)); 269} 270 271/** 272 * Check the version information on the bootmem descriptor 273 * 274 * @param exact_match 275 * Exact major version to check against. A zero means 276 * check that the version supports named blocks. 277 * 278 * Return: Zero if the version is correct. Negative if the version is 279 * incorrect. Failures also cause a message to be displayed. 280 */ 281static int __cvmx_bootmem_check_version(int exact_match) 282{ 283 int major_version; 284 285 major_version = CVMX_BOOTMEM_DESC_GET_FIELD(major_version); 286 if ((major_version > 3) || 287 (exact_match && major_version != exact_match)) { 288 debug("ERROR: Incompatible bootmem descriptor version: %d.%d at addr: 0x%llx\n", 289 major_version, 290 (int)CVMX_BOOTMEM_DESC_GET_FIELD(minor_version), 291 CAST_ULL(cvmx_bootmem_desc_addr)); 292 return -1; 293 } else { 294 return 0; 295 } 296} 297 298/** 299 * Get the low level bootmem descriptor lock. If no locking 300 * is specified in the flags, then nothing is done. 301 * 302 * @param flags CVMX_BOOTMEM_FLAG_NO_LOCKING means this functions should do 303 * nothing. This is used to support nested bootmem calls. 304 */ 305static inline void __cvmx_bootmem_lock(u32 flags) 306{ 307 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) { 308 /* 309 * Unfortunately we can't use the normal cvmx-spinlock code as 310 * the memory for the bootmem descriptor may be not accessible 311 * by a C pointer. We use a 64bit XKPHYS address to access the 312 * memory directly 313 */ 314 u64 lock_addr = (1ull << 63) | 315 (cvmx_bootmem_desc_addr + offsetof(struct cvmx_bootmem_desc, 316 lock)); 317 unsigned int tmp; 318 319 __asm__ __volatile__(".set noreorder\n" 320 "1: ll %[tmp], 0(%[addr])\n" 321 " bnez %[tmp], 1b\n" 322 " li %[tmp], 1\n" 323 " sc %[tmp], 0(%[addr])\n" 324 " beqz %[tmp], 1b\n" 325 " nop\n" 326 ".set reorder\n" 327 : [tmp] "=&r"(tmp) 328 : [addr] "r"(lock_addr) 329 : "memory"); 330 } 331} 332 333/** 334 * Release the low level bootmem descriptor lock. If no locking 335 * is specified in the flags, then nothing is done. 336 * 337 * @param flags CVMX_BOOTMEM_FLAG_NO_LOCKING means this functions should do 338 * nothing. This is used to support nested bootmem calls. 339 */ 340static inline void __cvmx_bootmem_unlock(u32 flags) 341{ 342 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) { 343 /* 344 * Unfortunately we can't use the normal cvmx-spinlock code as 345 * the memory for the bootmem descriptor may be not accessible 346 * by a C pointer. We use a 64bit XKPHYS address to access the 347 * memory directly 348 */ 349 u64 lock_addr = __cvmx_bootmem_get_lock_addr(); 350 351 CVMX_SYNCW; 352 __asm__ __volatile__("sw $0, 0(%[addr])\n" 353 : : [addr] "r"(lock_addr) 354 : "memory"); 355 CVMX_SYNCW; 356 } 357} 358 359/* 360 * Some of the cvmx-bootmem functions dealing with C pointers are not 361 * supported when we are compiling for CVMX_BUILD_FOR_LINUX_HOST. This 362 * ifndef removes these functions when they aren't needed. 363 * 364 * This functions takes an address range and adjusts it as necessary 365 * to match the ABI that is currently being used. This is required to 366 * ensure that bootmem_alloc* functions only return valid pointers for 367 * 32 bit ABIs 368 */ 369static int __cvmx_validate_mem_range(u64 *min_addr_ptr, 370 u64 *max_addr_ptr) 371{ 372 u64 max_phys = (1ull << 29) - 0x10; /* KSEG0 */ 373 374 *min_addr_ptr = min_t(u64, max_t(u64, *min_addr_ptr, 0x0), max_phys); 375 if (!*max_addr_ptr) { 376 *max_addr_ptr = max_phys; 377 } else { 378 *max_addr_ptr = max_t(u64, min_t(u64, *max_addr_ptr, 379 max_phys), 0x0); 380 } 381 382 return 0; 383} 384 385u64 cvmx_bootmem_phy_alloc_range(u64 size, u64 alignment, 386 u64 min_addr, u64 max_addr) 387{ 388 s64 address; 389 390 __cvmx_validate_mem_range(&min_addr, &max_addr); 391 address = cvmx_bootmem_phy_alloc(size, min_addr, max_addr, 392 alignment, 0); 393 if (address > 0) 394 return address; 395 else 396 return 0; 397} 398 399void *cvmx_bootmem_alloc_range(u64 size, u64 alignment, 400 u64 min_addr, u64 max_addr) 401{ 402 s64 address; 403 404 __cvmx_validate_mem_range(&min_addr, &max_addr); 405 address = cvmx_bootmem_phy_alloc(size, min_addr, max_addr, 406 alignment, 0); 407 408 if (address > 0) 409 return cvmx_phys_to_ptr(address); 410 else 411 return NULL; 412} 413 414void *cvmx_bootmem_alloc_address(u64 size, u64 address, 415 u64 alignment) 416{ 417 return cvmx_bootmem_alloc_range(size, alignment, address, 418 address + size); 419} 420 421void *cvmx_bootmem_alloc_node(u64 node, u64 size, u64 alignment) 422{ 423 return cvmx_bootmem_alloc_range(size, alignment, 424 node << CVMX_NODE_MEM_SHIFT, 425 ((node + 1) << CVMX_NODE_MEM_SHIFT) - 1); 426} 427 428void *cvmx_bootmem_alloc(u64 size, u64 alignment) 429{ 430 return cvmx_bootmem_alloc_range(size, alignment, 0, 0); 431} 432 433void *cvmx_bootmem_alloc_named_range_once(u64 size, u64 min_addr, 434 u64 max_addr, u64 align, 435 const char *name, 436 void (*init)(void *)) 437{ 438 u64 named_block_desc_addr; 439 void *ptr; 440 s64 addr; 441 442 __cvmx_bootmem_lock(0); 443 444 __cvmx_validate_mem_range(&min_addr, &max_addr); 445 named_block_desc_addr = 446 cvmx_bootmem_phy_named_block_find(name, 447 CVMX_BOOTMEM_FLAG_NO_LOCKING); 448 449 if (named_block_desc_addr) { 450 addr = CVMX_BOOTMEM_NAMED_GET_FIELD(named_block_desc_addr, 451 base_addr); 452 __cvmx_bootmem_unlock(0); 453 return cvmx_phys_to_ptr(addr); 454 } 455 456 addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr, 457 align, name, 458 CVMX_BOOTMEM_FLAG_NO_LOCKING); 459 460 if (addr < 0) { 461 __cvmx_bootmem_unlock(0); 462 return NULL; 463 } 464 ptr = cvmx_phys_to_ptr(addr); 465 466 if (init) 467 init(ptr); 468 else 469 memset(ptr, 0, size); 470 471 __cvmx_bootmem_unlock(0); 472 return ptr; 473} 474 475void *cvmx_bootmem_alloc_named_range_flags(u64 size, u64 min_addr, 476 u64 max_addr, u64 align, 477 const char *name, u32 flags) 478{ 479 s64 addr; 480 481 __cvmx_validate_mem_range(&min_addr, &max_addr); 482 addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr, 483 align, name, flags); 484 if (addr >= 0) 485 return cvmx_phys_to_ptr(addr); 486 else 487 return NULL; 488} 489 490void *cvmx_bootmem_alloc_named_range(u64 size, u64 min_addr, 491 u64 max_addr, u64 align, 492 const char *name) 493{ 494 return cvmx_bootmem_alloc_named_range_flags(size, min_addr, max_addr, 495 align, name, 0); 496} 497 498void *cvmx_bootmem_alloc_named_address(u64 size, u64 address, 499 const char *name) 500{ 501 return cvmx_bootmem_alloc_named_range(size, address, address + size, 502 0, name); 503} 504 505void *cvmx_bootmem_alloc_named(u64 size, u64 alignment, 506 const char *name) 507{ 508 return cvmx_bootmem_alloc_named_range(size, 0, 0, alignment, name); 509} 510 511void *cvmx_bootmem_alloc_named_flags(u64 size, u64 alignment, 512 const char *name, u32 flags) 513{ 514 return cvmx_bootmem_alloc_named_range_flags(size, 0, 0, alignment, 515 name, flags); 516} 517 518int cvmx_bootmem_free_named(const char *name) 519{ 520 return cvmx_bootmem_phy_named_block_free(name, 0); 521} 522 523/** 524 * Find a named block with flags 525 * 526 * @param name is the block name 527 * @param flags indicates the need to use locking during search 528 * Return: pointer to named block descriptor 529 * 530 * Note: this function returns a pointer to a static structure, 531 * and is therefore not re-entrant. 532 * Making this function re-entrant will break backward compatibility. 533 */ 534const struct cvmx_bootmem_named_block_desc * 535__cvmx_bootmem_find_named_block_flags(const char *name, u32 flags) 536{ 537 static struct cvmx_bootmem_named_block_desc desc; 538 u64 named_addr = cvmx_bootmem_phy_named_block_find(name, flags); 539 540 if (named_addr) { 541 desc.base_addr = CVMX_BOOTMEM_NAMED_GET_FIELD(named_addr, 542 base_addr); 543 desc.size = CVMX_BOOTMEM_NAMED_GET_FIELD(named_addr, size); 544 strncpy(desc.name, name, sizeof(desc.name)); 545 desc.name[sizeof(desc.name) - 1] = 0; 546 return &desc; 547 } else { 548 return NULL; 549 } 550} 551 552const struct cvmx_bootmem_named_block_desc * 553cvmx_bootmem_find_named_block(const char *name) 554{ 555 return __cvmx_bootmem_find_named_block_flags(name, 0); 556} 557 558void cvmx_bootmem_print_named(void) 559{ 560 cvmx_bootmem_phy_named_block_print(); 561} 562 563int cvmx_bootmem_init(u64 mem_desc_addr) 564{ 565 if (!cvmx_bootmem_desc_addr) 566 cvmx_bootmem_desc_addr = mem_desc_addr; 567 568 return 0; 569} 570 571u64 cvmx_bootmem_available_mem(u64 min_block_size) 572{ 573 return cvmx_bootmem_phy_available_mem(min_block_size); 574} 575 576/* 577 * The cvmx_bootmem_phy* functions below return 64 bit physical 578 * addresses, and expose more features that the cvmx_bootmem_functions 579 * above. These are required for full memory space access in 32 bit 580 * applications, as well as for using some advance features. Most 581 * applications should not need to use these. 582 */ 583 584s64 cvmx_bootmem_phy_alloc(u64 req_size, u64 address_min, 585 u64 address_max, u64 alignment, 586 u32 flags) 587{ 588 u64 head_addr, ent_addr, ent_size; 589 u64 target_ent_addr = 0, target_prev_addr = 0; 590 u64 target_size = ~0ull; 591 u64 free_start, free_end; 592 u64 next_addr, prev_addr = 0; 593 u64 new_ent_addr = 0, new_ent_size; 594 u64 desired_min_addr, usable_max; 595 u64 align, align_mask; 596 597 debug("%s: req_size: 0x%llx, min_addr: 0x%llx, max_addr: 0x%llx, align: 0x%llx\n", 598 __func__, CAST_ULL(req_size), CAST_ULL(address_min), 599 CAST_ULL(address_max), CAST_ULL(alignment)); 600 601 if (__cvmx_bootmem_check_version(0)) 602 return -1; 603 604 /* 605 * Do a variety of checks to validate the arguments. The 606 * allocator code will later assume that these checks have 607 * been made. We validate that the requested constraints are 608 * not self-contradictory before we look through the list of 609 * available memory 610 */ 611 612 /* 0 is not a valid req_size for this allocator */ 613 if (!req_size) 614 return -1; 615 616 /* Round req_size up to multiple of minimum alignment bytes */ 617 req_size = (req_size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) & 618 ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1); 619 620 /* Make sure alignment is power of 2, and at least the minimum */ 621 for (align = CVMX_BOOTMEM_ALIGNMENT_SIZE; 622 align < (1ull << 48); 623 align <<= 1) { 624 if (align >= alignment) 625 break; 626 } 627 628 align_mask = ~(align - 1); 629 630 /* 631 * Adjust address minimum based on requested alignment (round 632 * up to meet alignment). Do this here so we can reject 633 * impossible requests up front. (NOP for address_min == 0) 634 */ 635 address_min = (address_min + (align - 1)) & align_mask; 636 637 /* 638 * Convert !0 address_min and 0 address_max to special case of 639 * range that specifies an exact memory block to allocate. Do 640 * this before other checks and adjustments so that this 641 * tranformation will be validated 642 */ 643 if (address_min && !address_max) 644 address_max = address_min + req_size; 645 else if (!address_min && !address_max) 646 address_max = ~0ull; /* If no limits given, use max */ 647 648 /* 649 * Reject inconsistent args. We have adjusted these, so this 650 * may fail due to our internal changes even if this check 651 * would pass for the values the user supplied. 652 */ 653 if (req_size > address_max - address_min) 654 return -1; 655 656 __cvmx_bootmem_lock(flags); 657 658 /* Walk through the list entries to find the right fit */ 659 head_addr = CVMX_BOOTMEM_DESC_GET_FIELD(head_addr); 660 661 for (ent_addr = head_addr; 662 ent_addr != 0ULL && ent_addr < address_max; 663 prev_addr = ent_addr, 664 ent_addr = cvmx_bootmem_phy_get_next(ent_addr)) { 665 /* Raw free block size */ 666 ent_size = cvmx_bootmem_phy_get_size(ent_addr); 667 next_addr = cvmx_bootmem_phy_get_next(ent_addr); 668 669 /* Validate the free list ascending order */ 670 if (ent_size < CVMX_BOOTMEM_ALIGNMENT_SIZE || 671 (next_addr && ent_addr > next_addr)) { 672 debug("ERROR: %s: bad free list ent: %#llx, next: %#llx\n", 673 __func__, CAST_ULL(ent_addr), 674 CAST_ULL(next_addr)); 675 goto error_out; 676 } 677 678 /* adjust free block edges for alignment */ 679 free_start = (ent_addr + align - 1) & align_mask; 680 free_end = (ent_addr + ent_size) & align_mask; 681 682 /* check that free block is large enough */ 683 if ((free_start + req_size) > free_end) 684 continue; 685 686 /* check that desired start is within the free block */ 687 if (free_end < address_min || free_start > address_max) 688 continue; 689 if ((free_end - address_min) < req_size) 690 continue; 691 if ((address_max - free_start) < req_size) 692 continue; 693 694 /* Found usebale free block */ 695 target_ent_addr = ent_addr; 696 target_prev_addr = prev_addr; 697 target_size = ent_size; 698 699 /* Continue looking for highest/best block that fits */ 700 } 701 702 /* Bail if the search has resulted in no eligible free blocks */ 703 if (target_ent_addr == 0) { 704 debug("%s: eligible free block not found\n", __func__); 705 goto error_out; 706 } 707 708 /* Found the free block to allocate from */ 709 ent_addr = target_ent_addr; 710 prev_addr = target_prev_addr; 711 ent_size = target_size; 712 713 debug("%s: using free block at %#010llx size %#llx\n", 714 __func__, CAST_ULL(ent_addr), CAST_ULL(ent_size)); 715 716 /* Always allocate from the end of a free block */ 717 usable_max = min_t(u64, address_max, ent_addr + ent_size); 718 desired_min_addr = usable_max - req_size; 719 desired_min_addr &= align_mask; 720 721 /* Split current free block into up to 3 free blocks */ 722 723 /* Check for head room */ 724 if (desired_min_addr > ent_addr) { 725 /* Create a new free block at the allocation address */ 726 new_ent_addr = desired_min_addr; 727 new_ent_size = ent_size - (desired_min_addr - ent_addr); 728 729 cvmx_bootmem_phy_set_next(new_ent_addr, 730 cvmx_bootmem_phy_get_next(ent_addr)); 731 cvmx_bootmem_phy_set_size(new_ent_addr, new_ent_size); 732 733 /* Split out head room into a new free block */ 734 ent_size -= new_ent_size; 735 cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr); 736 cvmx_bootmem_phy_set_size(ent_addr, ent_size); 737 738 debug("%s: splitting head, addr %#llx size %#llx\n", 739 __func__, CAST_ULL(ent_addr), CAST_ULL(ent_size)); 740 741 /* Make the allocation target the current free block */ 742 prev_addr = ent_addr; 743 ent_addr = new_ent_addr; 744 ent_size = new_ent_size; 745 } 746 747 /* Check for tail room */ 748 if ((desired_min_addr + req_size) < (ent_addr + ent_size)) { 749 new_ent_addr = ent_addr + req_size; 750 new_ent_size = ent_size - req_size; 751 752 /* Create a new free block from tail room */ 753 cvmx_bootmem_phy_set_next(new_ent_addr, 754 cvmx_bootmem_phy_get_next(ent_addr)); 755 cvmx_bootmem_phy_set_size(new_ent_addr, new_ent_size); 756 757 debug("%s: splitting tail, addr %#llx size %#llx\n", 758 __func__, CAST_ULL(new_ent_addr), CAST_ULL(new_ent_size)); 759 760 /* Adjust the current block to exclude tail room */ 761 ent_size = ent_size - new_ent_size; 762 cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr); 763 cvmx_bootmem_phy_set_size(ent_addr, ent_size); 764 } 765 766 /* The current free block IS the allocation target */ 767 if (desired_min_addr != ent_addr || ent_size != req_size) 768 debug("ERROR: %s: internal error - addr %#llx %#llx size %#llx %#llx\n", 769 __func__, CAST_ULL(desired_min_addr), CAST_ULL(ent_addr), 770 CAST_ULL(ent_size), CAST_ULL(req_size)); 771 772 /* Remove the current free block from list */ 773 if (prev_addr) { 774 cvmx_bootmem_phy_set_next(prev_addr, 775 cvmx_bootmem_phy_get_next(ent_addr)); 776 } else { 777 /* head of list being returned, so update head ptr */ 778 CVMX_BOOTMEM_DESC_SET_FIELD(head_addr, 779 cvmx_bootmem_phy_get_next(ent_addr)); 780 } 781 782 __cvmx_bootmem_unlock(flags); 783 debug("%s: allocated size: %#llx, at addr: %#010llx\n", 784 __func__, 785 CAST_ULL(req_size), 786 CAST_ULL(desired_min_addr)); 787 788 return desired_min_addr; 789 790error_out: 791 /* Requested memory not found or argument error */ 792 __cvmx_bootmem_unlock(flags); 793 return -1; 794} 795 796int __cvmx_bootmem_phy_free(u64 phy_addr, u64 size, u32 flags) 797{ 798 u64 cur_addr; 799 u64 prev_addr = 0; /* zero is invalid */ 800 int retval = 0; 801 802 debug("%s addr: %#llx, size: %#llx\n", __func__, 803 CAST_ULL(phy_addr), CAST_ULL(size)); 804 805 if (__cvmx_bootmem_check_version(0)) 806 return 0; 807 808 /* 0 is not a valid size for this allocator */ 809 if (!size || !phy_addr) 810 return 0; 811 812 /* Round size up to mult of minimum alignment bytes */ 813 size = (size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) & 814 ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1); 815 816 __cvmx_bootmem_lock(flags); 817 cur_addr = CVMX_BOOTMEM_DESC_GET_FIELD(head_addr); 818 if (cur_addr == 0 || phy_addr < cur_addr) { 819 /* add at front of list - special case with changing head ptr */ 820 if (cur_addr && phy_addr + size > cur_addr) 821 goto bootmem_free_done; /* error, overlapping section */ 822 else if (phy_addr + size == cur_addr) { 823 /* Add to front of existing first block */ 824 cvmx_bootmem_phy_set_next(phy_addr, 825 cvmx_bootmem_phy_get_next(cur_addr)); 826 cvmx_bootmem_phy_set_size(phy_addr, 827 cvmx_bootmem_phy_get_size(cur_addr) + size); 828 CVMX_BOOTMEM_DESC_SET_FIELD(head_addr, phy_addr); 829 830 } else { 831 /* New block before first block */ 832 /* OK if cur_addr is 0 */ 833 cvmx_bootmem_phy_set_next(phy_addr, cur_addr); 834 cvmx_bootmem_phy_set_size(phy_addr, size); 835 CVMX_BOOTMEM_DESC_SET_FIELD(head_addr, phy_addr); 836 } 837 retval = 1; 838 goto bootmem_free_done; 839 } 840 841 /* Find place in list to add block */ 842 while (cur_addr && phy_addr > cur_addr) { 843 prev_addr = cur_addr; 844 cur_addr = cvmx_bootmem_phy_get_next(cur_addr); 845 } 846 847 if (!cur_addr) { 848 /* 849 * We have reached the end of the list, add on to end, checking 850 * to see if we need to combine with last block 851 */ 852 if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) == phy_addr) { 853 cvmx_bootmem_phy_set_size(prev_addr, 854 cvmx_bootmem_phy_get_size(prev_addr) + size); 855 } else { 856 cvmx_bootmem_phy_set_next(prev_addr, phy_addr); 857 cvmx_bootmem_phy_set_size(phy_addr, size); 858 cvmx_bootmem_phy_set_next(phy_addr, 0); 859 } 860 retval = 1; 861 goto bootmem_free_done; 862 } else { 863 /* 864 * insert between prev and cur nodes, checking for merge with 865 * either/both 866 */ 867 if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) == phy_addr) { 868 /* Merge with previous */ 869 cvmx_bootmem_phy_set_size(prev_addr, 870 cvmx_bootmem_phy_get_size(prev_addr) + size); 871 if (phy_addr + size == cur_addr) { 872 /* Also merge with current */ 873 cvmx_bootmem_phy_set_size(prev_addr, 874 cvmx_bootmem_phy_get_size(cur_addr) + 875 cvmx_bootmem_phy_get_size(prev_addr)); 876 cvmx_bootmem_phy_set_next(prev_addr, 877 cvmx_bootmem_phy_get_next(cur_addr)); 878 } 879 retval = 1; 880 goto bootmem_free_done; 881 } else if (phy_addr + size == cur_addr) { 882 /* Merge with current */ 883 cvmx_bootmem_phy_set_size(phy_addr, 884 cvmx_bootmem_phy_get_size(cur_addr) + size); 885 cvmx_bootmem_phy_set_next(phy_addr, 886 cvmx_bootmem_phy_get_next(cur_addr)); 887 cvmx_bootmem_phy_set_next(prev_addr, phy_addr); 888 retval = 1; 889 goto bootmem_free_done; 890 } 891 892 /* It is a standalone block, add in between prev and cur */ 893 cvmx_bootmem_phy_set_size(phy_addr, size); 894 cvmx_bootmem_phy_set_next(phy_addr, cur_addr); 895 cvmx_bootmem_phy_set_next(prev_addr, phy_addr); 896 } 897 retval = 1; 898 899bootmem_free_done: 900 __cvmx_bootmem_unlock(flags); 901 return retval; 902} 903 904void cvmx_bootmem_phy_list_print(void) 905{ 906 u64 addr; 907 908 addr = CVMX_BOOTMEM_DESC_GET_FIELD(head_addr); 909 printf("\n\n\nPrinting bootmem block list, descriptor: 0x%llx, head is 0x%llx\n", 910 CAST_ULL(cvmx_bootmem_desc_addr), CAST_ULL(addr)); 911 printf("Descriptor version: %d.%d\n", 912 (int)CVMX_BOOTMEM_DESC_GET_FIELD(major_version), 913 (int)CVMX_BOOTMEM_DESC_GET_FIELD(minor_version)); 914 if (CVMX_BOOTMEM_DESC_GET_FIELD(major_version) > 3) 915 debug("Warning: Bootmem descriptor version is newer than expected\n"); 916 917 if (!addr) 918 printf("mem list is empty!\n"); 919 920 while (addr) { 921 printf("Block address: 0x%08llx, size: 0x%08llx, next: 0x%08llx\n", CAST_ULL(addr), 922 CAST_ULL(cvmx_bootmem_phy_get_size(addr)), 923 CAST_ULL(cvmx_bootmem_phy_get_next(addr))); 924 addr = cvmx_bootmem_phy_get_next(addr); 925 } 926 printf("\n\n"); 927} 928 929u64 cvmx_bootmem_phy_available_mem(u64 min_block_size) 930{ 931 u64 addr; 932 933 u64 available_mem = 0; 934 935 __cvmx_bootmem_lock(0); 936 addr = CVMX_BOOTMEM_DESC_GET_FIELD(head_addr); 937 while (addr) { 938 if (cvmx_bootmem_phy_get_size(addr) >= min_block_size) 939 available_mem += cvmx_bootmem_phy_get_size(addr); 940 addr = cvmx_bootmem_phy_get_next(addr); 941 } 942 __cvmx_bootmem_unlock(0); 943 return available_mem; 944} 945 946u64 cvmx_bootmem_phy_named_block_find(const char *name, u32 flags) 947{ 948 u64 result = 0; 949 950 debug("%s: %s\n", __func__, name); 951 952 __cvmx_bootmem_lock(flags); 953 if (!__cvmx_bootmem_check_version(3)) { 954 int i; 955 u64 named_block_array_addr = 956 CVMX_BOOTMEM_DESC_GET_FIELD(named_block_array_addr); 957 int num_blocks = 958 CVMX_BOOTMEM_DESC_GET_FIELD(named_block_num_blocks); 959 int name_length = 960 CVMX_BOOTMEM_DESC_GET_FIELD(named_block_name_len); 961 u64 named_addr = named_block_array_addr; 962 963 for (i = 0; i < num_blocks; i++) { 964 u64 named_size = 965 CVMX_BOOTMEM_NAMED_GET_FIELD(named_addr, size); 966 if (name && named_size) { 967 char name_tmp[name_length + 1]; 968 969 CVMX_BOOTMEM_NAMED_GET_NAME(named_addr, 970 name_tmp, 971 name_length); 972 if (!strncmp(name, name_tmp, name_length)) { 973 result = named_addr; 974 break; 975 } 976 } else if (!name && !named_size) { 977 result = named_addr; 978 break; 979 } 980 981 named_addr += 982 sizeof(struct cvmx_bootmem_named_block_desc); 983 } 984 } 985 __cvmx_bootmem_unlock(flags); 986 return result; 987} 988 989int cvmx_bootmem_phy_named_block_free(const char *name, u32 flags) 990{ 991 u64 named_block_addr; 992 993 if (__cvmx_bootmem_check_version(3)) 994 return 0; 995 996 debug("%s: %s\n", __func__, name); 997 998 /* 999 * Take lock here, as name lookup/block free/name free need to be 1000 * atomic 1001 */ 1002 __cvmx_bootmem_lock(flags); 1003 1004 named_block_addr = cvmx_bootmem_phy_named_block_find(name, 1005 CVMX_BOOTMEM_FLAG_NO_LOCKING); 1006 if (named_block_addr) { 1007 u64 named_addr = 1008 CVMX_BOOTMEM_NAMED_GET_FIELD(named_block_addr, 1009 base_addr); 1010 u64 named_size = 1011 CVMX_BOOTMEM_NAMED_GET_FIELD(named_block_addr, size); 1012 1013 debug("%s: %s, base: 0x%llx, size: 0x%llx\n", 1014 __func__, name, CAST_ULL(named_addr), 1015 CAST_ULL(named_size)); 1016 1017 __cvmx_bootmem_phy_free(named_addr, named_size, 1018 CVMX_BOOTMEM_FLAG_NO_LOCKING); 1019 1020 /* Set size to zero to indicate block not used. */ 1021 CVMX_BOOTMEM_NAMED_SET_FIELD(named_block_addr, size, 0); 1022 } 1023 1024 __cvmx_bootmem_unlock(flags); 1025 return !!named_block_addr; /* 0 on failure, 1 on success */ 1026} 1027 1028s64 cvmx_bootmem_phy_named_block_alloc(u64 size, u64 min_addr, 1029 u64 max_addr, 1030 u64 alignment, const char *name, 1031 u32 flags) 1032{ 1033 s64 addr_allocated; 1034 u64 named_block_desc_addr; 1035 1036 debug("%s: size: 0x%llx, min: 0x%llx, max: 0x%llx, align: 0x%llx, name: %s\n", 1037 __func__, CAST_ULL(size), CAST_ULL(min_addr), CAST_ULL(max_addr), 1038 CAST_ULL(alignment), name); 1039 1040 if (__cvmx_bootmem_check_version(3)) 1041 return -1; 1042 1043 /* 1044 * Take lock here, as name lookup/block alloc/name add need to be 1045 * atomic 1046 */ 1047 __cvmx_bootmem_lock(flags); 1048 1049 named_block_desc_addr = 1050 cvmx_bootmem_phy_named_block_find(name, flags | 1051 CVMX_BOOTMEM_FLAG_NO_LOCKING); 1052 if (named_block_desc_addr) { 1053 __cvmx_bootmem_unlock(flags); 1054 return -1; 1055 } 1056 1057 /* Get pointer to first available named block descriptor */ 1058 named_block_desc_addr = 1059 cvmx_bootmem_phy_named_block_find(NULL, flags | 1060 CVMX_BOOTMEM_FLAG_NO_LOCKING); 1061 if (!named_block_desc_addr) { 1062 __cvmx_bootmem_unlock(flags); 1063 return -1; 1064 } 1065 1066 /* 1067 * Round size up to mult of minimum alignment bytes 1068 * We need the actual size allocated to allow for blocks to be 1069 * coallesced when they are freed. The alloc routine does the 1070 * same rounding up on all allocations. 1071 */ 1072 size = (size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) & 1073 ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1); 1074 1075 addr_allocated = cvmx_bootmem_phy_alloc(size, min_addr, max_addr, 1076 alignment, 1077 flags | CVMX_BOOTMEM_FLAG_NO_LOCKING); 1078 if (addr_allocated >= 0) { 1079 CVMX_BOOTMEM_NAMED_SET_FIELD(named_block_desc_addr, base_addr, 1080 addr_allocated); 1081 CVMX_BOOTMEM_NAMED_SET_FIELD(named_block_desc_addr, size, size); 1082 CVMX_BOOTMEM_NAMED_SET_NAME(named_block_desc_addr, name, 1083 CVMX_BOOTMEM_DESC_GET_FIELD(named_block_name_len)); 1084 } 1085 1086 __cvmx_bootmem_unlock(flags); 1087 return addr_allocated; 1088} 1089 1090void cvmx_bootmem_phy_named_block_print(void) 1091{ 1092 int i; 1093 int printed = 0; 1094 1095 u64 named_block_array_addr = 1096 CVMX_BOOTMEM_DESC_GET_FIELD(named_block_array_addr); 1097 int num_blocks = CVMX_BOOTMEM_DESC_GET_FIELD(named_block_num_blocks); 1098 int name_length = CVMX_BOOTMEM_DESC_GET_FIELD(named_block_name_len); 1099 u64 named_block_addr = named_block_array_addr; 1100 1101 debug("%s: desc addr: 0x%llx\n", 1102 __func__, CAST_ULL(cvmx_bootmem_desc_addr)); 1103 1104 if (__cvmx_bootmem_check_version(3)) 1105 return; 1106 1107 printf("List of currently allocated named bootmem blocks:\n"); 1108 for (i = 0; i < num_blocks; i++) { 1109 u64 named_size = 1110 CVMX_BOOTMEM_NAMED_GET_FIELD(named_block_addr, size); 1111 if (named_size) { 1112 char name_tmp[name_length + 1]; 1113 u64 named_addr = 1114 CVMX_BOOTMEM_NAMED_GET_FIELD(named_block_addr, 1115 base_addr); 1116 CVMX_BOOTMEM_NAMED_GET_NAME(named_block_addr, name_tmp, 1117 name_length); 1118 printed++; 1119 printf("Name: %s, address: 0x%08llx, size: 0x%08llx, index: %d\n", name_tmp, 1120 CAST_ULL(named_addr), 1121 CAST_ULL(named_size), i); 1122 } 1123 named_block_addr += 1124 sizeof(struct cvmx_bootmem_named_block_desc); 1125 } 1126 1127 if (!printed) 1128 printf("No named bootmem blocks exist.\n"); 1129} 1130 1131s64 cvmx_bootmem_phy_mem_list_init(u64 mem_size, 1132 u32 low_reserved_bytes, 1133 struct cvmx_bootmem_desc *desc_buffer) 1134{ 1135 u64 cur_block_addr; 1136 s64 addr; 1137 int i; 1138 1139 debug("%s (arg desc ptr: %p, cvmx_bootmem_desc: 0x%llx)\n", 1140 __func__, desc_buffer, CAST_ULL(cvmx_bootmem_desc_addr)); 1141 1142 /* 1143 * Descriptor buffer needs to be in 32 bit addressable space to be 1144 * compatible with 32 bit applications 1145 */ 1146 if (!desc_buffer) { 1147 debug("ERROR: no memory for cvmx_bootmem descriptor provided\n"); 1148 return 0; 1149 } 1150 1151 if (mem_size > OCTEON_MAX_PHY_MEM_SIZE) { 1152 mem_size = OCTEON_MAX_PHY_MEM_SIZE; 1153 debug("ERROR: requested memory size too large, truncating to maximum size\n"); 1154 } 1155 1156 if (cvmx_bootmem_desc_addr) 1157 return 1; 1158 1159 /* Initialize cvmx pointer to descriptor */ 1160 cvmx_bootmem_init(cvmx_ptr_to_phys(desc_buffer)); 1161 1162 /* Fill the bootmem descriptor */ 1163 CVMX_BOOTMEM_DESC_SET_FIELD(lock, 0); 1164 CVMX_BOOTMEM_DESC_SET_FIELD(flags, 0); 1165 CVMX_BOOTMEM_DESC_SET_FIELD(head_addr, 0); 1166 CVMX_BOOTMEM_DESC_SET_FIELD(major_version, CVMX_BOOTMEM_DESC_MAJ_VER); 1167 CVMX_BOOTMEM_DESC_SET_FIELD(minor_version, CVMX_BOOTMEM_DESC_MIN_VER); 1168 CVMX_BOOTMEM_DESC_SET_FIELD(app_data_addr, 0); 1169 CVMX_BOOTMEM_DESC_SET_FIELD(app_data_size, 0); 1170 1171 /* 1172 * Set up global pointer to start of list, exclude low 64k for exception 1173 * vectors, space for global descriptor 1174 */ 1175 cur_block_addr = (OCTEON_DDR0_BASE + low_reserved_bytes); 1176 1177 if (mem_size <= OCTEON_DDR0_SIZE) { 1178 __cvmx_bootmem_phy_free(cur_block_addr, 1179 mem_size - low_reserved_bytes, 0); 1180 goto frees_done; 1181 } 1182 1183 __cvmx_bootmem_phy_free(cur_block_addr, 1184 OCTEON_DDR0_SIZE - low_reserved_bytes, 0); 1185 1186 mem_size -= OCTEON_DDR0_SIZE; 1187 1188 /* Add DDR2 block next if present */ 1189 if (mem_size > OCTEON_DDR1_SIZE) { 1190 __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, OCTEON_DDR1_SIZE, 0); 1191 __cvmx_bootmem_phy_free(OCTEON_DDR2_BASE, 1192 mem_size - OCTEON_DDR2_BASE, 0); 1193 } else { 1194 __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, mem_size, 0); 1195 } 1196frees_done: 1197 1198 /* Initialize the named block structure */ 1199 CVMX_BOOTMEM_DESC_SET_FIELD(named_block_name_len, CVMX_BOOTMEM_NAME_LEN); 1200 CVMX_BOOTMEM_DESC_SET_FIELD(named_block_num_blocks, 1201 CVMX_BOOTMEM_NUM_NAMED_BLOCKS); 1202 CVMX_BOOTMEM_DESC_SET_FIELD(named_block_array_addr, 0); 1203 1204 /* Allocate this near the top of the low 256 MBytes of memory */ 1205 addr = cvmx_bootmem_phy_alloc(CVMX_BOOTMEM_NUM_NAMED_BLOCKS * 1206 sizeof(struct cvmx_bootmem_named_block_desc), 1207 0, 0x10000000, 0, 1208 CVMX_BOOTMEM_FLAG_END_ALLOC); 1209 if (addr >= 0) 1210 CVMX_BOOTMEM_DESC_SET_FIELD(named_block_array_addr, addr); 1211 1212 debug("%s: named_block_array_addr: 0x%llx)\n", 1213 __func__, CAST_ULL(addr)); 1214 1215 if (addr < 0) { 1216 debug("FATAL ERROR: unable to allocate memory for bootmem descriptor!\n"); 1217 return 0; 1218 } 1219 1220 for (i = 0; i < CVMX_BOOTMEM_NUM_NAMED_BLOCKS; i++) { 1221 CVMX_BOOTMEM_NAMED_SET_FIELD(addr, base_addr, 0); 1222 CVMX_BOOTMEM_NAMED_SET_FIELD(addr, size, 0); 1223 addr += sizeof(struct cvmx_bootmem_named_block_desc); 1224 } 1225 1226 return 1; 1227} 1228 1229s64 cvmx_bootmem_phy_mem_list_init_multi(u8 node_mask, 1230 u32 mem_sizes[], 1231 u32 low_reserved_bytes, 1232 struct cvmx_bootmem_desc *desc_buffer) 1233{ 1234 u64 cur_block_addr; 1235 u64 mem_size; 1236 s64 addr; 1237 int i; 1238 int node; 1239 u64 node_base; /* Make u64 to reduce type casting */ 1240 1241 mem_sizes[0] = gd->ram_size / (1024 * 1024); 1242 1243 debug("cvmx_bootmem_phy_mem_list_init (arg desc ptr: %p, cvmx_bootmem_desc: 0x%llx)\n", 1244 desc_buffer, CAST_ULL(cvmx_bootmem_desc_addr)); 1245 1246 /* 1247 * Descriptor buffer needs to be in 32 bit addressable space to be 1248 * compatible with 32 bit applications 1249 */ 1250 if (!desc_buffer) { 1251 debug("ERROR: no memory for cvmx_bootmem descriptor provided\n"); 1252 return 0; 1253 } 1254 1255 cvmx_coremask_for_each_node(node, node_mask) { 1256 if ((mem_sizes[node] * 1024 * 1024) > OCTEON_MAX_PHY_MEM_SIZE) { 1257 mem_sizes[node] = OCTEON_MAX_PHY_MEM_SIZE / 1258 (1024 * 1024); 1259 debug("ERROR node#%lld: requested memory size too large, truncating to maximum size\n", 1260 CAST_ULL(node)); 1261 } 1262 } 1263 1264 if (cvmx_bootmem_desc_addr) 1265 return 1; 1266 1267 /* Initialize cvmx pointer to descriptor */ 1268 cvmx_bootmem_init(cvmx_ptr_to_phys(desc_buffer)); 1269 1270 /* Fill the bootmem descriptor */ 1271 CVMX_BOOTMEM_DESC_SET_FIELD(lock, 0); 1272 CVMX_BOOTMEM_DESC_SET_FIELD(flags, 0); 1273 CVMX_BOOTMEM_DESC_SET_FIELD(head_addr, 0); 1274 CVMX_BOOTMEM_DESC_SET_FIELD(major_version, CVMX_BOOTMEM_DESC_MAJ_VER); 1275 CVMX_BOOTMEM_DESC_SET_FIELD(minor_version, CVMX_BOOTMEM_DESC_MIN_VER); 1276 CVMX_BOOTMEM_DESC_SET_FIELD(app_data_addr, 0); 1277 CVMX_BOOTMEM_DESC_SET_FIELD(app_data_size, 0); 1278 1279 cvmx_coremask_for_each_node(node, node_mask) { 1280 if (node != 0) /* do not reserve memory on remote nodes */ 1281 low_reserved_bytes = 0; 1282 1283 mem_size = (u64)mem_sizes[node] * (1024 * 1024); /* MBytes */ 1284 1285 /* 1286 * Set up global pointer to start of list, exclude low 64k 1287 * for exception vectors, space for global descriptor 1288 */ 1289 1290 node_base = (u64)node << CVMX_NODE_MEM_SHIFT; 1291 cur_block_addr = (OCTEON_DDR0_BASE + low_reserved_bytes) | 1292 node_base; 1293 1294 if (mem_size <= OCTEON_DDR0_SIZE) { 1295 __cvmx_bootmem_phy_free(cur_block_addr, 1296 mem_size - low_reserved_bytes, 1297 0); 1298 continue; 1299 } 1300 1301 __cvmx_bootmem_phy_free(cur_block_addr, 1302 OCTEON_DDR0_SIZE - low_reserved_bytes, 1303 0); 1304 1305 mem_size -= OCTEON_DDR0_SIZE; 1306 1307 /* Add DDR2 block next if present */ 1308 if (mem_size > OCTEON_DDR1_SIZE) { 1309 __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE | 1310 node_base, 1311 OCTEON_DDR1_SIZE, 0); 1312 __cvmx_bootmem_phy_free(OCTEON_DDR2_BASE | 1313 node_base, 1314 mem_size - OCTEON_DDR1_SIZE, 0); 1315 } else { 1316 __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE | 1317 node_base, 1318 mem_size, 0); 1319 } 1320 } 1321 1322 debug("%s: Initialize the named block\n", __func__); 1323 1324 /* Initialize the named block structure */ 1325 CVMX_BOOTMEM_DESC_SET_FIELD(named_block_name_len, CVMX_BOOTMEM_NAME_LEN); 1326 CVMX_BOOTMEM_DESC_SET_FIELD(named_block_num_blocks, 1327 CVMX_BOOTMEM_NUM_NAMED_BLOCKS); 1328 CVMX_BOOTMEM_DESC_SET_FIELD(named_block_array_addr, 0); 1329 1330 /* Allocate this near the top of the low 256 MBytes of memory */ 1331 addr = cvmx_bootmem_phy_alloc(CVMX_BOOTMEM_NUM_NAMED_BLOCKS * 1332 sizeof(struct cvmx_bootmem_named_block_desc), 1333 0, 0x10000000, 0, 1334 CVMX_BOOTMEM_FLAG_END_ALLOC); 1335 if (addr >= 0) 1336 CVMX_BOOTMEM_DESC_SET_FIELD(named_block_array_addr, addr); 1337 1338 debug("cvmx_bootmem_phy_mem_list_init: named_block_array_addr: 0x%llx)\n", 1339 CAST_ULL(addr)); 1340 1341 if (addr < 0) { 1342 debug("FATAL ERROR: unable to allocate memory for bootmem descriptor!\n"); 1343 return 0; 1344 } 1345 1346 for (i = 0; i < CVMX_BOOTMEM_NUM_NAMED_BLOCKS; i++) { 1347 CVMX_BOOTMEM_NAMED_SET_FIELD(addr, base_addr, 0); 1348 CVMX_BOOTMEM_NAMED_SET_FIELD(addr, size, 0); 1349 addr += sizeof(struct cvmx_bootmem_named_block_desc); 1350 } 1351 1352 cvmx_bootmem_phy_list_print(); 1353 1354 return 1; 1355} 1356 1357int cvmx_bootmem_reserve_memory(u64 start_addr, u64 size, 1358 const char *name, u32 flags) 1359{ 1360 u64 addr; 1361 int rc = 1; 1362 static unsigned int block_num; 1363 char block_name[CVMX_BOOTMEM_NAME_LEN]; 1364 1365 debug("%s: start %#llx, size: %#llx, name: %s, flags:%#x)\n", 1366 __func__, CAST_ULL(start_addr), CAST_ULL(size), name, flags); 1367 1368 if (__cvmx_bootmem_check_version(3)) 1369 return 0; 1370 1371 addr = CVMX_BOOTMEM_DESC_GET_FIELD(head_addr); 1372 if (!addr) 1373 return 0; 1374 1375 if (!name) 1376 name = "__cvmx_bootmem_reserved"; 1377 1378 while (addr && rc) { 1379 u64 block_size = cvmx_bootmem_phy_get_size(addr); 1380 u64 reserve_size = 0; 1381 1382 if (addr >= start_addr && addr < start_addr + size) { 1383 reserve_size = size - (addr - start_addr); 1384 if (block_size < reserve_size) 1385 reserve_size = block_size; 1386 } else if (start_addr > addr && 1387 start_addr < (addr + block_size)) { 1388 reserve_size = block_size - (start_addr - addr); 1389 } 1390 1391 if (reserve_size) { 1392 snprintf(block_name, sizeof(block_name), 1393 "%.32s_%012llx_%u", 1394 name, (unsigned long long)start_addr, 1395 (unsigned int)block_num); 1396 1397 debug("%s: Reserving 0x%llx bytes at address 0x%llx with name %s\n", 1398 __func__, CAST_ULL(reserve_size), 1399 CAST_ULL(addr), block_name); 1400 1401 if (cvmx_bootmem_phy_named_block_alloc(reserve_size, 1402 addr, 0, 0, 1403 block_name, 1404 flags) == -1) { 1405 debug("%s: Failed to reserve 0x%llx bytes at address 0x%llx\n", 1406 __func__, CAST_ULL(reserve_size), 1407 (unsigned long long)addr); 1408 rc = 0; 1409 break; 1410 } 1411 1412 debug("%s: Reserved 0x%llx bytes at address 0x%llx with name %s\n", 1413 __func__, CAST_ULL(reserve_size), 1414 CAST_ULL(addr), block_name); 1415 } 1416 1417 addr = cvmx_bootmem_phy_get_next(addr); 1418 block_num++; 1419 } 1420 1421 return rc; 1422} 1423 1424void cvmx_bootmem_lock(void) 1425{ 1426 __cvmx_bootmem_lock(0); 1427} 1428 1429void cvmx_bootmem_unlock(void) 1430{ 1431 __cvmx_bootmem_unlock(0); 1432} 1433 1434void *__cvmx_phys_addr_to_ptr(u64 phys, int size) 1435{ 1436 void *tmp; 1437 1438 if (sizeof(void *) == 8) { 1439 tmp = CASTPTR(void, CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, phys)); 1440 } else { 1441 u32 phy32 = (u32)(phys & 0x7fffffffULL); 1442 1443 tmp = CASTPTR(void, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 1444 phy32)); 1445 } 1446 1447 return tmp; 1448} 1449 1450void *__cvmx_bootmem_internal_get_desc_ptr(void) 1451{ 1452 return cvmx_phys_to_ptr(cvmx_bootmem_desc_addr); 1453} 1454