struct cvmx_bootmem_named_block_desc * cvmx_bootmem_phy_named_block_find(char *name, uint32_t flags) { unsigned int i; struct cvmx_bootmem_named_block_desc *named_block_array_ptr; #ifdef DEBUG cvmx_dprintf("cvmx_bootmem_phy_named_block_find: %s\n", name); #endif /* * Lock the structure to make sure that it is not being * changed while we are examining it. */ if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_lock(); /* Use XKPHYS for 64 bit linux */ named_block_array_ptr = (struct cvmx_bootmem_named_block_desc *) cvmx_phys_to_ptr(cvmx_bootmem_desc->named_block_array_addr); #ifdef DEBUG cvmx_dprintf ("cvmx_bootmem_phy_named_block_find: named_block_array_ptr: %p\n", named_block_array_ptr); #endif if (cvmx_bootmem_desc->major_version == 3) { for (i = 0; i < cvmx_bootmem_desc->named_block_num_blocks; i++) { if ((name && named_block_array_ptr[i].size && !strncmp(name, named_block_array_ptr[i].name, cvmx_bootmem_desc->named_block_name_len - 1)) || (!name && !named_block_array_ptr[i].size)) { if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_unlock(); return &(named_block_array_ptr[i]); } } } else { cvmx_dprintf("ERROR: Incompatible bootmem descriptor " "version: %d.%d at addr: %p\n", (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc); } if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_unlock(); return NULL; }
int cvmx_bootmem_phy_named_block_free(char *name, uint32_t flags) { struct cvmx_bootmem_named_block_desc *named_block_ptr; if (cvmx_bootmem_desc->major_version != 3) { cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: " "%d.%d at addr: %p\n", (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc); return 0; } #ifdef DEBUG cvmx_dprintf("cvmx_bootmem_phy_named_block_free: %s\n", name); #endif /* * Take lock here, as name lookup/block free/name free need to * be atomic. */ cvmx_bootmem_lock(); named_block_ptr = cvmx_bootmem_phy_named_block_find(name, CVMX_BOOTMEM_FLAG_NO_LOCKING); if (named_block_ptr) { #ifdef DEBUG 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); #endif __cvmx_bootmem_phy_free(named_block_ptr->base_addr, named_block_ptr->size, CVMX_BOOTMEM_FLAG_NO_LOCKING); named_block_ptr->size = 0; /* Set size to zero to indicate block not used. */ } cvmx_bootmem_unlock(); return named_block_ptr != NULL; /* 0 on failure, 1 on success */ }
int cvmx_bootmem_phy_named_block_free(char *name, uint32_t flags) { struct cvmx_bootmem_named_block_desc *named_block_ptr; if (cvmx_bootmem_desc->major_version != 3) { cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: " "%d.%d at addr: %p\n", (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc); return 0; } #ifdef DEBUG cvmx_dprintf("cvmx_bootmem_phy_named_block_free: %s\n", name); #endif cvmx_bootmem_lock(); named_block_ptr = cvmx_bootmem_phy_named_block_find(name, CVMX_BOOTMEM_FLAG_NO_LOCKING); if (named_block_ptr) { #ifdef DEBUG 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); #endif __cvmx_bootmem_phy_free(named_block_ptr->base_addr, named_block_ptr->size, CVMX_BOOTMEM_FLAG_NO_LOCKING); named_block_ptr->size = 0; } cvmx_bootmem_unlock(); return named_block_ptr != NULL; }
int __cvmx_bootmem_phy_free(uint64_t phy_addr, uint64_t size, uint32_t flags) { uint64_t cur_addr; uint64_t prev_addr = 0; int retval = 0; #ifdef DEBUG cvmx_dprintf("__cvmx_bootmem_phy_free addr: 0x%llx, size: 0x%llx\n", (unsigned long long)phy_addr, (unsigned long long)size); #endif if (cvmx_bootmem_desc->major_version > 3) { cvmx_dprintf("ERROR: Incompatible bootmem descriptor " "version: %d.%d at addr: %p\n", (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc); return 0; } if (!size) return 0; if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_lock(); cur_addr = cvmx_bootmem_desc->head_addr; if (cur_addr == 0 || phy_addr < cur_addr) { if (cur_addr && phy_addr + size > cur_addr) goto bootmem_free_done; else if (phy_addr + size == cur_addr) { cvmx_bootmem_phy_set_next(phy_addr, cvmx_bootmem_phy_get_next (cur_addr)); cvmx_bootmem_phy_set_size(phy_addr, cvmx_bootmem_phy_get_size (cur_addr) + size); cvmx_bootmem_desc->head_addr = phy_addr; } else { cvmx_bootmem_phy_set_next(phy_addr, cur_addr); cvmx_bootmem_phy_set_size(phy_addr, size); cvmx_bootmem_desc->head_addr = phy_addr; } retval = 1; goto bootmem_free_done; } while (cur_addr && phy_addr > cur_addr) { prev_addr = cur_addr; cur_addr = cvmx_bootmem_phy_get_next(cur_addr); } if (!cur_addr) { if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) == phy_addr) { cvmx_bootmem_phy_set_size(prev_addr, cvmx_bootmem_phy_get_size (prev_addr) + size); } else { cvmx_bootmem_phy_set_next(prev_addr, phy_addr); cvmx_bootmem_phy_set_size(phy_addr, size); cvmx_bootmem_phy_set_next(phy_addr, 0); } retval = 1; goto bootmem_free_done; } else { if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) == phy_addr) { cvmx_bootmem_phy_set_size(prev_addr, cvmx_bootmem_phy_get_size (prev_addr) + size); if (phy_addr + size == cur_addr) { cvmx_bootmem_phy_set_size(prev_addr, cvmx_bootmem_phy_get_size(cur_addr) + cvmx_bootmem_phy_get_size(prev_addr)); cvmx_bootmem_phy_set_next(prev_addr, cvmx_bootmem_phy_get_next(cur_addr)); } retval = 1; goto bootmem_free_done; } else if (phy_addr + size == cur_addr) { cvmx_bootmem_phy_set_size(phy_addr, cvmx_bootmem_phy_get_size (cur_addr) + size); cvmx_bootmem_phy_set_next(phy_addr, cvmx_bootmem_phy_get_next (cur_addr)); cvmx_bootmem_phy_set_next(prev_addr, phy_addr); retval = 1; goto bootmem_free_done; } cvmx_bootmem_phy_set_size(phy_addr, size); cvmx_bootmem_phy_set_next(phy_addr, cur_addr); cvmx_bootmem_phy_set_next(prev_addr, phy_addr); } retval = 1; bootmem_free_done: if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_unlock(); return retval; }
int64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min, uint64_t address_max, uint64_t alignment, uint32_t flags) { uint64_t head_addr; uint64_t ent_addr; uint64_t prev_addr = 0; uint64_t new_ent_addr = 0; uint64_t desired_min_addr; #ifdef DEBUG cvmx_dprintf("cvmx_bootmem_phy_alloc: req_size: 0x%llx, " "min_addr: 0x%llx, max_addr: 0x%llx, align: 0x%llx\n", (unsigned long long)req_size, (unsigned long long)address_min, (unsigned long long)address_max, (unsigned long long)alignment); #endif if (cvmx_bootmem_desc->major_version > 3) { cvmx_dprintf("ERROR: Incompatible bootmem descriptor " "version: %d.%d at addr: %p\n", (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc); goto error_out; } if (!req_size) goto error_out; req_size = (req_size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) & ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1); if (address_min && !address_max) address_max = address_min + req_size; else if (!address_min && !address_max) address_max = ~0ull; if (alignment < CVMX_BOOTMEM_ALIGNMENT_SIZE) alignment = CVMX_BOOTMEM_ALIGNMENT_SIZE; if (alignment) address_min = ALIGN(address_min, alignment); if (req_size > address_max - address_min) goto error_out; if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_lock(); head_addr = cvmx_bootmem_desc->head_addr; ent_addr = head_addr; for (; ent_addr; prev_addr = ent_addr, ent_addr = cvmx_bootmem_phy_get_next(ent_addr)) { uint64_t usable_base, usable_max; uint64_t ent_size = cvmx_bootmem_phy_get_size(ent_addr); if (cvmx_bootmem_phy_get_next(ent_addr) && ent_addr > cvmx_bootmem_phy_get_next(ent_addr)) { cvmx_dprintf("Internal bootmem_alloc() error: ent: " "0x%llx, next: 0x%llx\n", (unsigned long long)ent_addr, (unsigned long long) cvmx_bootmem_phy_get_next(ent_addr)); goto error_out; } usable_base = ALIGN(max(address_min, ent_addr), alignment); usable_max = min(address_max, ent_addr + ent_size); desired_min_addr = usable_base; if (!((ent_addr + ent_size) > usable_base && ent_addr < address_max && req_size <= usable_max - usable_base)) continue; if (flags & CVMX_BOOTMEM_FLAG_END_ALLOC) { desired_min_addr = usable_max - req_size; desired_min_addr &= ~(alignment - 1); } if (desired_min_addr == ent_addr) { if (req_size < ent_size) { new_ent_addr = ent_addr + req_size; cvmx_bootmem_phy_set_next(new_ent_addr, cvmx_bootmem_phy_get_next(ent_addr)); cvmx_bootmem_phy_set_size(new_ent_addr, ent_size - req_size); cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr); } if (prev_addr) cvmx_bootmem_phy_set_next(prev_addr, cvmx_bootmem_phy_get_next(ent_addr)); else cvmx_bootmem_desc->head_addr = cvmx_bootmem_phy_get_next(ent_addr); if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_unlock(); return desired_min_addr; } new_ent_addr = desired_min_addr; cvmx_bootmem_phy_set_next(new_ent_addr, cvmx_bootmem_phy_get_next (ent_addr)); cvmx_bootmem_phy_set_size(new_ent_addr, cvmx_bootmem_phy_get_size (ent_addr) - (desired_min_addr - ent_addr)); cvmx_bootmem_phy_set_size(ent_addr, desired_min_addr - ent_addr); cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr); } error_out: if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_unlock(); return -1; }
int __cvmx_bootmem_phy_free(uint64_t phy_addr, uint64_t size, uint32_t flags) { uint64_t cur_addr; uint64_t prev_addr = 0; /* zero is invalid */ int retval = 0; #ifdef DEBUG cvmx_dprintf("__cvmx_bootmem_phy_free addr: 0x%llx, size: 0x%llx\n", (unsigned long long)phy_addr, (unsigned long long)size); #endif if (cvmx_bootmem_desc->major_version > 3) { cvmx_dprintf("ERROR: Incompatible bootmem descriptor " "version: %d.%d at addr: %p\n", (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc); return 0; } /* 0 is not a valid size for this allocator */ if (!size) return 0; if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_lock(); cur_addr = cvmx_bootmem_desc->head_addr; if (cur_addr == 0 || phy_addr < cur_addr) { /* add at front of list - special case with changing head ptr */ if (cur_addr && phy_addr + size > cur_addr) goto bootmem_free_done; /* error, overlapping section */ else if (phy_addr + size == cur_addr) { /* Add to front of existing first block */ cvmx_bootmem_phy_set_next(phy_addr, cvmx_bootmem_phy_get_next (cur_addr)); cvmx_bootmem_phy_set_size(phy_addr, cvmx_bootmem_phy_get_size (cur_addr) + size); cvmx_bootmem_desc->head_addr = phy_addr; } else { /* New block before first block. OK if cur_addr is 0 */ cvmx_bootmem_phy_set_next(phy_addr, cur_addr); cvmx_bootmem_phy_set_size(phy_addr, size); cvmx_bootmem_desc->head_addr = phy_addr; } retval = 1; goto bootmem_free_done; } /* Find place in list to add block */ while (cur_addr && phy_addr > cur_addr) { prev_addr = cur_addr; cur_addr = cvmx_bootmem_phy_get_next(cur_addr); } if (!cur_addr) { /* * We have reached the end of the list, add on to end, * checking to see if we need to combine with last * block */ if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) == phy_addr) { cvmx_bootmem_phy_set_size(prev_addr, cvmx_bootmem_phy_get_size (prev_addr) + size); } else { cvmx_bootmem_phy_set_next(prev_addr, phy_addr); cvmx_bootmem_phy_set_size(phy_addr, size); cvmx_bootmem_phy_set_next(phy_addr, 0); } retval = 1; goto bootmem_free_done; } else { /* * insert between prev and cur nodes, checking for * merge with either/both. */ if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) == phy_addr) { /* Merge with previous */ cvmx_bootmem_phy_set_size(prev_addr, cvmx_bootmem_phy_get_size (prev_addr) + size); if (phy_addr + size == cur_addr) { /* Also merge with current */ cvmx_bootmem_phy_set_size(prev_addr, cvmx_bootmem_phy_get_size(cur_addr) + cvmx_bootmem_phy_get_size(prev_addr)); cvmx_bootmem_phy_set_next(prev_addr, cvmx_bootmem_phy_get_next(cur_addr)); } retval = 1; goto bootmem_free_done; } else if (phy_addr + size == cur_addr) { /* Merge with current */ cvmx_bootmem_phy_set_size(phy_addr, cvmx_bootmem_phy_get_size (cur_addr) + size); cvmx_bootmem_phy_set_next(phy_addr, cvmx_bootmem_phy_get_next (cur_addr)); cvmx_bootmem_phy_set_next(prev_addr, phy_addr); retval = 1; goto bootmem_free_done; } /* It is a standalone block, add in between prev and cur */ cvmx_bootmem_phy_set_size(phy_addr, size); cvmx_bootmem_phy_set_next(phy_addr, cur_addr); cvmx_bootmem_phy_set_next(prev_addr, phy_addr); } retval = 1; bootmem_free_done: if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_unlock(); return retval; }
int64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min, uint64_t address_max, uint64_t alignment, uint32_t flags) { uint64_t head_addr; uint64_t ent_addr; /* points to previous list entry, NULL current entry is head of list */ uint64_t prev_addr = 0; uint64_t new_ent_addr = 0; uint64_t desired_min_addr; #ifdef DEBUG cvmx_dprintf("cvmx_bootmem_phy_alloc: req_size: 0x%llx, " "min_addr: 0x%llx, max_addr: 0x%llx, align: 0x%llx\n", (unsigned long long)req_size, (unsigned long long)address_min, (unsigned long long)address_max, (unsigned long long)alignment); #endif if (cvmx_bootmem_desc->major_version > 3) { cvmx_dprintf("ERROR: Incompatible bootmem descriptor " "version: %d.%d at addr: %p\n", (int)cvmx_bootmem_desc->major_version, (int)cvmx_bootmem_desc->minor_version, cvmx_bootmem_desc); goto error_out; } /* * Do a variety of checks to validate the arguments. The * allocator code will later assume that these checks have * been made. We validate that the requested constraints are * not self-contradictory before we look through the list of * available memory. */ /* 0 is not a valid req_size for this allocator */ if (!req_size) goto error_out; /* Round req_size up to mult of minimum alignment bytes */ req_size = (req_size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) & ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1); /* * Convert !0 address_min and 0 address_max to special case of * range that specifies an exact memory block to allocate. Do * this before other checks and adjustments so that this * tranformation will be validated. */ if (address_min && !address_max) address_max = address_min + req_size; else if (!address_min && !address_max) address_max = ~0ull; /* If no limits given, use max limits */ /* * Enforce minimum alignment (this also keeps the minimum free block * req_size the same as the alignment req_size. */ if (alignment < CVMX_BOOTMEM_ALIGNMENT_SIZE) alignment = CVMX_BOOTMEM_ALIGNMENT_SIZE; /* * Adjust address minimum based on requested alignment (round * up to meet alignment). Do this here so we can reject * impossible requests up front. (NOP for address_min == 0) */ if (alignment) address_min = ALIGN(address_min, alignment); /* * Reject inconsistent args. We have adjusted these, so this * may fail due to our internal changes even if this check * would pass for the values the user supplied. */ if (req_size > address_max - address_min) goto error_out; /* Walk through the list entries - first fit found is returned */ if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_lock(); head_addr = cvmx_bootmem_desc->head_addr; ent_addr = head_addr; for (; ent_addr; prev_addr = ent_addr, ent_addr = cvmx_bootmem_phy_get_next(ent_addr)) { uint64_t usable_base, usable_max; uint64_t ent_size = cvmx_bootmem_phy_get_size(ent_addr); if (cvmx_bootmem_phy_get_next(ent_addr) && ent_addr > cvmx_bootmem_phy_get_next(ent_addr)) { cvmx_dprintf("Internal bootmem_alloc() error: ent: " "0x%llx, next: 0x%llx\n", (unsigned long long)ent_addr, (unsigned long long) cvmx_bootmem_phy_get_next(ent_addr)); goto error_out; } /* * Determine if this is an entry that can satisify the * request Check to make sure entry is large enough to * satisfy request. */ usable_base = ALIGN(max(address_min, ent_addr), alignment); usable_max = min(address_max, ent_addr + ent_size); /* * We should be able to allocate block at address * usable_base. */ desired_min_addr = usable_base; /* * Determine if request can be satisfied from the * current entry. */ if (!((ent_addr + ent_size) > usable_base && ent_addr < address_max && req_size <= usable_max - usable_base)) continue; /* * We have found an entry that has room to satisfy the * request, so allocate it from this entry. If end * CVMX_BOOTMEM_FLAG_END_ALLOC set, then allocate from * the end of this block rather than the beginning. */ if (flags & CVMX_BOOTMEM_FLAG_END_ALLOC) { desired_min_addr = usable_max - req_size; /* * Align desired address down to required * alignment. */ desired_min_addr &= ~(alignment - 1); } /* Match at start of entry */ if (desired_min_addr == ent_addr) { if (req_size < ent_size) { /* * big enough to create a new block * from top portion of block. */ new_ent_addr = ent_addr + req_size; cvmx_bootmem_phy_set_next(new_ent_addr, cvmx_bootmem_phy_get_next(ent_addr)); cvmx_bootmem_phy_set_size(new_ent_addr, ent_size - req_size); /* * Adjust next pointer as following * code uses this. */ cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr); } /* * adjust prev ptr or head to remove this * entry from list. */ if (prev_addr) cvmx_bootmem_phy_set_next(prev_addr, cvmx_bootmem_phy_get_next(ent_addr)); else /* * head of list being returned, so * update head ptr. */ cvmx_bootmem_desc->head_addr = cvmx_bootmem_phy_get_next(ent_addr); if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_unlock(); return desired_min_addr; } /* * block returned doesn't start at beginning of entry, * so we know that we will be splitting a block off * the front of this one. Create a new block from the * beginning, add to list, and go to top of loop * again. * * create new block from high portion of * block, so that top block starts at desired * addr. */ new_ent_addr = desired_min_addr; cvmx_bootmem_phy_set_next(new_ent_addr, cvmx_bootmem_phy_get_next (ent_addr)); cvmx_bootmem_phy_set_size(new_ent_addr, cvmx_bootmem_phy_get_size (ent_addr) - (desired_min_addr - ent_addr)); cvmx_bootmem_phy_set_size(ent_addr, desired_min_addr - ent_addr); cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr); /* Loop again to handle actual alloc from new block */ } error_out: /* We didn't find anything, so return error */ if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) cvmx_bootmem_unlock(); return -1; }