1 /*
2  *  Copyright (C) 2008-2019  Anders Gavare.  All rights reserved.
3  *
4  *  Redistribution and use in source and binary forms, with or without
5  *  modification, are permitted provided that the following conditions are met:
6  *
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. The name of the author may not be used to endorse or promote products
13  *     derived from this software without specific prior written permission.
14  *
15  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  *  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  *  SUCH DAMAGE.
26  */
27 
28 #include <assert.h>
29 #include <string.h>
30 #include <fstream>
31 
32 using std::ifstream;
33 
34 #include "AddressDataBus.h"
35 #include "Component.h"
36 #include "FileLoader.h"
37 #include "FileLoader_aout.h"
38 #include "FileLoader_bout.h"
39 #include "FileLoader_ELF.h"
40 #include "FileLoader_raw.h"
41 
42 
FileLoader(const string & filename)43 FileLoader::FileLoader(const string& filename)
44 	: m_filename(filename)
45 {
46 	m_fileLoaders.push_back(new FileLoader_aout(filename));
47 	m_fileLoaders.push_back(new FileLoader_bout(filename));
48 	m_fileLoaders.push_back(new FileLoader_ELF(filename));
49 	m_fileLoaders.push_back(new FileLoader_raw(filename));
50 }
51 
52 
GetFilename() const53 const string& FileLoader::GetFilename() const
54 {
55 	return m_filename;
56 }
57 
58 
DetectFileFormat(refcount_ptr<const FileLoaderImpl> & loader) const59 string FileLoader::DetectFileFormat(refcount_ptr<const FileLoaderImpl>& loader) const
60 {
61 	loader = NULL;
62 
63 	// TODO: Disk images?
64 
65 	ifstream file(m_filename.c_str());
66 
67 	unsigned char buf[512];
68 	memset(buf, 0, sizeof(buf));
69 	size_t amountRead = 0;
70 	if (file.is_open()) {
71 		file.read((char *)buf, sizeof(buf));
72 		amountRead = file.gcount();
73 	}
74 
75 	// Ask all file loaders about how well they handle the format. Return
76 	// the format string from the loader that had the highest score.
77 	float bestMatch = 0.0;
78 	string bestFormat = "Unknown";
79 	FileLoaderImplVector::const_iterator it = m_fileLoaders.begin();
80 	for (; it != m_fileLoaders.end(); ++it) {
81 		float match;
82 		string format = (*it)->DetectFileType(buf, amountRead, match);
83 		if (match > bestMatch) {
84 			bestMatch = match;
85 			bestFormat = format;
86 			loader = *it;
87 		}
88 	}
89 
90 	if (bestMatch == 0.0 && !file.is_open())
91 		return "Not accessible";
92 
93 	return bestFormat;
94 }
95 
96 
Load(refcount_ptr<Component> component,ostream & messages) const97 bool FileLoader::Load(refcount_ptr<Component> component, ostream& messages) const
98 {
99 	AddressDataBus * bus = component->AsAddressDataBus();
100 	if (bus == NULL) {
101 		// We cannot load into something that isn't an AddressDataBus.
102 		messages << "Error: " << component->GenerateShortestPossiblePath()
103 		    << " is not an AddressDataBus.\n";
104 		return false;
105 	}
106 
107 	refcount_ptr<const FileLoaderImpl> loaderImpl;
108 	string fileFormat = DetectFileFormat(loaderImpl);
109 
110 	if (!loaderImpl.IsNULL())
111 		return loaderImpl->LoadIntoComponent(component, messages);
112 
113 	messages << "Error: File format '" <<
114 	    fileFormat << "' not yet implemented. TODO\n";
115 
116 	return false;
117 }
118 
119 
120 /*****************************************************************************/
121 
122 
123 #ifdef WITHUNITTESTS
124 
125 #include "ComponentFactory.h"
126 
Test_FileLoader_Constructor()127 static void Test_FileLoader_Constructor()
128 {
129 	FileLoader fileLoader("test/FileLoader_ELF_MIPS");
130 	UnitTest::Assert("filename mismatch?",
131 	    fileLoader.GetFilename(), "test/FileLoader_ELF_MIPS");
132 }
133 
Test_FileLoader_Constructor_NonExistingFile()134 static void Test_FileLoader_Constructor_NonExistingFile()
135 {
136 	FileLoader fileLoader("test/Nonexisting");
137 	UnitTest::Assert("filename mismatch?",
138 	    fileLoader.GetFilename(), "test/Nonexisting");
139 }
140 
Test_FileLoader_DetectFileFormat_NonexistingFile()141 static void Test_FileLoader_DetectFileFormat_NonexistingFile()
142 {
143 	FileLoader fileLoader("test/Nonexisting");
144 	refcount_ptr<const FileLoaderImpl> loaderImpl;
145 	UnitTest::Assert("file format detection failure?",
146 	    fileLoader.DetectFileFormat(loaderImpl), "Not accessible");
147 }
148 
Test_FileLoader_DetectFileFormat_NonsenseFile()149 static void Test_FileLoader_DetectFileFormat_NonsenseFile()
150 {
151 	FileLoader fileLoader("test/FileLoader_NonsenseFile");
152 	refcount_ptr<const FileLoaderImpl> loaderImpl;
153 	UnitTest::Assert("file format detection failure?",
154 	    fileLoader.DetectFileFormat(loaderImpl), "Unknown");
155 }
156 
Test_FileLoader_DetectFileFormat_ELF32()157 static void Test_FileLoader_DetectFileFormat_ELF32()
158 {
159 	FileLoader fileLoader("test/FileLoader_ELF_MIPS");
160 	refcount_ptr<const FileLoaderImpl> loaderImpl;
161 	UnitTest::Assert("file format detection failure?",
162 	    fileLoader.DetectFileFormat(loaderImpl), "ELF32");
163 }
164 
Test_FileLoader_DetectFileFormat_ELF64()165 static void Test_FileLoader_DetectFileFormat_ELF64()
166 {
167 	FileLoader fileLoader("test/FileLoader_ELF_SH5");
168 	refcount_ptr<const FileLoaderImpl> loaderImpl;
169 	UnitTest::Assert("file format detection failure?",
170 	    fileLoader.DetectFileFormat(loaderImpl), "ELF64");
171 }
172 
Test_FileLoader_DetectFileFormat_aout_88K()173 static void Test_FileLoader_DetectFileFormat_aout_88K()
174 {
175 	FileLoader fileLoader("test/FileLoader_A.OUT_M88K");
176 	refcount_ptr<const FileLoaderImpl> loaderImpl;
177 	UnitTest::Assert("file format detection failure?",
178 	    fileLoader.DetectFileFormat(loaderImpl), "a.out_M88K_fromBeginning");
179 }
180 
Test_FileLoader_DetectFileFormat_bout_i960()181 static void Test_FileLoader_DetectFileFormat_bout_i960()
182 {
183 	FileLoader fileLoader("test/FileLoader_B.OUT_i960");
184 	refcount_ptr<const FileLoaderImpl> loaderImpl;
185 	UnitTest::Assert("file format detection failure?",
186 	    fileLoader.DetectFileFormat(loaderImpl), "b.out_i960_little");
187 }
188 
SetupTestMachineAndLoad(string machineName,string fileName,bool ramAtZero)189 static refcount_ptr<Component> SetupTestMachineAndLoad(
190 	string machineName, string fileName, bool ramAtZero)
191 {
192 	FileLoader fileLoader(fileName);
193 	refcount_ptr<Component> machine =
194 	    ComponentFactory::CreateComponent(machineName);
195 	UnitTest::Assert("could not add machine type?", !machine.IsNULL());
196 
197 	machine->SetVariableValue("name", "\"machine\"");
198 	refcount_ptr<Component> component =
199 	    machine->LookupPath("machine.mainbus0.cpu0");
200 	UnitTest::Assert("could not look up CPU to load into?",
201 	    !component.IsNULL());
202 
203 	refcount_ptr<Component> ram = machine->LookupPath("machine.mainbus0.ram0");
204 	UnitTest::Assert("could not look up RAM?", !ram.IsNULL());
205 	if (ramAtZero)
206 		ram->SetVariableValue("memoryMappedBase", "0");
207 
208 	stringstream messages;
209 	UnitTest::Assert("could not load the file " + fileName + " for"
210 	    " machine " + machineName, fileLoader.Load(component, messages));
211 
212 	return machine;
213 }
214 
215 #if 0
216 static void Test_FileLoader_Load_ELF32()
217 {
218 	// Yes, it is odd to load a MIPS binary into a RISC-V machine, but still...
219 	refcount_ptr<Component> machine =
220 	    SetupTestMachineAndLoad("riscv-virt", "test/FileLoader_ELF_MIPS", false);
221 
222 	// Read from CPU, to make sure the file was loaded:
223 	refcount_ptr<Component> cpu =
224 	    machine->LookupPath("machine.mainbus0.cpu0");
225 	AddressDataBus * bus = cpu->AsAddressDataBus();
226 	bus->AddressSelect((int32_t)0x00010000);
227 	uint32_t word = 0x12345678;
228 	bus->ReadData(word, BigEndian);
229 	UnitTest::Assert("memory (CPU) wasn't filled with data from the file?",
230 	    word, 0x8f8b8008);
231 
232 	// Read directly from RAM too, to make sure the file was loaded:
233 	refcount_ptr<Component> ram =
234 	    machine->LookupPath("machine.mainbus0.ram0");
235 	AddressDataBus * ramBus = ram->AsAddressDataBus();
236 	ramBus->AddressSelect(0x1000c);
237 	uint32_t word2 = 0x12345678;
238 	ramBus->ReadData(word2, BigEndian);
239 	UnitTest::Assert("memory (RAM) wasn't filled with data from the file?",
240 	    word2, 0x006b7021);
241 }
242 #endif
243 
Test_FileLoader_Load_ELF64()244 static void Test_FileLoader_Load_ELF64()
245 {
246 	refcount_ptr<Component> machine =
247 	    SetupTestMachineAndLoad("riscv-virt", "test/FileLoader_ELF_RISCV64", true);
248 
249 	// Read from CPU, to make sure the file was loaded:
250 	refcount_ptr<Component> cpu =
251 	    machine->LookupPath("machine.mainbus0.cpu0");
252 	AddressDataBus * bus = cpu->AsAddressDataBus();
253 	bus->AddressSelect((int32_t)0x00010078);
254 	uint32_t word = 0x12345678;
255 	bus->ReadData(word, BigEndian);
256 	UnitTest::Assert("memory (CPU) wasn't filled with data from the file?",
257 	    word, 0x011122ec);
258 
259 	// Read directly from RAM too, to make sure the file was loaded:
260 	refcount_ptr<Component> ram =
261 	    machine->LookupPath("machine.mainbus0.ram0");
262 	AddressDataBus * ramBus = ram->AsAddressDataBus();
263 	ramBus->AddressSelect(0x1007c);
264 	uint32_t word2 = 0x12345678;
265 	ramBus->ReadData(word2, BigEndian);
266 	UnitTest::Assert("memory (RAM) wasn't filled with data from the file?",
267 	    word2, 0x0010aa87);
268 }
269 
Test_FileLoader_Load_aout()270 static void Test_FileLoader_Load_aout()
271 {
272 	refcount_ptr<Component> machine =
273 	    SetupTestMachineAndLoad("testm88k", "test/FileLoader_A.OUT_M88K", true);
274 
275 	// Read from CPU, to make sure the file was loaded:
276 	refcount_ptr<Component> cpu =
277 	    machine->LookupPath("machine.mainbus0.cpu0");
278 	AddressDataBus * bus = cpu->AsAddressDataBus();
279 	bus->AddressSelect((int32_t)0x12b8);
280 	uint32_t word = 0x12345678;
281 	bus->ReadData(word, BigEndian);
282 	UnitTest::Assert("memory (CPU) wasn't filled with data from the file?",
283 	    word, 0x67ff0020);
284 
285 	// Read directly from RAM too, to make sure the file was loaded:
286 	refcount_ptr<Component> ram =
287 	    machine->LookupPath("machine.mainbus0.ram0");
288 	AddressDataBus * ramBus = ram->AsAddressDataBus();
289 	ramBus->AddressSelect(0x12e0);
290 	uint32_t word2 = 0xfdecba98;
291 	ramBus->ReadData(word2, BigEndian);
292 	UnitTest::Assert("memory (RAM) wasn't filled with data from the file?",
293 	    word2, 0xf6c05802);
294 }
295 
UNITTESTS(FileLoader)296 UNITTESTS(FileLoader)
297 {
298 	UNITTEST(Test_FileLoader_Constructor);
299 	UNITTEST(Test_FileLoader_Constructor_NonExistingFile);
300 
301 	UNITTEST(Test_FileLoader_DetectFileFormat_NonexistingFile);
302 	UNITTEST(Test_FileLoader_DetectFileFormat_NonsenseFile);
303 	UNITTEST(Test_FileLoader_DetectFileFormat_ELF32);
304 	UNITTEST(Test_FileLoader_DetectFileFormat_ELF64);
305 	UNITTEST(Test_FileLoader_DetectFileFormat_aout_88K);
306 	UNITTEST(Test_FileLoader_DetectFileFormat_bout_i960);
307 
308 	// UNITTEST(Test_FileLoader_Load_ELF32);
309 	UNITTEST(Test_FileLoader_Load_ELF64);
310 	UNITTEST(Test_FileLoader_Load_aout);
311 }
312 
313 #endif
314