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;

}
Ejemplo n.º 2
0
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;

    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;
}
Ejemplo n.º 4
0
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;
}