cvmx-bootmem.c revision 210284
1210284Sjmallett/***********************license start***************
2210284Sjmallett *  Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
3210284Sjmallett *  reserved.
4210284Sjmallett *
5210284Sjmallett *
6210284Sjmallett *  Redistribution and use in source and binary forms, with or without
7210284Sjmallett *  modification, are permitted provided that the following conditions are
8210284Sjmallett *  met:
9210284Sjmallett *
10210284Sjmallett *      * Redistributions of source code must retain the above copyright
11210284Sjmallett *        notice, this list of conditions and the following disclaimer.
12210284Sjmallett *
13210284Sjmallett *      * Redistributions in binary form must reproduce the above
14210284Sjmallett *        copyright notice, this list of conditions and the following
15210284Sjmallett *        disclaimer in the documentation and/or other materials provided
16210284Sjmallett *        with the distribution.
17210284Sjmallett *
18210284Sjmallett *      * Neither the name of Cavium Networks nor the names of
19210284Sjmallett *        its contributors may be used to endorse or promote products
20210284Sjmallett *        derived from this software without specific prior written
21210284Sjmallett *        permission.
22210284Sjmallett *
23210284Sjmallett *  TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
24210284Sjmallett *  AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
25210284Sjmallett *  OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
26210284Sjmallett *  RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
27210284Sjmallett *  REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
28210284Sjmallett *  DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
29210284Sjmallett *  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
30210284Sjmallett *  PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
31210284Sjmallett *  POSSESSION OR CORRESPONDENCE TO DESCRIPTION.  THE ENTIRE RISK ARISING OUT
32210284Sjmallett *  OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
33210284Sjmallett *
34210284Sjmallett *
35210284Sjmallett *  For any questions regarding licensing please contact marketing@caviumnetworks.com
36210284Sjmallett *
37210284Sjmallett ***********************license end**************************************/
38210284Sjmallett
39210284Sjmallett
40210284Sjmallett
41210284Sjmallett
42210284Sjmallett
43210284Sjmallett/**
44210284Sjmallett * @file
45210284Sjmallett * Simple allocate only memory allocator.  Used to allocate memory at application
46210284Sjmallett * start time.
47210284Sjmallett *
48210284Sjmallett * <hr>$Revision: 41586 $<hr>
49210284Sjmallett *
50210284Sjmallett */
51210284Sjmallett
52210284Sjmallett#include "cvmx.h"
53210284Sjmallett#include "cvmx-spinlock.h"
54210284Sjmallett#include "cvmx-bootmem.h"
55210284Sjmallett
56210284Sjmallett
57210284Sjmallett//#define DEBUG
58210284Sjmallett
59210284Sjmallett
60210284Sjmallett#undef	MAX
61210284Sjmallett#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
62210284Sjmallett
63210284Sjmallett#undef	MIN
64210284Sjmallett#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
65210284Sjmallett
66210284Sjmallett#define ALIGN_ADDR_UP(addr, align)     (((addr) + (~(align))) & (align))
67210284Sjmallett
68210284Sjmallettstatic CVMX_SHARED cvmx_bootmem_desc_t *cvmx_bootmem_desc = NULL;
69210284Sjmallett
70210284Sjmallett/* See header file for descriptions of functions */
71210284Sjmallett
72210284Sjmallett/* Wrapper functions are provided for reading/writing the size and next block
73210284Sjmallett** values as these may not be directly addressible (in 32 bit applications, for instance.)
74210284Sjmallett*/
75210284Sjmallett/* Offsets of data elements in bootmem list, must match cvmx_bootmem_block_header_t */
76210284Sjmallett#define NEXT_OFFSET 0
77210284Sjmallett#define SIZE_OFFSET 8
78210284Sjmallettstatic void cvmx_bootmem_phy_set_size(uint64_t addr, uint64_t size)
79210284Sjmallett{
80210284Sjmallett    cvmx_write64_uint64((addr + SIZE_OFFSET) | (1ull << 63), size);
81210284Sjmallett}
82210284Sjmallettstatic void cvmx_bootmem_phy_set_next(uint64_t addr, uint64_t next)
83210284Sjmallett{
84210284Sjmallett    cvmx_write64_uint64((addr + NEXT_OFFSET) | (1ull << 63), next);
85210284Sjmallett}
86210284Sjmallettstatic uint64_t cvmx_bootmem_phy_get_size(uint64_t addr)
87210284Sjmallett{
88210284Sjmallett    return(cvmx_read64_uint64((addr + SIZE_OFFSET) | (1ull << 63)));
89210284Sjmallett}
90210284Sjmallettstatic uint64_t cvmx_bootmem_phy_get_next(uint64_t addr)
91210284Sjmallett{
92210284Sjmallett    return(cvmx_read64_uint64((addr + NEXT_OFFSET) | (1ull << 63)));
93210284Sjmallett}
94210284Sjmallett
95210284Sjmallett
96210284Sjmallett/* This functions takes an address range and adjusts it as necessary to
97210284Sjmallett** match the ABI that is currently being used.  This is required to ensure
98210284Sjmallett** that bootmem_alloc* functions only return valid pointers for 32 bit ABIs */
99210284Sjmallettstatic int __cvmx_validate_mem_range(uint64_t *min_addr_ptr, uint64_t *max_addr_ptr)
100210284Sjmallett{
101210284Sjmallett
102210284Sjmallett#if defined(__linux__) && defined(CVMX_ABI_N32)
103210284Sjmallett    {
104210284Sjmallett        extern uint64_t linux_mem32_min;
105210284Sjmallett        extern uint64_t linux_mem32_max;
106210284Sjmallett        /* For 32 bit Linux apps, we need to restrict the allocations to the range
107210284Sjmallett        ** of memory configured for access from userspace.  Also, we need to add mappings
108210284Sjmallett        ** for the data structures that we access.*/
109210284Sjmallett
110210284Sjmallett        /* Narrow range requests to be bounded by the 32 bit limits.  octeon_phy_mem_block_alloc()
111210284Sjmallett        ** will reject inconsistent req_size/range requests, so we don't repeat those checks here.
112210284Sjmallett        ** If max unspecified, set to 32 bit maximum. */
113210284Sjmallett        *min_addr_ptr = MIN(MAX(*min_addr_ptr, linux_mem32_min), linux_mem32_max);
114210284Sjmallett        if (!*max_addr_ptr)
115210284Sjmallett            *max_addr_ptr = linux_mem32_max;
116210284Sjmallett        else
117210284Sjmallett            *max_addr_ptr = MAX(MIN(*max_addr_ptr, linux_mem32_max), linux_mem32_min);
118210284Sjmallett    }
119210284Sjmallett#elif defined(CVMX_ABI_N32)
120210284Sjmallett    {
121210284Sjmallett        uint32_t max_phys = 0x0FFFFFFF;  /* Max physical address when 1-1 mappings not used */
122210284Sjmallett#if CVMX_USE_1_TO_1_TLB_MAPPINGS
123210284Sjmallett        max_phys = 0x7FFFFFFF;
124210284Sjmallett#endif
125210284Sjmallett        /* We are are running standalone simple executive, so we need to limit the range
126210284Sjmallett        ** that we allocate from */
127210284Sjmallett
128210284Sjmallett        /* Narrow range requests to be bounded by the 32 bit limits.  octeon_phy_mem_block_alloc()
129210284Sjmallett        ** will reject inconsistent req_size/range requests, so we don't repeat those checks here.
130210284Sjmallett        ** If max unspecified, set to 32 bit maximum. */
131210284Sjmallett        *min_addr_ptr = MIN(MAX(*min_addr_ptr, 0x0), max_phys);
132210284Sjmallett        if (!*max_addr_ptr)
133210284Sjmallett            *max_addr_ptr = max_phys;
134210284Sjmallett        else
135210284Sjmallett            *max_addr_ptr = MAX(MIN(*max_addr_ptr, max_phys), 0x0);
136210284Sjmallett    }
137210284Sjmallett#endif
138210284Sjmallett
139210284Sjmallett    return 0;
140210284Sjmallett}
141210284Sjmallett
142210284Sjmallett
143210284Sjmallettvoid *cvmx_bootmem_alloc_range(uint64_t size, uint64_t alignment, uint64_t min_addr, uint64_t max_addr)
144210284Sjmallett{
145210284Sjmallett    int64_t address;
146210284Sjmallett
147210284Sjmallett    __cvmx_validate_mem_range(&min_addr, &max_addr);
148210284Sjmallett    address = cvmx_bootmem_phy_alloc(size, min_addr, max_addr, alignment, 0);
149210284Sjmallett
150210284Sjmallett    if (address > 0)
151210284Sjmallett        return cvmx_phys_to_ptr(address);
152210284Sjmallett    else
153210284Sjmallett        return NULL;
154210284Sjmallett}
155210284Sjmallett
156210284Sjmallettvoid *cvmx_bootmem_alloc_address(uint64_t size, uint64_t address, uint64_t alignment)
157210284Sjmallett{
158210284Sjmallett    return cvmx_bootmem_alloc_range(size, alignment, address, address + size);
159210284Sjmallett}
160210284Sjmallett
161210284Sjmallett
162210284Sjmallettvoid *cvmx_bootmem_alloc(uint64_t size, uint64_t alignment)
163210284Sjmallett{
164210284Sjmallett    return cvmx_bootmem_alloc_range(size, alignment, 0, 0);
165210284Sjmallett}
166210284Sjmallett
167210284Sjmallettvoid *cvmx_bootmem_alloc_named_range(uint64_t size, uint64_t min_addr, uint64_t max_addr, uint64_t align, char *name)
168210284Sjmallett{
169210284Sjmallett    int64_t addr;
170210284Sjmallett
171210284Sjmallett    __cvmx_validate_mem_range(&min_addr, &max_addr);
172210284Sjmallett    addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr, align, name, 0);
173210284Sjmallett    if (addr >= 0)
174210284Sjmallett        return cvmx_phys_to_ptr(addr);
175210284Sjmallett    else
176210284Sjmallett        return NULL;
177210284Sjmallett
178210284Sjmallett}
179210284Sjmallettvoid *cvmx_bootmem_alloc_named_address(uint64_t size, uint64_t address, char *name)
180210284Sjmallett{
181210284Sjmallett    return(cvmx_bootmem_alloc_named_range(size, address, address + size, 0, name));
182210284Sjmallett}
183210284Sjmallettvoid *cvmx_bootmem_alloc_named(uint64_t size, uint64_t alignment, char *name)
184210284Sjmallett{
185210284Sjmallett    return(cvmx_bootmem_alloc_named_range(size, 0, 0, alignment, name));
186210284Sjmallett}
187210284Sjmallett
188210284Sjmallettint cvmx_bootmem_free_named(char *name)
189210284Sjmallett{
190210284Sjmallett    return(cvmx_bootmem_phy_named_block_free(name, 0));
191210284Sjmallett}
192210284Sjmallett
193210284Sjmallettcvmx_bootmem_named_block_desc_t * cvmx_bootmem_find_named_block(char *name)
194210284Sjmallett{
195210284Sjmallett    return(cvmx_bootmem_phy_named_block_find(name, 0));
196210284Sjmallett}
197210284Sjmallett
198210284Sjmallettvoid cvmx_bootmem_print_named(void)
199210284Sjmallett{
200210284Sjmallett    cvmx_bootmem_phy_named_block_print();
201210284Sjmallett}
202210284Sjmallett
203210284Sjmallett#if defined(__linux__) && defined(CVMX_ABI_N32)
204210284Sjmallettcvmx_bootmem_named_block_desc_t *linux32_named_block_array_ptr;
205210284Sjmallett#endif
206210284Sjmallett
207210284Sjmallettint cvmx_bootmem_init(void *mem_desc_ptr)
208210284Sjmallett{
209210284Sjmallett    /* Verify that the size of cvmx_spinlock_t meets our assumptions */
210210284Sjmallett    if (sizeof(cvmx_spinlock_t) != 4)
211210284Sjmallett    {
212210284Sjmallett        cvmx_dprintf("ERROR: Unexpected size of cvmx_spinlock_t\n");
213210284Sjmallett        return(-1);
214210284Sjmallett    }
215210284Sjmallett
216210284Sjmallett    /* Here we set the global pointer to the bootmem descriptor block.  This pointer will
217210284Sjmallett    ** be used directly, so we will set it up to be directly usable by the application.
218210284Sjmallett    ** It is set up as follows for the various runtime/ABI combinations:
219210284Sjmallett    ** Linux 64 bit: Set XKPHYS bit
220210284Sjmallett    ** Linux 32 bit: use mmap to create mapping, use virtual address
221210284Sjmallett    ** CVMX 64 bit:  use physical address directly
222210284Sjmallett    ** CVMX 32 bit:  use physical address directly
223210284Sjmallett    ** Note that the CVMX environment assumes the use of 1-1 TLB mappings so that the physical addresses
224210284Sjmallett    ** can be used directly
225210284Sjmallett    */
226210284Sjmallett    if (!cvmx_bootmem_desc)
227210284Sjmallett    {
228210284Sjmallett#if defined(CVMX_BUILD_FOR_LINUX_USER) && defined(CVMX_ABI_N32)
229210284Sjmallett        void *base_ptr;
230210284Sjmallett        /* For 32 bit, we need to use mmap to create a mapping for the bootmem descriptor */
231210284Sjmallett        int dm_fd = open("/dev/mem", O_RDWR);
232210284Sjmallett        if (dm_fd < 0)
233210284Sjmallett        {
234210284Sjmallett            cvmx_dprintf("ERROR opening /dev/mem for boot descriptor mapping\n");
235210284Sjmallett            return(-1);
236210284Sjmallett        }
237210284Sjmallett
238210284Sjmallett        base_ptr = mmap(NULL,
239210284Sjmallett                        sizeof(cvmx_bootmem_desc_t) + sysconf(_SC_PAGESIZE),
240210284Sjmallett                        PROT_READ | PROT_WRITE,
241210284Sjmallett                        MAP_SHARED,
242210284Sjmallett                        dm_fd,
243210284Sjmallett                        ((off_t)mem_desc_ptr) & ~(sysconf(_SC_PAGESIZE) - 1));
244210284Sjmallett
245210284Sjmallett        if (MAP_FAILED == base_ptr)
246210284Sjmallett        {
247210284Sjmallett            cvmx_dprintf("Error mapping bootmem descriptor!\n");
248210284Sjmallett            close(dm_fd);
249210284Sjmallett            return(-1);
250210284Sjmallett        }
251210284Sjmallett
252210284Sjmallett        /* Adjust pointer to point to bootmem_descriptor, rather than start of page it is in */
253210284Sjmallett        cvmx_bootmem_desc =  (cvmx_bootmem_desc_t*)((char*)base_ptr + (((off_t)mem_desc_ptr) & (sysconf(_SC_PAGESIZE) - 1)));
254210284Sjmallett
255210284Sjmallett        /* Also setup mapping for named memory block desc. while we are at it.  Here we must keep another
256210284Sjmallett        ** pointer around, as the value in the bootmem descriptor is shared with other applications. */
257210284Sjmallett        base_ptr = mmap(NULL,
258210284Sjmallett                        sizeof(cvmx_bootmem_named_block_desc_t) * cvmx_bootmem_desc->named_block_num_blocks + sysconf(_SC_PAGESIZE),
259210284Sjmallett                        PROT_READ | PROT_WRITE,
260210284Sjmallett                        MAP_SHARED,
261210284Sjmallett                        dm_fd,
262210284Sjmallett                        ((off_t)cvmx_bootmem_desc->named_block_array_addr) & ~(sysconf(_SC_PAGESIZE) - 1));
263210284Sjmallett
264210284Sjmallett        close(dm_fd);
265210284Sjmallett
266210284Sjmallett        if (MAP_FAILED == base_ptr)
267210284Sjmallett        {
268210284Sjmallett            cvmx_dprintf("Error mapping named block descriptor!\n");
269210284Sjmallett            return(-1);
270210284Sjmallett        }
271210284Sjmallett
272210284Sjmallett        /* Adjust pointer to point to named block array, rather than start of page it is in */
273210284Sjmallett        linux32_named_block_array_ptr = (cvmx_bootmem_named_block_desc_t*)((char*)base_ptr + (((off_t)cvmx_bootmem_desc->named_block_array_addr) & (sysconf(_SC_PAGESIZE) - 1)));
274210284Sjmallett
275210284Sjmallett#elif (defined(CVMX_BUILD_FOR_LINUX_KERNEL) || defined(CVMX_BUILD_FOR_LINUX_USER)) && defined(CVMX_ABI_64)
276210284Sjmallett        /* Set XKPHYS bit */
277210284Sjmallett        cvmx_bootmem_desc = cvmx_phys_to_ptr(CAST64(mem_desc_ptr));
278210284Sjmallett#else
279210284Sjmallett        cvmx_bootmem_desc = (cvmx_bootmem_desc_t*)mem_desc_ptr;
280210284Sjmallett#endif
281210284Sjmallett    }
282210284Sjmallett
283210284Sjmallett
284210284Sjmallett    return(0);
285210284Sjmallett}
286210284Sjmallett
287210284Sjmallett
288210284Sjmallettuint64_t cvmx_bootmem_available_mem(uint64_t min_block_size)
289210284Sjmallett{
290210284Sjmallett    return(cvmx_bootmem_phy_available_mem(min_block_size));
291210284Sjmallett}
292210284Sjmallett
293210284Sjmallett
294210284Sjmallett
295210284Sjmallett
296210284Sjmallett
297210284Sjmallett/*********************************************************************
298210284Sjmallett** The cvmx_bootmem_phy* functions below return 64 bit physical addresses,
299210284Sjmallett** and expose more features that the cvmx_bootmem_functions above.  These are
300210284Sjmallett** required for full memory space access in 32 bit applications, as well as for
301210284Sjmallett** using some advance features.
302210284Sjmallett** Most applications should not need to use these.
303210284Sjmallett**
304210284Sjmallett**/
305210284Sjmallett
306210284Sjmallett
307210284Sjmallettint64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min, uint64_t address_max, uint64_t alignment, uint32_t flags)
308210284Sjmallett{
309210284Sjmallett
310210284Sjmallett    uint64_t head_addr;
311210284Sjmallett    uint64_t ent_addr;
312210284Sjmallett    uint64_t prev_addr = 0;  /* points to previous list entry, NULL current entry is head of list */
313210284Sjmallett    uint64_t new_ent_addr = 0;
314210284Sjmallett    uint64_t desired_min_addr;
315210284Sjmallett    uint64_t alignment_mask = ~(alignment - 1);
316210284Sjmallett
317210284Sjmallett#ifdef DEBUG
318210284Sjmallett    cvmx_dprintf("cvmx_bootmem_phy_alloc: req_size: 0x%llx, min_addr: 0x%llx, max_addr: 0x%llx, align: 0x%llx\n",
319210284Sjmallett           (unsigned long long)req_size, (unsigned long long)address_min, (unsigned long long)address_max, (unsigned long long)alignment);
320210284Sjmallett#endif
321210284Sjmallett
322210284Sjmallett    if (cvmx_bootmem_desc->major_version > 3)
323210284Sjmallett    {
324210284Sjmallett        cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: %d.%d at addr: %p\n",
325210284Sjmallett               (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc);
326210284Sjmallett        goto error_out;
327210284Sjmallett    }
328210284Sjmallett
329210284Sjmallett    /* Do a variety of checks to validate the arguments.  The allocator code will later assume
330210284Sjmallett    ** that these checks have been made.  We validate that the requested constraints are not
331210284Sjmallett    ** self-contradictory before we look through the list of available memory
332210284Sjmallett    */
333210284Sjmallett
334210284Sjmallett    /* 0 is not a valid req_size for this allocator */
335210284Sjmallett    if (!req_size)
336210284Sjmallett        goto error_out;
337210284Sjmallett
338210284Sjmallett    /* Round req_size up to mult of minimum alignment bytes */
339210284Sjmallett    req_size = (req_size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) & ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1);
340210284Sjmallett
341210284Sjmallett    /* Convert !0 address_min and 0 address_max to special case of range that specifies an exact
342210284Sjmallett    ** memory block to allocate.  Do this before other checks and adjustments so that this tranformation will be validated */
343210284Sjmallett    if (address_min && !address_max)
344210284Sjmallett        address_max = address_min + req_size;
345210284Sjmallett    else if (!address_min && !address_max)
346210284Sjmallett        address_max = ~0ull;   /* If no limits given, use max limits */
347210284Sjmallett
348210284Sjmallett
349210284Sjmallett
350210284Sjmallett
351210284Sjmallett    /* Enforce minimum alignment (this also keeps the minimum free block
352210284Sjmallett    ** req_size the same as the alignment req_size */
353210284Sjmallett    if (alignment < CVMX_BOOTMEM_ALIGNMENT_SIZE)
354210284Sjmallett    {
355210284Sjmallett        alignment = CVMX_BOOTMEM_ALIGNMENT_SIZE;
356210284Sjmallett    }
357210284Sjmallett    alignment_mask = ~(alignment - 1);
358210284Sjmallett
359210284Sjmallett    /* Adjust address minimum based on requested alignment (round up to meet alignment).  Do this here so we can
360210284Sjmallett    ** reject impossible requests up front. (NOP for address_min == 0) */
361210284Sjmallett    if (alignment)
362210284Sjmallett        address_min = (address_min + (alignment - 1)) & ~(alignment - 1);
363210284Sjmallett
364210284Sjmallett
365210284Sjmallett    /* Reject inconsistent args.  We have adjusted these, so this may fail due to our internal changes
366210284Sjmallett    ** even if this check would pass for the values the user supplied. */
367210284Sjmallett    if (req_size > address_max - address_min)
368210284Sjmallett        goto error_out;
369210284Sjmallett
370210284Sjmallett    /* Walk through the list entries - first fit found is returned */
371210284Sjmallett
372210284Sjmallett    if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
373210284Sjmallett        cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
374210284Sjmallett    head_addr = cvmx_bootmem_desc->head_addr;
375210284Sjmallett    ent_addr = head_addr;
376210284Sjmallett    while (ent_addr)
377210284Sjmallett    {
378210284Sjmallett        uint64_t usable_base, usable_max;
379210284Sjmallett        uint64_t ent_size = cvmx_bootmem_phy_get_size(ent_addr);
380210284Sjmallett
381210284Sjmallett        if (cvmx_bootmem_phy_get_next(ent_addr) && ent_addr > cvmx_bootmem_phy_get_next(ent_addr))
382210284Sjmallett        {
383210284Sjmallett            cvmx_dprintf("Internal bootmem_alloc() error: ent: 0x%llx, next: 0x%llx\n",
384210284Sjmallett                   (unsigned long long)ent_addr, (unsigned long long)cvmx_bootmem_phy_get_next(ent_addr));
385210284Sjmallett            goto error_out;
386210284Sjmallett        }
387210284Sjmallett
388210284Sjmallett        /* Determine if this is an entry that can satisify the request */
389210284Sjmallett        /* Check to make sure entry is large enough to satisfy request */
390210284Sjmallett        usable_base = ALIGN_ADDR_UP(MAX(address_min, ent_addr), alignment_mask);
391210284Sjmallett        usable_max = MIN(address_max, ent_addr + ent_size);
392210284Sjmallett        /* We should be able to allocate block at address usable_base */
393210284Sjmallett
394210284Sjmallett        desired_min_addr = usable_base;
395210284Sjmallett
396210284Sjmallett        /* Determine if request can be satisfied from the current entry */
397210284Sjmallett        if ((((ent_addr + ent_size) > usable_base && ent_addr < address_max))
398210284Sjmallett            && req_size <= usable_max - usable_base)
399210284Sjmallett        {
400210284Sjmallett            /* We have found an entry that has room to satisfy the request, so allocate it from this entry */
401210284Sjmallett
402210284Sjmallett            /* If end CVMX_BOOTMEM_FLAG_END_ALLOC set, then allocate from the end of this block
403210284Sjmallett            ** rather than the beginning */
404210284Sjmallett            if (flags & CVMX_BOOTMEM_FLAG_END_ALLOC)
405210284Sjmallett            {
406210284Sjmallett                desired_min_addr = usable_max - req_size;
407210284Sjmallett                /* Align desired address down to required alignment */
408210284Sjmallett                desired_min_addr &= alignment_mask;
409210284Sjmallett            }
410210284Sjmallett
411210284Sjmallett            /* Match at start of entry */
412210284Sjmallett            if (desired_min_addr == ent_addr)
413210284Sjmallett            {
414210284Sjmallett                if (req_size < ent_size)
415210284Sjmallett                {
416210284Sjmallett                    /* big enough to create a new block from top portion of block */
417210284Sjmallett                    new_ent_addr = ent_addr + req_size;
418210284Sjmallett                    cvmx_bootmem_phy_set_next(new_ent_addr, cvmx_bootmem_phy_get_next(ent_addr));
419210284Sjmallett                    cvmx_bootmem_phy_set_size(new_ent_addr, ent_size - req_size);
420210284Sjmallett
421210284Sjmallett                    /* Adjust next pointer as following code uses this */
422210284Sjmallett                    cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr);
423210284Sjmallett                }
424210284Sjmallett
425210284Sjmallett                /* adjust prev ptr or head to remove this entry from list */
426210284Sjmallett                if (prev_addr)
427210284Sjmallett                {
428210284Sjmallett                    cvmx_bootmem_phy_set_next(prev_addr, cvmx_bootmem_phy_get_next(ent_addr));
429210284Sjmallett                }
430210284Sjmallett                else
431210284Sjmallett                {
432210284Sjmallett                    /* head of list being returned, so update head ptr */
433210284Sjmallett                    cvmx_bootmem_desc->head_addr = cvmx_bootmem_phy_get_next(ent_addr);
434210284Sjmallett                }
435210284Sjmallett                if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
436210284Sjmallett                    cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
437210284Sjmallett                return(desired_min_addr);
438210284Sjmallett            }
439210284Sjmallett
440210284Sjmallett
441210284Sjmallett            /* block returned doesn't start at beginning of entry, so we know
442210284Sjmallett            ** that we will be splitting a block off the front of this one.  Create a new block
443210284Sjmallett            ** from the beginning, add to list, and go to top of loop again.
444210284Sjmallett            **
445210284Sjmallett            ** create new block from high portion of block, so that top block
446210284Sjmallett            ** starts at desired addr
447210284Sjmallett            **/
448210284Sjmallett            new_ent_addr = desired_min_addr;
449210284Sjmallett            cvmx_bootmem_phy_set_next(new_ent_addr, cvmx_bootmem_phy_get_next(ent_addr));
450210284Sjmallett            cvmx_bootmem_phy_set_size(new_ent_addr, cvmx_bootmem_phy_get_size(ent_addr) - (desired_min_addr - ent_addr));
451210284Sjmallett            cvmx_bootmem_phy_set_size(ent_addr, desired_min_addr - ent_addr);
452210284Sjmallett            cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr);
453210284Sjmallett            /* Loop again to handle actual alloc from new block */
454210284Sjmallett        }
455210284Sjmallett
456210284Sjmallett        prev_addr = ent_addr;
457210284Sjmallett        ent_addr = cvmx_bootmem_phy_get_next(ent_addr);
458210284Sjmallett    }
459210284Sjmalletterror_out:
460210284Sjmallett    /* We didn't find anything, so return error */
461210284Sjmallett    if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
462210284Sjmallett        cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
463210284Sjmallett    return(-1);
464210284Sjmallett}
465210284Sjmallett
466210284Sjmallett
467210284Sjmallett
468210284Sjmallettint __cvmx_bootmem_phy_free(uint64_t phy_addr, uint64_t size, uint32_t flags)
469210284Sjmallett{
470210284Sjmallett    uint64_t cur_addr;
471210284Sjmallett    uint64_t prev_addr = 0;  /* zero is invalid */
472210284Sjmallett    int retval = 0;
473210284Sjmallett
474210284Sjmallett#ifdef DEBUG
475210284Sjmallett    cvmx_dprintf("__cvmx_bootmem_phy_free addr: 0x%llx, size: 0x%llx\n", (unsigned long long)phy_addr, (unsigned long long)size);
476210284Sjmallett#endif
477210284Sjmallett    if (cvmx_bootmem_desc->major_version > 3)
478210284Sjmallett    {
479210284Sjmallett        cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: %d.%d at addr: %p\n",
480210284Sjmallett               (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc);
481210284Sjmallett        return(0);
482210284Sjmallett    }
483210284Sjmallett
484210284Sjmallett    /* 0 is not a valid size for this allocator */
485210284Sjmallett    if (!size)
486210284Sjmallett        return(0);
487210284Sjmallett
488210284Sjmallett
489210284Sjmallett    if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
490210284Sjmallett        cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
491210284Sjmallett    cur_addr = cvmx_bootmem_desc->head_addr;
492210284Sjmallett    if (cur_addr == 0 || phy_addr < cur_addr)
493210284Sjmallett    {
494210284Sjmallett        /* add at front of list - special case with changing head ptr */
495210284Sjmallett        if (cur_addr && phy_addr + size > cur_addr)
496210284Sjmallett            goto bootmem_free_done; /* error, overlapping section */
497210284Sjmallett        else if (phy_addr + size == cur_addr)
498210284Sjmallett        {
499210284Sjmallett            /* Add to front of existing first block */
500210284Sjmallett            cvmx_bootmem_phy_set_next(phy_addr, cvmx_bootmem_phy_get_next(cur_addr));
501210284Sjmallett            cvmx_bootmem_phy_set_size(phy_addr, cvmx_bootmem_phy_get_size(cur_addr) + size);
502210284Sjmallett            cvmx_bootmem_desc->head_addr = phy_addr;
503210284Sjmallett
504210284Sjmallett        }
505210284Sjmallett        else
506210284Sjmallett        {
507210284Sjmallett            /* New block before first block */
508210284Sjmallett            cvmx_bootmem_phy_set_next(phy_addr, cur_addr);  /* OK if cur_addr is 0 */
509210284Sjmallett            cvmx_bootmem_phy_set_size(phy_addr, size);
510210284Sjmallett            cvmx_bootmem_desc->head_addr = phy_addr;
511210284Sjmallett        }
512210284Sjmallett        retval = 1;
513210284Sjmallett        goto bootmem_free_done;
514210284Sjmallett    }
515210284Sjmallett
516210284Sjmallett    /* Find place in list to add block */
517210284Sjmallett    while (cur_addr && phy_addr > cur_addr)
518210284Sjmallett    {
519210284Sjmallett        prev_addr = cur_addr;
520210284Sjmallett        cur_addr = cvmx_bootmem_phy_get_next(cur_addr);
521210284Sjmallett    }
522210284Sjmallett
523210284Sjmallett    if (!cur_addr)
524210284Sjmallett    {
525210284Sjmallett        /* We have reached the end of the list, add on to end, checking
526210284Sjmallett        ** to see if we need to combine with last block
527210284Sjmallett        **/
528210284Sjmallett        if (prev_addr +  cvmx_bootmem_phy_get_size(prev_addr) == phy_addr)
529210284Sjmallett        {
530210284Sjmallett            cvmx_bootmem_phy_set_size(prev_addr, cvmx_bootmem_phy_get_size(prev_addr) + size);
531210284Sjmallett        }
532210284Sjmallett        else
533210284Sjmallett        {
534210284Sjmallett            cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
535210284Sjmallett            cvmx_bootmem_phy_set_size(phy_addr, size);
536210284Sjmallett            cvmx_bootmem_phy_set_next(phy_addr, 0);
537210284Sjmallett        }
538210284Sjmallett        retval = 1;
539210284Sjmallett        goto bootmem_free_done;
540210284Sjmallett    }
541210284Sjmallett    else
542210284Sjmallett    {
543210284Sjmallett        /* insert between prev and cur nodes, checking for merge with either/both */
544210284Sjmallett
545210284Sjmallett        if (prev_addr +  cvmx_bootmem_phy_get_size(prev_addr) == phy_addr)
546210284Sjmallett        {
547210284Sjmallett            /* Merge with previous */
548210284Sjmallett            cvmx_bootmem_phy_set_size(prev_addr, cvmx_bootmem_phy_get_size(prev_addr) + size);
549210284Sjmallett            if (phy_addr + size == cur_addr)
550210284Sjmallett            {
551210284Sjmallett                /* Also merge with current */
552210284Sjmallett                cvmx_bootmem_phy_set_size(prev_addr, cvmx_bootmem_phy_get_size(cur_addr) + cvmx_bootmem_phy_get_size(prev_addr));
553210284Sjmallett                cvmx_bootmem_phy_set_next(prev_addr, cvmx_bootmem_phy_get_next(cur_addr));
554210284Sjmallett            }
555210284Sjmallett            retval = 1;
556210284Sjmallett            goto bootmem_free_done;
557210284Sjmallett        }
558210284Sjmallett        else if (phy_addr + size == cur_addr)
559210284Sjmallett        {
560210284Sjmallett            /* Merge with current */
561210284Sjmallett            cvmx_bootmem_phy_set_size(phy_addr, cvmx_bootmem_phy_get_size(cur_addr) + size);
562210284Sjmallett            cvmx_bootmem_phy_set_next(phy_addr, cvmx_bootmem_phy_get_next(cur_addr));
563210284Sjmallett            cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
564210284Sjmallett            retval = 1;
565210284Sjmallett            goto bootmem_free_done;
566210284Sjmallett        }
567210284Sjmallett
568210284Sjmallett        /* It is a standalone block, add in between prev and cur */
569210284Sjmallett        cvmx_bootmem_phy_set_size(phy_addr, size);
570210284Sjmallett        cvmx_bootmem_phy_set_next(phy_addr, cur_addr);
571210284Sjmallett        cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
572210284Sjmallett
573210284Sjmallett
574210284Sjmallett    }
575210284Sjmallett    retval = 1;
576210284Sjmallett
577210284Sjmallettbootmem_free_done:
578210284Sjmallett    if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
579210284Sjmallett        cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
580210284Sjmallett    return(retval);
581210284Sjmallett
582210284Sjmallett}
583210284Sjmallett
584210284Sjmallett
585210284Sjmallett
586210284Sjmallettvoid cvmx_bootmem_phy_list_print(void)
587210284Sjmallett{
588210284Sjmallett    uint64_t addr;
589210284Sjmallett
590210284Sjmallett    addr = cvmx_bootmem_desc->head_addr;
591210284Sjmallett    cvmx_dprintf("\n\n\nPrinting bootmem block list, descriptor: %p,  head is 0x%llx\n",
592210284Sjmallett           cvmx_bootmem_desc, (unsigned long long)addr);
593210284Sjmallett    cvmx_dprintf("Descriptor version: %d.%d\n", (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version);
594210284Sjmallett    if (cvmx_bootmem_desc->major_version > 3)
595210284Sjmallett    {
596210284Sjmallett        cvmx_dprintf("Warning: Bootmem descriptor version is newer than expected\n");
597210284Sjmallett    }
598210284Sjmallett    if (!addr)
599210284Sjmallett    {
600210284Sjmallett        cvmx_dprintf("mem list is empty!\n");
601210284Sjmallett    }
602210284Sjmallett    while (addr)
603210284Sjmallett    {
604210284Sjmallett        cvmx_dprintf("Block address: 0x%08qx, size: 0x%08qx, next: 0x%08qx\n",
605210284Sjmallett               (unsigned long long)addr,
606210284Sjmallett               (unsigned long long)cvmx_bootmem_phy_get_size(addr),
607210284Sjmallett               (unsigned long long)cvmx_bootmem_phy_get_next(addr));
608210284Sjmallett        addr = cvmx_bootmem_phy_get_next(addr);
609210284Sjmallett    }
610210284Sjmallett    cvmx_dprintf("\n\n");
611210284Sjmallett
612210284Sjmallett}
613210284Sjmallett
614210284Sjmallett
615210284Sjmallettuint64_t cvmx_bootmem_phy_available_mem(uint64_t min_block_size)
616210284Sjmallett{
617210284Sjmallett    uint64_t addr;
618210284Sjmallett
619210284Sjmallett    uint64_t available_mem = 0;
620210284Sjmallett
621210284Sjmallett    cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
622210284Sjmallett    addr = cvmx_bootmem_desc->head_addr;
623210284Sjmallett    while (addr)
624210284Sjmallett    {
625210284Sjmallett        if (cvmx_bootmem_phy_get_size(addr) >= min_block_size)
626210284Sjmallett            available_mem += cvmx_bootmem_phy_get_size(addr);
627210284Sjmallett        addr = cvmx_bootmem_phy_get_next(addr);
628210284Sjmallett    }
629210284Sjmallett    cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
630210284Sjmallett    return(available_mem);
631210284Sjmallett
632210284Sjmallett}
633210284Sjmallett
634210284Sjmallett
635210284Sjmallett
636210284Sjmallettcvmx_bootmem_named_block_desc_t * cvmx_bootmem_phy_named_block_find(char *name, uint32_t flags)
637210284Sjmallett{
638210284Sjmallett    unsigned int i;
639210284Sjmallett    cvmx_bootmem_named_block_desc_t *named_block_array_ptr;
640210284Sjmallett
641210284Sjmallett
642210284Sjmallett#ifdef DEBUG
643210284Sjmallett    cvmx_dprintf("cvmx_bootmem_phy_named_block_find: %s\n", name);
644210284Sjmallett#endif
645210284Sjmallett    /* Lock the structure to make sure that it is not being changed while we are
646210284Sjmallett    ** examining it.
647210284Sjmallett    */
648210284Sjmallett    if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
649210284Sjmallett        cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
650210284Sjmallett
651210284Sjmallett#if defined(__linux__) && !defined(CONFIG_OCTEON_U_BOOT)
652210284Sjmallett#ifdef CVMX_ABI_N32
653210284Sjmallett    /* Need to use mmapped named block pointer in 32 bit linux apps */
654210284Sjmallettextern cvmx_bootmem_named_block_desc_t *linux32_named_block_array_ptr;
655210284Sjmallett    named_block_array_ptr = linux32_named_block_array_ptr;
656210284Sjmallett#else
657210284Sjmallett    /* Use XKPHYS for 64 bit linux */
658210284Sjmallett    named_block_array_ptr = (cvmx_bootmem_named_block_desc_t *)cvmx_phys_to_ptr(cvmx_bootmem_desc->named_block_array_addr);
659210284Sjmallett#endif
660210284Sjmallett#else
661210284Sjmallett    /* Simple executive case. (and u-boot)
662210284Sjmallett    ** This could be in the low 1 meg of memory that is not 1-1 mapped, so we need use XKPHYS/KSEG0 addressing for it */
663210284Sjmallett    named_block_array_ptr = CASTPTR(cvmx_bootmem_named_block_desc_t, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0,cvmx_bootmem_desc->named_block_array_addr));
664210284Sjmallett#endif
665210284Sjmallett
666210284Sjmallett#ifdef DEBUG
667210284Sjmallett    cvmx_dprintf("cvmx_bootmem_phy_named_block_find: named_block_array_ptr: %p\n", named_block_array_ptr);
668210284Sjmallett#endif
669210284Sjmallett    if (cvmx_bootmem_desc->major_version == 3)
670210284Sjmallett    {
671210284Sjmallett        for (i = 0; i < cvmx_bootmem_desc->named_block_num_blocks; i++)
672210284Sjmallett        {
673210284Sjmallett            if ((name && named_block_array_ptr[i].size && !strncmp(name, named_block_array_ptr[i].name, cvmx_bootmem_desc->named_block_name_len - 1))
674210284Sjmallett                || (!name && !named_block_array_ptr[i].size))
675210284Sjmallett            {
676210284Sjmallett                if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
677210284Sjmallett                    cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
678210284Sjmallett
679210284Sjmallett                return(&(named_block_array_ptr[i]));
680210284Sjmallett            }
681210284Sjmallett        }
682210284Sjmallett    }
683210284Sjmallett    else
684210284Sjmallett    {
685210284Sjmallett        cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: %d.%d at addr: %p\n",
686210284Sjmallett               (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc);
687210284Sjmallett    }
688210284Sjmallett    if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
689210284Sjmallett        cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
690210284Sjmallett
691210284Sjmallett    return(NULL);
692210284Sjmallett}
693210284Sjmallett
694210284Sjmallettint cvmx_bootmem_phy_named_block_free(char *name, uint32_t flags)
695210284Sjmallett{
696210284Sjmallett    cvmx_bootmem_named_block_desc_t *named_block_ptr;
697210284Sjmallett
698210284Sjmallett    if (cvmx_bootmem_desc->major_version != 3)
699210284Sjmallett    {
700210284Sjmallett        cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: %d.%d at addr: %p\n",
701210284Sjmallett               (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc);
702210284Sjmallett        return(0);
703210284Sjmallett    }
704210284Sjmallett#ifdef DEBUG
705210284Sjmallett    cvmx_dprintf("cvmx_bootmem_phy_named_block_free: %s\n", name);
706210284Sjmallett#endif
707210284Sjmallett
708210284Sjmallett    /* Take lock here, as name lookup/block free/name free need to be atomic */
709210284Sjmallett    cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
710210284Sjmallett
711210284Sjmallett    named_block_ptr = cvmx_bootmem_phy_named_block_find(name, CVMX_BOOTMEM_FLAG_NO_LOCKING);
712210284Sjmallett    if (named_block_ptr)
713210284Sjmallett    {
714210284Sjmallett#ifdef DEBUG
715210284Sjmallett        cvmx_dprintf("cvmx_bootmem_phy_named_block_free: %s, base: 0x%llx, size: 0x%llx\n", name, (unsigned long long)named_block_ptr->base_addr, (unsigned long long)named_block_ptr->size);
716210284Sjmallett#endif
717210284Sjmallett        __cvmx_bootmem_phy_free(named_block_ptr->base_addr, named_block_ptr->size, CVMX_BOOTMEM_FLAG_NO_LOCKING);
718210284Sjmallett        named_block_ptr->size = 0;
719210284Sjmallett        /* Set size to zero to indicate block not used. */
720210284Sjmallett    }
721210284Sjmallett
722210284Sjmallett    cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
723210284Sjmallett
724210284Sjmallett    return(!!named_block_ptr);  /* 0 on failure, 1 on success */
725210284Sjmallett}
726210284Sjmallett
727210284Sjmallett
728210284Sjmallett
729210284Sjmallett
730210284Sjmallett
731210284Sjmallettint64_t cvmx_bootmem_phy_named_block_alloc(uint64_t size, uint64_t min_addr, uint64_t max_addr, uint64_t alignment, char *name, uint32_t flags)
732210284Sjmallett{
733210284Sjmallett    int64_t addr_allocated;
734210284Sjmallett    cvmx_bootmem_named_block_desc_t *named_block_desc_ptr;
735210284Sjmallett
736210284Sjmallett#ifdef DEBUG
737210284Sjmallett    cvmx_dprintf("cvmx_bootmem_phy_named_block_alloc: size: 0x%llx, min: 0x%llx, max: 0x%llx, align: 0x%llx, name: %s\n",
738210284Sjmallett                 (unsigned long long)size,
739210284Sjmallett                 (unsigned long long)min_addr,
740210284Sjmallett                 (unsigned long long)max_addr,
741210284Sjmallett                 (unsigned long long)alignment,
742210284Sjmallett                 name);
743210284Sjmallett#endif
744210284Sjmallett    if (cvmx_bootmem_desc->major_version != 3)
745210284Sjmallett    {
746210284Sjmallett        cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: %d.%d at addr: %p\n",
747210284Sjmallett               (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc);
748210284Sjmallett        return(-1);
749210284Sjmallett    }
750210284Sjmallett
751210284Sjmallett
752210284Sjmallett    /* Take lock here, as name lookup/block alloc/name add need to be atomic */
753210284Sjmallett
754210284Sjmallett    if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
755210284Sjmallett        cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
756210284Sjmallett
757210284Sjmallett    /* Get pointer to first available named block descriptor */
758210284Sjmallett    named_block_desc_ptr = cvmx_bootmem_phy_named_block_find(NULL, flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
759210284Sjmallett
760210284Sjmallett    /* Check to see if name already in use, return error if name
761210284Sjmallett    ** not available or no more room for blocks.
762210284Sjmallett    */
763210284Sjmallett    if (cvmx_bootmem_phy_named_block_find(name, flags | CVMX_BOOTMEM_FLAG_NO_LOCKING) || !named_block_desc_ptr)
764210284Sjmallett    {
765210284Sjmallett        if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
766210284Sjmallett            cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
767210284Sjmallett        return(-1);
768210284Sjmallett    }
769210284Sjmallett
770210284Sjmallett
771210284Sjmallett    /* Round size up to mult of minimum alignment bytes
772210284Sjmallett    ** We need the actual size allocated to allow for blocks to be coallesced
773210284Sjmallett    ** when they are freed.  The alloc routine does the same rounding up
774210284Sjmallett    ** on all allocations. */
775210284Sjmallett    size = (size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) & ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1);
776210284Sjmallett
777210284Sjmallett    addr_allocated = cvmx_bootmem_phy_alloc(size, min_addr, max_addr, alignment, flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
778210284Sjmallett    if (addr_allocated >= 0)
779210284Sjmallett    {
780210284Sjmallett        named_block_desc_ptr->base_addr = addr_allocated;
781210284Sjmallett        named_block_desc_ptr->size = size;
782210284Sjmallett        strncpy(named_block_desc_ptr->name, name, cvmx_bootmem_desc->named_block_name_len);
783210284Sjmallett        named_block_desc_ptr->name[cvmx_bootmem_desc->named_block_name_len - 1] = 0;
784210284Sjmallett    }
785210284Sjmallett
786210284Sjmallett    if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
787210284Sjmallett        cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
788210284Sjmallett
789210284Sjmallett    return(addr_allocated);
790210284Sjmallett}
791210284Sjmallett
792210284Sjmallett
793210284Sjmallett
794210284Sjmallett
795210284Sjmallettvoid cvmx_bootmem_phy_named_block_print(void)
796210284Sjmallett{
797210284Sjmallett    unsigned int i;
798210284Sjmallett    int printed = 0;
799210284Sjmallett
800210284Sjmallett#if defined(__linux__) && !defined(CONFIG_OCTEON_U_BOOT)
801210284Sjmallett#ifdef CVMX_ABI_N32
802210284Sjmallett    /* Need to use mmapped named block pointer in 32 bit linux apps */
803210284Sjmallettextern cvmx_bootmem_named_block_desc_t *linux32_named_block_array_ptr;
804210284Sjmallett    cvmx_bootmem_named_block_desc_t *named_block_array_ptr = linux32_named_block_array_ptr;
805210284Sjmallett#else
806210284Sjmallett    /* Use XKPHYS for 64 bit linux */
807210284Sjmallett    cvmx_bootmem_named_block_desc_t *named_block_array_ptr = (cvmx_bootmem_named_block_desc_t *)cvmx_phys_to_ptr(cvmx_bootmem_desc->named_block_array_addr);
808210284Sjmallett#endif
809210284Sjmallett#else
810210284Sjmallett    /* Simple executive case. (and u-boot)
811210284Sjmallett    ** This could be in the low 1 meg of memory that is not 1-1 mapped, so we need use XKPHYS/KSEG0 addressing for it */
812210284Sjmallett    cvmx_bootmem_named_block_desc_t *named_block_array_ptr = CASTPTR(cvmx_bootmem_named_block_desc_t, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0,cvmx_bootmem_desc->named_block_array_addr));
813210284Sjmallett#endif
814210284Sjmallett#ifdef DEBUG
815210284Sjmallett    cvmx_dprintf("cvmx_bootmem_phy_named_block_print, desc addr: %p\n", cvmx_bootmem_desc);
816210284Sjmallett#endif
817210284Sjmallett    if (cvmx_bootmem_desc->major_version != 3)
818210284Sjmallett    {
819210284Sjmallett        cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: %d.%d at addr: %p\n",
820210284Sjmallett               (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc);
821210284Sjmallett        return;
822210284Sjmallett    }
823210284Sjmallett    cvmx_dprintf("List of currently allocated named bootmem blocks:\n");
824210284Sjmallett    for (i = 0; i < cvmx_bootmem_desc->named_block_num_blocks; i++)
825210284Sjmallett    {
826210284Sjmallett        if (named_block_array_ptr[i].size)
827210284Sjmallett        {
828210284Sjmallett            printed++;
829210284Sjmallett            cvmx_dprintf("Name: %s, address: 0x%08qx, size: 0x%08qx, index: %d\n",
830210284Sjmallett                   named_block_array_ptr[i].name,
831210284Sjmallett                   (unsigned long long)named_block_array_ptr[i].base_addr,
832210284Sjmallett                   (unsigned long long)named_block_array_ptr[i].size,
833210284Sjmallett                   i);
834210284Sjmallett
835210284Sjmallett        }
836210284Sjmallett    }
837210284Sjmallett    if (!printed)
838210284Sjmallett    {
839210284Sjmallett        cvmx_dprintf("No named bootmem blocks exist.\n");
840210284Sjmallett    }
841210284Sjmallett
842210284Sjmallett}
843210284Sjmallett
844210284Sjmallett
845210284Sjmallett/* Real physical addresses of memory regions */
846210284Sjmallett#define OCTEON_DDR0_BASE    (0x0ULL)
847210284Sjmallett#define OCTEON_DDR0_SIZE    (0x010000000ULL)
848210284Sjmallett#define OCTEON_DDR1_BASE    (0x410000000ULL)
849210284Sjmallett#define OCTEON_DDR1_SIZE    (0x010000000ULL)
850210284Sjmallett#define OCTEON_DDR2_BASE    (0x020000000ULL)
851210284Sjmallett#define OCTEON_DDR2_SIZE    (0x3e0000000ULL)
852210284Sjmallett#define OCTEON_MAX_PHY_MEM_SIZE (16*1024*1024*1024ULL)
853210284Sjmallettint64_t cvmx_bootmem_phy_mem_list_init(uint64_t mem_size, uint32_t low_reserved_bytes, cvmx_bootmem_desc_t *desc_buffer)
854210284Sjmallett{
855210284Sjmallett    uint64_t cur_block_addr;
856210284Sjmallett    int64_t addr;
857210284Sjmallett
858210284Sjmallett#ifdef DEBUG
859210284Sjmallett    cvmx_dprintf("cvmx_bootmem_phy_mem_list_init (arg desc ptr: %p, cvmx_bootmem_desc: %p)\n", desc_buffer, cvmx_bootmem_desc);
860210284Sjmallett#endif
861210284Sjmallett
862210284Sjmallett    /* Descriptor buffer needs to be in 32 bit addressable space to be compatible with
863210284Sjmallett    ** 32 bit applications */
864210284Sjmallett    if (!desc_buffer)
865210284Sjmallett    {
866210284Sjmallett        cvmx_dprintf("ERROR: no memory for cvmx_bootmem descriptor provided\n");
867210284Sjmallett        return 0;
868210284Sjmallett    }
869210284Sjmallett
870210284Sjmallett    if (mem_size > OCTEON_MAX_PHY_MEM_SIZE)
871210284Sjmallett    {
872210284Sjmallett        mem_size = OCTEON_MAX_PHY_MEM_SIZE;
873210284Sjmallett        cvmx_dprintf("ERROR: requested memory size too large, truncating to maximum size\n");
874210284Sjmallett    }
875210284Sjmallett
876210284Sjmallett    if (cvmx_bootmem_desc)
877210284Sjmallett        return 1;
878210284Sjmallett
879210284Sjmallett    /* Initialize cvmx pointer to descriptor */
880210284Sjmallett    cvmx_bootmem_init(desc_buffer);
881210284Sjmallett
882210284Sjmallett    /* Set up global pointer to start of list, exclude low 64k for exception vectors, space for global descriptor */
883210284Sjmallett    memset(cvmx_bootmem_desc, 0x0, sizeof(cvmx_bootmem_desc_t));
884210284Sjmallett    /* Set version of bootmem descriptor */
885210284Sjmallett    cvmx_bootmem_desc->major_version = CVMX_BOOTMEM_DESC_MAJ_VER;
886210284Sjmallett    cvmx_bootmem_desc->minor_version = CVMX_BOOTMEM_DESC_MIN_VER;
887210284Sjmallett
888210284Sjmallett    cur_block_addr = cvmx_bootmem_desc->head_addr = (OCTEON_DDR0_BASE + low_reserved_bytes);
889210284Sjmallett
890210284Sjmallett    cvmx_bootmem_desc->head_addr = 0;
891210284Sjmallett
892210284Sjmallett    if (mem_size <= OCTEON_DDR0_SIZE)
893210284Sjmallett    {
894210284Sjmallett        __cvmx_bootmem_phy_free(cur_block_addr, mem_size - low_reserved_bytes, 0);
895210284Sjmallett        goto frees_done;
896210284Sjmallett    }
897210284Sjmallett
898210284Sjmallett    __cvmx_bootmem_phy_free(cur_block_addr, OCTEON_DDR0_SIZE - low_reserved_bytes, 0);
899210284Sjmallett
900210284Sjmallett    mem_size -= OCTEON_DDR0_SIZE;
901210284Sjmallett
902210284Sjmallett    /* Add DDR2 block next if present */
903210284Sjmallett    if (mem_size > OCTEON_DDR1_SIZE)
904210284Sjmallett    {
905210284Sjmallett        __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, OCTEON_DDR1_SIZE, 0);
906210284Sjmallett        __cvmx_bootmem_phy_free(OCTEON_DDR2_BASE, mem_size - OCTEON_DDR1_SIZE, 0);
907210284Sjmallett    }
908210284Sjmallett    else
909210284Sjmallett    {
910210284Sjmallett        __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, mem_size, 0);
911210284Sjmallett
912210284Sjmallett    }
913210284Sjmallettfrees_done:
914210284Sjmallett
915210284Sjmallett    /* Initialize the named block structure */
916210284Sjmallett    cvmx_bootmem_desc->named_block_name_len = CVMX_BOOTMEM_NAME_LEN;
917210284Sjmallett    cvmx_bootmem_desc->named_block_num_blocks = CVMX_BOOTMEM_NUM_NAMED_BLOCKS;
918210284Sjmallett    cvmx_bootmem_desc->named_block_array_addr = 0;
919210284Sjmallett
920210284Sjmallett    /* Allocate this near the top of the low 256 MBytes of memory */
921210284Sjmallett    addr = cvmx_bootmem_phy_alloc(CVMX_BOOTMEM_NUM_NAMED_BLOCKS * sizeof(cvmx_bootmem_named_block_desc_t),0, 0x10000000, 0 ,CVMX_BOOTMEM_FLAG_END_ALLOC);
922210284Sjmallett    if (addr >= 0)
923210284Sjmallett        cvmx_bootmem_desc->named_block_array_addr = addr;
924210284Sjmallett
925210284Sjmallett#ifdef DEBUG
926210284Sjmallett    cvmx_dprintf("cvmx_bootmem_phy_mem_list_init: named_block_array_addr: 0x%llx)\n", (unsigned long long)cvmx_bootmem_desc->named_block_array_addr);
927210284Sjmallett#endif
928210284Sjmallett    if (!cvmx_bootmem_desc->named_block_array_addr)
929210284Sjmallett    {
930210284Sjmallett        cvmx_dprintf("FATAL ERROR: unable to allocate memory for bootmem descriptor!\n");
931210284Sjmallett        return(0);
932210284Sjmallett    }
933210284Sjmallett    memset((void *)(unsigned long)cvmx_bootmem_desc->named_block_array_addr, 0x0, CVMX_BOOTMEM_NUM_NAMED_BLOCKS * sizeof(cvmx_bootmem_named_block_desc_t));
934210284Sjmallett
935210284Sjmallett    return(1);
936210284Sjmallett}
937210284Sjmallett
938210284Sjmallett
939210284Sjmallettvoid cvmx_bootmem_lock(void)
940210284Sjmallett{
941210284Sjmallett    cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
942210284Sjmallett}
943210284Sjmallett
944210284Sjmallettvoid cvmx_bootmem_unlock(void)
945210284Sjmallett{
946210284Sjmallett    cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
947210284Sjmallett}
948210284Sjmallett
949210284Sjmallettvoid *__cvmx_bootmem_internal_get_desc_ptr(void)
950210284Sjmallett{
951210284Sjmallett    return(cvmx_bootmem_desc);
952210284Sjmallett}
953