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