Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

/*
 * File:	DataSourceImager.cpp
 *
 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
 * See included license file for license details.
 */

#include "DataSourceImager.h"
#include <stdlib.h>
#include <string.h>

using namespace elftosb;

DataSourceImager::DataSourceImager()
:	Blob(),
	m_fill(0),
	m_baseAddress(0),
	m_isBaseAddressSet(false)
{
}

void DataSourceImager::setBaseAddress(uint32_t address)
{
	m_baseAddress = address;
	m_isBaseAddressSet = true;
}

void DataSourceImager::setFillPattern(uint8_t pattern)
{
	m_fill = pattern;
}

void DataSourceImager::reset()
{
	clear();
	
	m_fill = 0;
	m_baseAddress = 0;
	m_isBaseAddressSet = false;
}

//! \param dataSource Pointer to an instance of a concrete data source subclass.
//!
void DataSourceImager::addDataSource(DataSource * source)
{
	unsigned segmentCount = source->getSegmentCount();
	unsigned index = 0;
	for (; index < segmentCount; ++index)
	{
		addDataSegment(source->getSegmentAt(index));
	}
}

//! \param segment The segment to add. May be any type of data segment, including
//!		a pattern segment.
void DataSourceImager::addDataSegment(DataSource::Segment * segment)
{
	DataSource::PatternSegment * patternSegment = dynamic_cast<DataSource::PatternSegment*>(segment);
	
	unsigned segmentLength = segment->getLength();
	bool segmentHasLocation = segment->hasNaturalLocation();
	uint32_t segmentAddress = segment->getBaseAddress();
	
	uint8_t * toPtr = NULL;
	unsigned addressDelta;
	unsigned newLength;
	
	// If a pattern segment's length is 0 then make it as big as the fill pattern.
	// This needs to be done before the buffer is adjusted.
	if (patternSegment && segmentLength == 0)
	{
		SizedIntegerValue & pattern = patternSegment->getPattern();
		segmentLength = pattern.getSize();
	}
	
	if (segmentLength)
	{
		if (segmentHasLocation)
		{
			// Make sure a base address is set.
			if (!m_isBaseAddressSet)
			{
				m_baseAddress = segmentAddress;
				m_isBaseAddressSet = true;
			}
			
			// The segment is located before our buffer's first address.
			// toPtr is not set in this if, but in the else branch of the next if.
			// Unless the segment completely overwrite the current data.
			if (segmentAddress < m_baseAddress)
			{
				addressDelta = m_baseAddress - segmentAddress;
				
				uint8_t * newData = (uint8_t *)malloc(m_length + addressDelta);
				memcpy(&newData[addressDelta], m_data, m_length);
				free(m_data);
				
				m_data = newData;
				m_length += addressDelta;
				m_baseAddress = segmentAddress;
			}
			
			// This segment is located or extends outside of our buffer.
			if (segmentAddress + segmentLength > m_baseAddress + m_length)
			{
				newLength = segmentAddress + segmentLength - m_baseAddress;
				
				m_data = (uint8_t *)realloc(m_data, newLength);
				
				// Clear any bytes between the old data and the new segment.
				addressDelta = segmentAddress - (m_baseAddress + m_length);
				if (addressDelta)
				{
					memset(m_data + m_length, 0, addressDelta);
				}
				
				toPtr = m_data + (segmentAddress - m_baseAddress);
				m_length = newLength;
			}
			else
			{
				toPtr = m_data + (segmentAddress - m_baseAddress);
			}
		}
		// Segment has no natural location, so just append it to the end of our buffer.
		else
		{
			newLength = m_length + segmentLength;
			m_data = (uint8_t *)realloc(m_data, newLength);
			toPtr = m_data + m_length;
			m_length = newLength;
		}
	}

	// A loop is used because getData() may fill in less than the requested
	// number of bytes per call.
	unsigned offset = 0;
	while (offset < segmentLength)
	{
		offset += segment->getData(offset, segmentLength - offset, toPtr + offset);
	}
}