1/*
2 * File:	SRecordSourceFile.cpp
3 *
4 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5 * See included license file for license details.
6 */
7
8#include "SRecordSourceFile.h"
9#include "Logging.h"
10#include "smart_ptr.h"
11#include <assert.h>
12#include <string.h>
13enum
14{
15	//! Size in bytes of the buffer used to collect S-record data records
16	//! before adding them to the executable image. Currently 64KB.
17	COLLECTION_BUFFER_SIZE = 64 * 1024
18};
19
20using namespace elftosb;
21
22SRecordSourceFile::SRecordSourceFile(const std::string & path)
23:	SourceFile(path), m_image(0), m_hasEntryRecord(false)
24{
25}
26
27bool SRecordSourceFile::isSRecordFile(std::istream & stream)
28{
29	StSRecordFile srec(stream);
30	return srec.isSRecordFile();
31}
32
33void SRecordSourceFile::open()
34{
35	SourceFile::open();
36
37	// create file parser and examine file
38	m_file = new StSRecordFile(*m_stream);
39	m_file->parse();
40
41	// build an image of the file
42	m_image = new StExecutableImage();
43	buildMemoryImage();
44
45	// dispose of file parser object
46	delete m_file;
47	m_file = 0;
48}
49
50void SRecordSourceFile::close()
51{
52	assert(m_image);
53
54	SourceFile::close();
55
56	// dispose of memory image
57	delete m_image;
58	m_image = 0;
59}
60
61//! \pre The file must be open before this method can be called.
62//!
63DataSource * SRecordSourceFile::createDataSource()
64{
65	assert(m_image);
66	return new MemoryImageDataSource(m_image);
67}
68
69//! \retval true The file has an S7, S8, or S9 record.
70//! \retval false No entry point is available.
71bool SRecordSourceFile::hasEntryPoint()
72{
73	return m_hasEntryRecord;
74}
75
76//! If no entry point is available then 0 is returned instead. The method scans
77//! the records in the file looking for S7, S8, or S9 records. Thus, 16-bit,
78//! 24-bit, and 32-bit entry point records are supported.
79//!
80//! \return Entry point address.
81//! \retval 0 No entry point is available.
82uint32_t SRecordSourceFile::getEntryPointAddress()
83{
84	if (m_hasEntryRecord)
85	{
86		// the address in the record is the entry point
87		Log::log(Logger::DEBUG2, "entry point address is 0x%08x\n", m_entryRecord.m_address);
88		return m_entryRecord.m_address;
89	}
90
91	return 0;
92}
93
94//! Scans the S-records of the file looking for data records. These are S3, S2, or
95//! S1 records. The contents of these records are added to an StExecutableImage
96//! object, which coalesces the individual records into contiguous regions of
97//! memory.
98//!
99//! Also looks for S7, S8, or S9 records that contain the entry point. The first
100//! match of one of these records is saved off into the #m_entryRecord member.
101//!
102//! \pre The #m_file member must be valid.
103//! \pre The #m_image member variable must have been instantiated.
104void SRecordSourceFile::buildMemoryImage()
105{
106	assert(m_file);
107	assert(m_image);
108
109	// Clear the entry point related members.
110	m_hasEntryRecord = false;
111	memset(&m_entryRecord, 0, sizeof(m_entryRecord));
112
113	// Allocate buffer to hold data before adding it to the executable image.
114	// Contiguous records are added to this buffer. When overflowed or when a
115	// non-contiguous record is encountered the buffer is added to the executable
116	// image where it will be coalesced further. We don't add records individually
117	// to the image because coalescing record by record is very slow.
118	smart_array_ptr<uint8_t> buffer = new uint8_t[COLLECTION_BUFFER_SIZE];
119	unsigned startAddress;
120	unsigned nextAddress;
121	unsigned dataLength = 0;
122
123	// process SRecords
124    StSRecordFile::const_iterator it = m_file->getBegin();
125	for (; it != m_file->getEnd(); it++)
126	{
127        const StSRecordFile::SRecord & theRecord = *it;
128
129        // only handle S3,2,1 records
130        bool isDataRecord = theRecord.m_type == 3 || theRecord.m_type == 2 || theRecord.m_type == 1;
131        bool hasData = theRecord.m_data && theRecord.m_dataCount;
132		if (isDataRecord && hasData)
133		{
134			// If this record's data would overflow the collection buffer, or if the
135			// record is not contiguous with the rest of the data in the collection
136			// buffer, then flush the buffer to the executable image and restart.
137			if (dataLength && ((dataLength + theRecord.m_dataCount > COLLECTION_BUFFER_SIZE) || (theRecord.m_address != nextAddress)))
138			{
139				m_image->addTextRegion(startAddress, buffer, dataLength);
140
141				dataLength = 0;
142			}
143
144			// Capture addresses when starting an empty buffer.
145			if (dataLength == 0)
146			{
147				startAddress = theRecord.m_address;
148				nextAddress = startAddress;
149			}
150
151			// Copy record data into place in the collection buffer and update
152			// size and address.
153			memcpy(&buffer[dataLength], theRecord.m_data, theRecord.m_dataCount);
154			dataLength += theRecord.m_dataCount;
155			nextAddress += theRecord.m_dataCount;
156		}
157		else if (!m_hasEntryRecord)
158		{
159			// look for S7,8,9 records
160			bool isEntryPointRecord = theRecord.m_type == 7 || theRecord.m_type == 8 || theRecord.m_type == 9;
161			if (isEntryPointRecord)
162			{
163				// save off the entry point record so we don't have to scan again
164				memcpy(&m_entryRecord, &theRecord, sizeof(m_entryRecord));
165				m_hasEntryRecord = true;
166			}
167		}
168	}
169
170	// Add any leftover data in the collection buffer to the executable image.
171	if (dataLength)
172	{
173		m_image->addTextRegion(startAddress, buffer, dataLength);
174	}
175}
176
177