Back to home page

OSCL-LXR

 
 

    


0001 /***********************license start***************
0002  * Author: Cavium Networks
0003  *
0004  * Contact: support@caviumnetworks.com
0005  * This file is part of the OCTEON SDK
0006  *
0007  * Copyright (c) 2003-2008 Cavium Networks
0008  *
0009  * This file is free software; you can redistribute it and/or modify
0010  * it under the terms of the GNU General Public License, Version 2, as
0011  * published by the Free Software Foundation.
0012  *
0013  * This file is distributed in the hope that it will be useful, but
0014  * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
0015  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
0016  * NONINFRINGEMENT.  See the GNU General Public License for more
0017  * details.
0018  *
0019  * You should have received a copy of the GNU General Public License
0020  * along with this file; if not, write to the Free Software
0021  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
0022  * or visit http://www.gnu.org/licenses/.
0023  *
0024  * This file may also be available under a different license from Cavium.
0025  * Contact Cavium Networks for more information
0026  ***********************license end**************************************/
0027 
0028 /*
0029  * Simple allocate only memory allocator.  Used to allocate memory at
0030  * application start time.
0031  */
0032 
0033 #include <linux/export.h>
0034 #include <linux/kernel.h>
0035 
0036 #include <asm/octeon/cvmx.h>
0037 #include <asm/octeon/cvmx-spinlock.h>
0038 #include <asm/octeon/cvmx-bootmem.h>
0039 
0040 /*#define DEBUG */
0041 
0042 
0043 static struct cvmx_bootmem_desc *cvmx_bootmem_desc;
0044 
0045 /* See header file for descriptions of functions */
0046 
0047 /*
0048  * This macro returns a member of the
0049  * cvmx_bootmem_named_block_desc_t structure. These members can't
0050  * be directly addressed as they might be in memory not directly
0051  * reachable. In the case where bootmem is compiled with
0052  * LINUX_HOST, the structure itself might be located on a remote
0053  * Octeon. The argument "field" is the member name of the
0054  * cvmx_bootmem_named_block_desc_t to read. Regardless of the type
0055  * of the field, the return type is always a uint64_t. The "addr"
0056  * parameter is the physical address of the structure.
0057  */
0058 #define CVMX_BOOTMEM_NAMED_GET_FIELD(addr, field)           \
0059     __cvmx_bootmem_desc_get(addr,                   \
0060         offsetof(struct cvmx_bootmem_named_block_desc, field),  \
0061         sizeof_field(struct cvmx_bootmem_named_block_desc, field))
0062 
0063 /*
0064  * This function is the implementation of the get macros defined
0065  * for individual structure members. The argument are generated
0066  * by the macros inorder to read only the needed memory.
0067  *
0068  * @param base   64bit physical address of the complete structure
0069  * @param offset Offset from the beginning of the structure to the member being
0070  *               accessed.
0071  * @param size   Size of the structure member.
0072  *
0073  * @return Value of the structure member promoted into a uint64_t.
0074  */
0075 static inline uint64_t __cvmx_bootmem_desc_get(uint64_t base, int offset,
0076                            int size)
0077 {
0078     base = (1ull << 63) | (base + offset);
0079     switch (size) {
0080     case 4:
0081         return cvmx_read64_uint32(base);
0082     case 8:
0083         return cvmx_read64_uint64(base);
0084     default:
0085         return 0;
0086     }
0087 }
0088 
0089 /*
0090  * Wrapper functions are provided for reading/writing the size and
0091  * next block values as these may not be directly addressible (in 32
0092  * bit applications, for instance.)  Offsets of data elements in
0093  * bootmem list, must match cvmx_bootmem_block_header_t.
0094  */
0095 #define NEXT_OFFSET 0
0096 #define SIZE_OFFSET 8
0097 
0098 static void cvmx_bootmem_phy_set_size(uint64_t addr, uint64_t size)
0099 {
0100     cvmx_write64_uint64((addr + SIZE_OFFSET) | (1ull << 63), size);
0101 }
0102 
0103 static void cvmx_bootmem_phy_set_next(uint64_t addr, uint64_t next)
0104 {
0105     cvmx_write64_uint64((addr + NEXT_OFFSET) | (1ull << 63), next);
0106 }
0107 
0108 static uint64_t cvmx_bootmem_phy_get_size(uint64_t addr)
0109 {
0110     return cvmx_read64_uint64((addr + SIZE_OFFSET) | (1ull << 63));
0111 }
0112 
0113 static uint64_t cvmx_bootmem_phy_get_next(uint64_t addr)
0114 {
0115     return cvmx_read64_uint64((addr + NEXT_OFFSET) | (1ull << 63));
0116 }
0117 
0118 /*
0119  * Allocate a block of memory from the free list that was
0120  * passed to the application by the bootloader within a specified
0121  * address range. This is an allocate-only algorithm, so
0122  * freeing memory is not possible. Allocation will fail if
0123  * memory cannot be allocated in the requested range.
0124  *
0125  * @size:      Size in bytes of block to allocate
0126  * @min_addr:  defines the minimum address of the range
0127  * @max_addr:  defines the maximum address of the range
0128  * @alignment: Alignment required - must be power of 2
0129  * Returns pointer to block of memory, NULL on error
0130  */
0131 static void *cvmx_bootmem_alloc_range(uint64_t size, uint64_t alignment,
0132                       uint64_t min_addr, uint64_t max_addr)
0133 {
0134     int64_t address;
0135     address =
0136         cvmx_bootmem_phy_alloc(size, min_addr, max_addr, alignment, 0);
0137 
0138     if (address > 0)
0139         return cvmx_phys_to_ptr(address);
0140     else
0141         return NULL;
0142 }
0143 
0144 void *cvmx_bootmem_alloc_address(uint64_t size, uint64_t address,
0145                  uint64_t alignment)
0146 {
0147     return cvmx_bootmem_alloc_range(size, alignment, address,
0148                     address + size);
0149 }
0150 
0151 void *cvmx_bootmem_alloc_named_range(uint64_t size, uint64_t min_addr,
0152                      uint64_t max_addr, uint64_t align,
0153                      char *name)
0154 {
0155     int64_t addr;
0156 
0157     addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr,
0158                           align, name, 0);
0159     if (addr >= 0)
0160         return cvmx_phys_to_ptr(addr);
0161     else
0162         return NULL;
0163 }
0164 
0165 void *cvmx_bootmem_alloc_named(uint64_t size, uint64_t alignment, char *name)
0166 {
0167     return cvmx_bootmem_alloc_named_range(size, 0, 0, alignment, name);
0168 }
0169 EXPORT_SYMBOL(cvmx_bootmem_alloc_named);
0170 
0171 void cvmx_bootmem_lock(void)
0172 {
0173     cvmx_spinlock_lock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock));
0174 }
0175 
0176 void cvmx_bootmem_unlock(void)
0177 {
0178     cvmx_spinlock_unlock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock));
0179 }
0180 
0181 int cvmx_bootmem_init(void *mem_desc_ptr)
0182 {
0183     /* Here we set the global pointer to the bootmem descriptor
0184      * block.  This pointer will be used directly, so we will set
0185      * it up to be directly usable by the application.  It is set
0186      * up as follows for the various runtime/ABI combinations:
0187      *
0188      * Linux 64 bit: Set XKPHYS bit
0189      * Linux 32 bit: use mmap to create mapping, use virtual address
0190      * CVMX 64 bit:  use physical address directly
0191      * CVMX 32 bit:  use physical address directly
0192      *
0193      * Note that the CVMX environment assumes the use of 1-1 TLB
0194      * mappings so that the physical addresses can be used
0195      * directly
0196      */
0197     if (!cvmx_bootmem_desc) {
0198 #if   defined(CVMX_ABI_64)
0199         /* Set XKPHYS bit */
0200         cvmx_bootmem_desc = cvmx_phys_to_ptr(CAST64(mem_desc_ptr));
0201 #else
0202         cvmx_bootmem_desc = (struct cvmx_bootmem_desc *) mem_desc_ptr;
0203 #endif
0204     }
0205 
0206     return 0;
0207 }
0208 
0209 /*
0210  * The cvmx_bootmem_phy* functions below return 64 bit physical
0211  * addresses, and expose more features that the cvmx_bootmem_functions
0212  * above.  These are required for full memory space access in 32 bit
0213  * applications, as well as for using some advance features.  Most
0214  * applications should not need to use these.
0215  */
0216 
0217 int64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min,
0218                    uint64_t address_max, uint64_t alignment,
0219                    uint32_t flags)
0220 {
0221 
0222     uint64_t head_addr;
0223     uint64_t ent_addr;
0224     /* points to previous list entry, NULL current entry is head of list */
0225     uint64_t prev_addr = 0;
0226     uint64_t new_ent_addr = 0;
0227     uint64_t desired_min_addr;
0228 
0229 #ifdef DEBUG
0230     cvmx_dprintf("cvmx_bootmem_phy_alloc: req_size: 0x%llx, "
0231              "min_addr: 0x%llx, max_addr: 0x%llx, align: 0x%llx\n",
0232              (unsigned long long)req_size,
0233              (unsigned long long)address_min,
0234              (unsigned long long)address_max,
0235              (unsigned long long)alignment);
0236 #endif
0237 
0238     if (cvmx_bootmem_desc->major_version > 3) {
0239         cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
0240                  "version: %d.%d at addr: %p\n",
0241                  (int)cvmx_bootmem_desc->major_version,
0242                  (int)cvmx_bootmem_desc->minor_version,
0243                  cvmx_bootmem_desc);
0244         goto error_out;
0245     }
0246 
0247     /*
0248      * Do a variety of checks to validate the arguments.  The
0249      * allocator code will later assume that these checks have
0250      * been made.  We validate that the requested constraints are
0251      * not self-contradictory before we look through the list of
0252      * available memory.
0253      */
0254 
0255     /* 0 is not a valid req_size for this allocator */
0256     if (!req_size)
0257         goto error_out;
0258 
0259     /* Round req_size up to mult of minimum alignment bytes */
0260     req_size = (req_size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) &
0261         ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1);
0262 
0263     /*
0264      * Convert !0 address_min and 0 address_max to special case of
0265      * range that specifies an exact memory block to allocate.  Do
0266      * this before other checks and adjustments so that this
0267      * tranformation will be validated.
0268      */
0269     if (address_min && !address_max)
0270         address_max = address_min + req_size;
0271     else if (!address_min && !address_max)
0272         address_max = ~0ull;  /* If no limits given, use max limits */
0273 
0274 
0275     /*
0276      * Enforce minimum alignment (this also keeps the minimum free block
0277      * req_size the same as the alignment req_size.
0278      */
0279     if (alignment < CVMX_BOOTMEM_ALIGNMENT_SIZE)
0280         alignment = CVMX_BOOTMEM_ALIGNMENT_SIZE;
0281 
0282     /*
0283      * Adjust address minimum based on requested alignment (round
0284      * up to meet alignment).  Do this here so we can reject
0285      * impossible requests up front. (NOP for address_min == 0)
0286      */
0287     if (alignment)
0288         address_min = ALIGN(address_min, alignment);
0289 
0290     /*
0291      * Reject inconsistent args.  We have adjusted these, so this
0292      * may fail due to our internal changes even if this check
0293      * would pass for the values the user supplied.
0294      */
0295     if (req_size > address_max - address_min)
0296         goto error_out;
0297 
0298     /* Walk through the list entries - first fit found is returned */
0299 
0300     if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0301         cvmx_bootmem_lock();
0302     head_addr = cvmx_bootmem_desc->head_addr;
0303     ent_addr = head_addr;
0304     for (; ent_addr;
0305          prev_addr = ent_addr,
0306          ent_addr = cvmx_bootmem_phy_get_next(ent_addr)) {
0307         uint64_t usable_base, usable_max;
0308         uint64_t ent_size = cvmx_bootmem_phy_get_size(ent_addr);
0309 
0310         if (cvmx_bootmem_phy_get_next(ent_addr)
0311             && ent_addr > cvmx_bootmem_phy_get_next(ent_addr)) {
0312             cvmx_dprintf("Internal bootmem_alloc() error: ent: "
0313                 "0x%llx, next: 0x%llx\n",
0314                 (unsigned long long)ent_addr,
0315                 (unsigned long long)
0316                 cvmx_bootmem_phy_get_next(ent_addr));
0317             goto error_out;
0318         }
0319 
0320         /*
0321          * Determine if this is an entry that can satisfy the
0322          * request Check to make sure entry is large enough to
0323          * satisfy request.
0324          */
0325         usable_base =
0326             ALIGN(max(address_min, ent_addr), alignment);
0327         usable_max = min(address_max, ent_addr + ent_size);
0328         /*
0329          * We should be able to allocate block at address
0330          * usable_base.
0331          */
0332 
0333         desired_min_addr = usable_base;
0334         /*
0335          * Determine if request can be satisfied from the
0336          * current entry.
0337          */
0338         if (!((ent_addr + ent_size) > usable_base
0339                 && ent_addr < address_max
0340                 && req_size <= usable_max - usable_base))
0341             continue;
0342         /*
0343          * We have found an entry that has room to satisfy the
0344          * request, so allocate it from this entry.  If end
0345          * CVMX_BOOTMEM_FLAG_END_ALLOC set, then allocate from
0346          * the end of this block rather than the beginning.
0347          */
0348         if (flags & CVMX_BOOTMEM_FLAG_END_ALLOC) {
0349             desired_min_addr = usable_max - req_size;
0350             /*
0351              * Align desired address down to required
0352              * alignment.
0353              */
0354             desired_min_addr &= ~(alignment - 1);
0355         }
0356 
0357         /* Match at start of entry */
0358         if (desired_min_addr == ent_addr) {
0359             if (req_size < ent_size) {
0360                 /*
0361                  * big enough to create a new block
0362                  * from top portion of block.
0363                  */
0364                 new_ent_addr = ent_addr + req_size;
0365                 cvmx_bootmem_phy_set_next(new_ent_addr,
0366                     cvmx_bootmem_phy_get_next(ent_addr));
0367                 cvmx_bootmem_phy_set_size(new_ent_addr,
0368                             ent_size -
0369                             req_size);
0370 
0371                 /*
0372                  * Adjust next pointer as following
0373                  * code uses this.
0374                  */
0375                 cvmx_bootmem_phy_set_next(ent_addr,
0376                             new_ent_addr);
0377             }
0378 
0379             /*
0380              * adjust prev ptr or head to remove this
0381              * entry from list.
0382              */
0383             if (prev_addr)
0384                 cvmx_bootmem_phy_set_next(prev_addr,
0385                     cvmx_bootmem_phy_get_next(ent_addr));
0386             else
0387                 /*
0388                  * head of list being returned, so
0389                  * update head ptr.
0390                  */
0391                 cvmx_bootmem_desc->head_addr =
0392                     cvmx_bootmem_phy_get_next(ent_addr);
0393 
0394             if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0395                 cvmx_bootmem_unlock();
0396             return desired_min_addr;
0397         }
0398         /*
0399          * block returned doesn't start at beginning of entry,
0400          * so we know that we will be splitting a block off
0401          * the front of this one.  Create a new block from the
0402          * beginning, add to list, and go to top of loop
0403          * again.
0404          *
0405          * create new block from high portion of
0406          * block, so that top block starts at desired
0407          * addr.
0408          */
0409         new_ent_addr = desired_min_addr;
0410         cvmx_bootmem_phy_set_next(new_ent_addr,
0411                     cvmx_bootmem_phy_get_next
0412                     (ent_addr));
0413         cvmx_bootmem_phy_set_size(new_ent_addr,
0414                     cvmx_bootmem_phy_get_size
0415                     (ent_addr) -
0416                     (desired_min_addr -
0417                         ent_addr));
0418         cvmx_bootmem_phy_set_size(ent_addr,
0419                     desired_min_addr - ent_addr);
0420         cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr);
0421         /* Loop again to handle actual alloc from new block */
0422     }
0423 error_out:
0424     /* We didn't find anything, so return error */
0425     if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0426         cvmx_bootmem_unlock();
0427     return -1;
0428 }
0429 
0430 int __cvmx_bootmem_phy_free(uint64_t phy_addr, uint64_t size, uint32_t flags)
0431 {
0432     uint64_t cur_addr;
0433     uint64_t prev_addr = 0; /* zero is invalid */
0434     int retval = 0;
0435 
0436 #ifdef DEBUG
0437     cvmx_dprintf("__cvmx_bootmem_phy_free addr: 0x%llx, size: 0x%llx\n",
0438              (unsigned long long)phy_addr, (unsigned long long)size);
0439 #endif
0440     if (cvmx_bootmem_desc->major_version > 3) {
0441         cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
0442                  "version: %d.%d at addr: %p\n",
0443                  (int)cvmx_bootmem_desc->major_version,
0444                  (int)cvmx_bootmem_desc->minor_version,
0445                  cvmx_bootmem_desc);
0446         return 0;
0447     }
0448 
0449     /* 0 is not a valid size for this allocator */
0450     if (!size)
0451         return 0;
0452 
0453     if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0454         cvmx_bootmem_lock();
0455     cur_addr = cvmx_bootmem_desc->head_addr;
0456     if (cur_addr == 0 || phy_addr < cur_addr) {
0457         /* add at front of list - special case with changing head ptr */
0458         if (cur_addr && phy_addr + size > cur_addr)
0459             goto bootmem_free_done; /* error, overlapping section */
0460         else if (phy_addr + size == cur_addr) {
0461             /* Add to front of existing first block */
0462             cvmx_bootmem_phy_set_next(phy_addr,
0463                           cvmx_bootmem_phy_get_next
0464                           (cur_addr));
0465             cvmx_bootmem_phy_set_size(phy_addr,
0466                           cvmx_bootmem_phy_get_size
0467                           (cur_addr) + size);
0468             cvmx_bootmem_desc->head_addr = phy_addr;
0469 
0470         } else {
0471             /* New block before first block.  OK if cur_addr is 0 */
0472             cvmx_bootmem_phy_set_next(phy_addr, cur_addr);
0473             cvmx_bootmem_phy_set_size(phy_addr, size);
0474             cvmx_bootmem_desc->head_addr = phy_addr;
0475         }
0476         retval = 1;
0477         goto bootmem_free_done;
0478     }
0479 
0480     /* Find place in list to add block */
0481     while (cur_addr && phy_addr > cur_addr) {
0482         prev_addr = cur_addr;
0483         cur_addr = cvmx_bootmem_phy_get_next(cur_addr);
0484     }
0485 
0486     if (!cur_addr) {
0487         /*
0488          * We have reached the end of the list, add on to end,
0489          * checking to see if we need to combine with last
0490          * block
0491          */
0492         if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) ==
0493             phy_addr) {
0494             cvmx_bootmem_phy_set_size(prev_addr,
0495                           cvmx_bootmem_phy_get_size
0496                           (prev_addr) + size);
0497         } else {
0498             cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
0499             cvmx_bootmem_phy_set_size(phy_addr, size);
0500             cvmx_bootmem_phy_set_next(phy_addr, 0);
0501         }
0502         retval = 1;
0503         goto bootmem_free_done;
0504     } else {
0505         /*
0506          * insert between prev and cur nodes, checking for
0507          * merge with either/both.
0508          */
0509         if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) ==
0510             phy_addr) {
0511             /* Merge with previous */
0512             cvmx_bootmem_phy_set_size(prev_addr,
0513                           cvmx_bootmem_phy_get_size
0514                           (prev_addr) + size);
0515             if (phy_addr + size == cur_addr) {
0516                 /* Also merge with current */
0517                 cvmx_bootmem_phy_set_size(prev_addr,
0518                     cvmx_bootmem_phy_get_size(cur_addr) +
0519                     cvmx_bootmem_phy_get_size(prev_addr));
0520                 cvmx_bootmem_phy_set_next(prev_addr,
0521                     cvmx_bootmem_phy_get_next(cur_addr));
0522             }
0523             retval = 1;
0524             goto bootmem_free_done;
0525         } else if (phy_addr + size == cur_addr) {
0526             /* Merge with current */
0527             cvmx_bootmem_phy_set_size(phy_addr,
0528                           cvmx_bootmem_phy_get_size
0529                           (cur_addr) + size);
0530             cvmx_bootmem_phy_set_next(phy_addr,
0531                           cvmx_bootmem_phy_get_next
0532                           (cur_addr));
0533             cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
0534             retval = 1;
0535             goto bootmem_free_done;
0536         }
0537 
0538         /* It is a standalone block, add in between prev and cur */
0539         cvmx_bootmem_phy_set_size(phy_addr, size);
0540         cvmx_bootmem_phy_set_next(phy_addr, cur_addr);
0541         cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
0542 
0543     }
0544     retval = 1;
0545 
0546 bootmem_free_done:
0547     if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0548         cvmx_bootmem_unlock();
0549     return retval;
0550 
0551 }
0552 
0553 /*
0554  * Finds a named memory block by name.
0555  * Also used for finding an unused entry in the named block table.
0556  *
0557  * @name: Name of memory block to find.  If NULL pointer given, then
0558  *    finds unused descriptor, if available.
0559  *
0560  * @flags: Flags to control options for the allocation.
0561  *
0562  * Returns Pointer to memory block descriptor, NULL if not found.
0563  *     If NULL returned when name parameter is NULL, then no memory
0564  *     block descriptors are available.
0565  */
0566 static struct cvmx_bootmem_named_block_desc *
0567     cvmx_bootmem_phy_named_block_find(char *name, uint32_t flags)
0568 {
0569     unsigned int i;
0570     struct cvmx_bootmem_named_block_desc *named_block_array_ptr;
0571 
0572 #ifdef DEBUG
0573     cvmx_dprintf("cvmx_bootmem_phy_named_block_find: %s\n", name);
0574 #endif
0575     /*
0576      * Lock the structure to make sure that it is not being
0577      * changed while we are examining it.
0578      */
0579     if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0580         cvmx_bootmem_lock();
0581 
0582     /* Use XKPHYS for 64 bit linux */
0583     named_block_array_ptr = (struct cvmx_bootmem_named_block_desc *)
0584         cvmx_phys_to_ptr(cvmx_bootmem_desc->named_block_array_addr);
0585 
0586 #ifdef DEBUG
0587     cvmx_dprintf
0588         ("cvmx_bootmem_phy_named_block_find: named_block_array_ptr: %p\n",
0589          named_block_array_ptr);
0590 #endif
0591     if (cvmx_bootmem_desc->major_version == 3) {
0592         for (i = 0;
0593              i < cvmx_bootmem_desc->named_block_num_blocks; i++) {
0594             if ((name && named_block_array_ptr[i].size
0595                  && !strncmp(name, named_block_array_ptr[i].name,
0596                      cvmx_bootmem_desc->named_block_name_len
0597                      - 1))
0598                 || (!name && !named_block_array_ptr[i].size)) {
0599                 if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0600                     cvmx_bootmem_unlock();
0601 
0602                 return &(named_block_array_ptr[i]);
0603             }
0604         }
0605     } else {
0606         cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
0607                  "version: %d.%d at addr: %p\n",
0608                  (int)cvmx_bootmem_desc->major_version,
0609                  (int)cvmx_bootmem_desc->minor_version,
0610                  cvmx_bootmem_desc);
0611     }
0612     if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0613         cvmx_bootmem_unlock();
0614 
0615     return NULL;
0616 }
0617 
0618 void *cvmx_bootmem_alloc_named_range_once(uint64_t size, uint64_t min_addr,
0619                       uint64_t max_addr, uint64_t align,
0620                       char *name,
0621                       void (*init) (void *))
0622 {
0623     int64_t addr;
0624     void *ptr;
0625     uint64_t named_block_desc_addr;
0626 
0627     named_block_desc_addr = (uint64_t)
0628         cvmx_bootmem_phy_named_block_find(name,
0629                           (uint32_t)CVMX_BOOTMEM_FLAG_NO_LOCKING);
0630 
0631     if (named_block_desc_addr) {
0632         addr = CVMX_BOOTMEM_NAMED_GET_FIELD(named_block_desc_addr,
0633                             base_addr);
0634         return cvmx_phys_to_ptr(addr);
0635     }
0636 
0637     addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr,
0638                           align, name,
0639                           (uint32_t)CVMX_BOOTMEM_FLAG_NO_LOCKING);
0640 
0641     if (addr < 0)
0642         return NULL;
0643     ptr = cvmx_phys_to_ptr(addr);
0644 
0645     if (init)
0646         init(ptr);
0647     else
0648         memset(ptr, 0, size);
0649 
0650     return ptr;
0651 }
0652 EXPORT_SYMBOL(cvmx_bootmem_alloc_named_range_once);
0653 
0654 struct cvmx_bootmem_named_block_desc *cvmx_bootmem_find_named_block(char *name)
0655 {
0656     return cvmx_bootmem_phy_named_block_find(name, 0);
0657 }
0658 EXPORT_SYMBOL(cvmx_bootmem_find_named_block);
0659 
0660 /*
0661  * Frees a named block.
0662  *
0663  * @name:   name of block to free
0664  * @flags:  flags for passing options
0665  *
0666  * Returns 0 on failure
0667  *     1 on success
0668  */
0669 static int cvmx_bootmem_phy_named_block_free(char *name, uint32_t flags)
0670 {
0671     struct cvmx_bootmem_named_block_desc *named_block_ptr;
0672 
0673     if (cvmx_bootmem_desc->major_version != 3) {
0674         cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: "
0675                  "%d.%d at addr: %p\n",
0676                  (int)cvmx_bootmem_desc->major_version,
0677                  (int)cvmx_bootmem_desc->minor_version,
0678                  cvmx_bootmem_desc);
0679         return 0;
0680     }
0681 #ifdef DEBUG
0682     cvmx_dprintf("cvmx_bootmem_phy_named_block_free: %s\n", name);
0683 #endif
0684 
0685     /*
0686      * Take lock here, as name lookup/block free/name free need to
0687      * be atomic.
0688      */
0689     cvmx_bootmem_lock();
0690 
0691     named_block_ptr =
0692         cvmx_bootmem_phy_named_block_find(name,
0693                           CVMX_BOOTMEM_FLAG_NO_LOCKING);
0694     if (named_block_ptr) {
0695 #ifdef DEBUG
0696         cvmx_dprintf("cvmx_bootmem_phy_named_block_free: "
0697                  "%s, base: 0x%llx, size: 0x%llx\n",
0698                  name,
0699                  (unsigned long long)named_block_ptr->base_addr,
0700                  (unsigned long long)named_block_ptr->size);
0701 #endif
0702         __cvmx_bootmem_phy_free(named_block_ptr->base_addr,
0703                     named_block_ptr->size,
0704                     CVMX_BOOTMEM_FLAG_NO_LOCKING);
0705         named_block_ptr->size = 0;
0706         /* Set size to zero to indicate block not used. */
0707     }
0708 
0709     cvmx_bootmem_unlock();
0710     return named_block_ptr != NULL; /* 0 on failure, 1 on success */
0711 }
0712 
0713 int cvmx_bootmem_free_named(char *name)
0714 {
0715     return cvmx_bootmem_phy_named_block_free(name, 0);
0716 }
0717 
0718 int64_t cvmx_bootmem_phy_named_block_alloc(uint64_t size, uint64_t min_addr,
0719                        uint64_t max_addr,
0720                        uint64_t alignment,
0721                        char *name,
0722                        uint32_t flags)
0723 {
0724     int64_t addr_allocated;
0725     struct cvmx_bootmem_named_block_desc *named_block_desc_ptr;
0726 
0727 #ifdef DEBUG
0728     cvmx_dprintf("cvmx_bootmem_phy_named_block_alloc: size: 0x%llx, min: "
0729              "0x%llx, max: 0x%llx, align: 0x%llx, name: %s\n",
0730              (unsigned long long)size,
0731              (unsigned long long)min_addr,
0732              (unsigned long long)max_addr,
0733              (unsigned long long)alignment,
0734              name);
0735 #endif
0736     if (cvmx_bootmem_desc->major_version != 3) {
0737         cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: "
0738                  "%d.%d at addr: %p\n",
0739                  (int)cvmx_bootmem_desc->major_version,
0740                  (int)cvmx_bootmem_desc->minor_version,
0741                  cvmx_bootmem_desc);
0742         return -1;
0743     }
0744 
0745     /*
0746      * Take lock here, as name lookup/block alloc/name add need to
0747      * be atomic.
0748      */
0749     if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0750         cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
0751 
0752     /* Get pointer to first available named block descriptor */
0753     named_block_desc_ptr =
0754         cvmx_bootmem_phy_named_block_find(NULL,
0755                           flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
0756 
0757     /*
0758      * Check to see if name already in use, return error if name
0759      * not available or no more room for blocks.
0760      */
0761     if (cvmx_bootmem_phy_named_block_find(name,
0762                           flags | CVMX_BOOTMEM_FLAG_NO_LOCKING) || !named_block_desc_ptr) {
0763         if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0764             cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
0765         return -1;
0766     }
0767 
0768 
0769     /*
0770      * Round size up to mult of minimum alignment bytes We need
0771      * the actual size allocated to allow for blocks to be
0772      * coalesced when they are freed. The alloc routine does the
0773      * same rounding up on all allocations.
0774      */
0775     size = ALIGN(size, CVMX_BOOTMEM_ALIGNMENT_SIZE);
0776 
0777     addr_allocated = cvmx_bootmem_phy_alloc(size, min_addr, max_addr,
0778                         alignment,
0779                         flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
0780     if (addr_allocated >= 0) {
0781         named_block_desc_ptr->base_addr = addr_allocated;
0782         named_block_desc_ptr->size = size;
0783         strncpy(named_block_desc_ptr->name, name,
0784             cvmx_bootmem_desc->named_block_name_len);
0785         named_block_desc_ptr->name[cvmx_bootmem_desc->named_block_name_len - 1] = 0;
0786     }
0787 
0788     if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
0789         cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
0790     return addr_allocated;
0791 }
0792 
0793 struct cvmx_bootmem_desc *cvmx_bootmem_get_desc(void)
0794 {
0795     return cvmx_bootmem_desc;
0796 }