Import changeset
[gem5.git] / dev / disk_image.cc
1 /*
2 * Copyright (c) 2003 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * 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 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /* @file
30 * Disk Image Definitions
31 */
32
33 #include <sys/types.h>
34 #include <sys/uio.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include <cstdio>
40 #include <fstream>
41 #include <string>
42
43 #include "disk_image.hh"
44 #include "misc.hh"
45 #include "trace.hh"
46 #include "sim_exit.hh"
47 #include "callback.hh"
48
49 using namespace std;
50
51 ////////////////////////////////////////////////////////////////////////
52 //
53 // Raw Disk image
54 //
55 RawDiskImage::RawDiskImage(const string &name, const string &filename,
56 bool rd_only)
57 : DiskImage(name), disk_size(0)
58 { open(filename, rd_only); }
59
60 RawDiskImage::~RawDiskImage()
61 { close(); }
62
63 void
64 RawDiskImage::open(const string &filename, bool rd_only)
65 {
66 if (!filename.empty()) {
67 initialized = true;
68 readonly = rd_only;
69 file = filename;
70
71 ios::openmode mode = ios::in | ios::binary;
72 if (!readonly)
73 mode |= ios::out;
74 stream.open(file.c_str(), mode);
75 if (!stream.is_open())
76 panic("Error opening %s", filename);
77 }
78 }
79
80 void
81 RawDiskImage::close()
82 {
83 stream.close();
84 }
85
86 off_t
87 RawDiskImage::size() const
88 {
89 if (disk_size == 0) {
90 if (!stream.is_open())
91 panic("file not open!\n");
92 stream.seekg(0, ios::end);
93 disk_size = stream.tellg();
94 }
95
96 return disk_size / SectorSize;
97 }
98
99 off_t
100 RawDiskImage::read(uint8_t *data, off_t offset) const
101 {
102 if (!initialized)
103 panic("RawDiskImage not initialized");
104
105 if (!stream.is_open())
106 panic("file not open!\n");
107
108 if (stream.seekg(offset * SectorSize, ios::beg) < 0)
109 panic("Could not seek to location in file");
110
111 off_t pos = stream.tellg();
112 stream.read((char *)data, SectorSize);
113
114 DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
115 DDUMP(DiskImageRead, data, SectorSize);
116
117 return stream.tellg() - pos;
118 }
119
120 off_t
121 RawDiskImage::write(const uint8_t *data, off_t offset)
122 {
123 if (!initialized)
124 panic("RawDiskImage not initialized");
125
126 if (readonly)
127 panic("Cannot write to a read only disk image");
128
129 if (!stream.is_open())
130 panic("file not open!\n");
131
132 if (stream.seekp(offset * SectorSize, ios::beg) < 0)
133 panic("Could not seek to location in file");
134
135 DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
136 DDUMP(DiskImageWrite, data, SectorSize);
137
138 off_t pos = stream.tellp();
139 stream.write((const char *)data, SectorSize);
140 return stream.tellp() - pos;
141 }
142
143 DEFINE_SIM_OBJECT_CLASS_NAME("DiskImage", DiskImage)
144
145 BEGIN_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage)
146
147 Param<string> image_file;
148 Param<bool> read_only;
149
150 END_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage)
151
152 BEGIN_INIT_SIM_OBJECT_PARAMS(RawDiskImage)
153
154 INIT_PARAM(image_file, "disk image file"),
155 INIT_PARAM_DFLT(read_only, "read only image", false)
156
157 END_INIT_SIM_OBJECT_PARAMS(RawDiskImage)
158
159
160 CREATE_SIM_OBJECT(RawDiskImage)
161 {
162 return new RawDiskImage(getInstanceName(), image_file, read_only);
163 }
164
165 REGISTER_SIM_OBJECT("RawDiskImage", RawDiskImage)
166
167 ////////////////////////////////////////////////////////////////////////
168 //
169 // Copy on Write Disk image
170 //
171 const int CowDiskImage::VersionMajor = 1;
172 const int CowDiskImage::VersionMinor = 0;
173
174 CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size)
175 : DiskImage(name), child(kid), table(NULL)
176 { init(hash_size); }
177
178 class CowDiskCallback : public Callback
179 {
180 private:
181 CowDiskImage *image;
182
183 public:
184 CowDiskCallback(CowDiskImage *i) : image(i) {}
185 void process() { image->save(); delete this; }
186 };
187
188 CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size,
189 const string &file, bool read_only)
190 : DiskImage(name), filename(file), child(kid), table(NULL)
191 {
192 if (!open()) {
193 assert(!read_only && "why have a non-existent read only file?");
194 init(hash_size);
195 }
196
197 if (!read_only)
198 registerExitCallback(new CowDiskCallback(this));
199 }
200
201 CowDiskImage::~CowDiskImage()
202 {
203 SectorTable::iterator i = table->begin();
204 SectorTable::iterator end = table->end();
205
206 while (i != end) {
207 delete (*i).second;
208 ++i;
209 }
210 }
211
212 void
213 SafeRead(ifstream &stream, void *data, int count)
214 {
215 stream.read((char *)data, count);
216 if (!stream.is_open())
217 panic("file not open");
218
219 if (stream.eof())
220 panic("premature end-of-file");
221
222 if (stream.bad() || stream.fail())
223 panic("error reading cowdisk image");
224 }
225
226 template<class T>
227 void
228 SafeRead(ifstream &stream, T &data)
229 { SafeRead(stream, &data, sizeof(data)); }
230
231 bool
232 CowDiskImage::open()
233 {
234 ifstream stream(filename.c_str());
235 if (!stream.is_open())
236 return false;
237
238 if (stream.fail() || stream.bad())
239 panic("Error opening %s", filename);
240
241 uint64_t magic;
242 SafeRead(stream, magic);
243
244 if (memcmp(&magic, "COWDISK!", sizeof(magic)) != 0)
245 panic("Could not open %s: Invalid magic", filename);
246
247 uint32_t major, minor;
248 SafeRead(stream, major);
249 SafeRead(stream, minor);
250
251 if (major != VersionMajor && minor != VersionMinor)
252 panic("Could not open %s: invalid version %d.%d != %d.%d",
253 filename, major, minor, VersionMajor, VersionMinor);
254
255 uint64_t sector_count;
256 SafeRead(stream, sector_count);
257 table = new SectorTable(sector_count);
258
259
260 for (uint64_t i = 0; i < sector_count; i++) {
261 uint64_t offset;
262 SafeRead(stream, offset);
263
264 Sector *sector = new Sector;
265 SafeRead(stream, sector, sizeof(Sector));
266
267 assert(table->find(offset) == table->end());
268 (*table)[offset] = sector;
269 }
270
271 stream.close();
272
273 initialized = true;
274 return true;
275 }
276
277 void
278 CowDiskImage::init(int hash_size)
279 {
280 table = new SectorTable(hash_size);
281
282 initialized = true;
283 }
284
285 void
286 SafeWrite(ofstream &stream, const void *data, int count)
287 {
288 stream.write((const char *)data, count);
289 if (!stream.is_open())
290 panic("file not open");
291
292 if (stream.eof())
293 panic("premature end-of-file");
294
295 if (stream.bad() || stream.fail())
296 panic("error reading cowdisk image");
297 }
298
299 template<class T>
300 void
301 SafeWrite(ofstream &stream, const T &data)
302 { SafeWrite(stream, &data, sizeof(data)); }
303
304 void
305 CowDiskImage::save()
306 {
307 if (!initialized)
308 panic("RawDiskImage not initialized");
309
310 ofstream stream(filename.c_str());
311 if (!stream.is_open() || stream.fail() || stream.bad())
312 panic("Error opening %s", filename);
313
314 uint64_t magic;
315 memcpy(&magic, "COWDISK!", sizeof(magic));
316 SafeWrite(stream, magic);
317
318 SafeWrite(stream, (uint32_t)VersionMajor);
319 SafeWrite(stream, (uint32_t)VersionMinor);
320 SafeWrite(stream, (uint64_t)table->size());
321
322 uint64_t size = table->size();
323 SectorTable::iterator iter = table->begin();
324 SectorTable::iterator end = table->end();
325
326 for (uint64_t i = 0; i < size; i++) {
327 if (iter == end)
328 panic("Incorrect Table Size during save of COW disk image");
329
330 SafeWrite(stream, (uint64_t)(*iter).first);
331 SafeWrite(stream, (*iter).second->data, sizeof(Sector));
332 ++iter;
333 }
334
335 stream.close();
336 }
337
338 void
339 CowDiskImage::writeback()
340 {
341 SectorTable::iterator i = table->begin();
342 SectorTable::iterator end = table->end();
343
344 while (i != end) {
345 child->write((*i).second->data, (*i).first);
346 ++i;
347 }
348 }
349
350 off_t
351 CowDiskImage::size() const
352 { return child->size(); }
353
354 off_t
355 CowDiskImage::read(uint8_t *data, off_t offset) const
356 {
357 if (!initialized)
358 panic("CowDiskImage not initialized");
359
360 if (offset > size())
361 panic("access out of bounds");
362
363 SectorTable::const_iterator i = table->find(offset);
364 if (i == table->end())
365 return child->read(data, offset);
366 else {
367 memcpy(data, (*i).second->data, SectorSize);
368 DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
369 DDUMP(DiskImageRead, data, SectorSize);
370 return SectorSize;
371 }
372 }
373
374 off_t
375 CowDiskImage::write(const uint8_t *data, off_t offset)
376 {
377 if (!initialized)
378 panic("RawDiskImage not initialized");
379
380 if (offset > size())
381 panic("access out of bounds");
382
383 SectorTable::iterator i = table->find(offset);
384 if (i == table->end()) {
385 Sector *sector = new Sector;
386 memcpy(sector, data, SectorSize);
387 table->insert(make_pair(offset, sector));
388 } else {
389 memcpy((*i).second->data, data, SectorSize);
390 }
391
392 DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
393 DDUMP(DiskImageWrite, data, SectorSize);
394
395 return SectorSize;
396 }
397
398 BEGIN_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage)
399
400 SimObjectParam<DiskImage *> child;
401 Param<string> image_file;
402 Param<int> table_size;
403 Param<bool> read_only;
404
405 END_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage)
406
407 BEGIN_INIT_SIM_OBJECT_PARAMS(CowDiskImage)
408
409 INIT_PARAM(child, "child image"),
410 INIT_PARAM_DFLT(image_file, "disk image file", ""),
411 INIT_PARAM_DFLT(table_size, "initial table size", 65536),
412 INIT_PARAM_DFLT(read_only, "don't write back to the copy-on-write file",
413 true)
414
415 END_INIT_SIM_OBJECT_PARAMS(CowDiskImage)
416
417
418 CREATE_SIM_OBJECT(CowDiskImage)
419 {
420 if (((string)image_file).empty())
421 return new CowDiskImage(getInstanceName(), child, table_size);
422 else
423 return new CowDiskImage(getInstanceName(), child, table_size,
424 image_file, read_only);
425 }
426
427 REGISTER_SIM_OBJECT("CowDiskImage", CowDiskImage)