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