Merge ktlim@zamp:/z/ktlim2/clean/m5-o3
[gem5.git] / src / dev / disk_image.cc
1 /*
2 * Copyright (c) 2001-2005 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 * Authors: Nathan Binkert
29 */
30
31 /** @file
32 * Disk Image Definitions
33 */
34
35 #include <sys/types.h>
36 #include <sys/uio.h>
37 #include <errno.h>
38 #include <unistd.h>
39
40 #include <cstring>
41 #include <fstream>
42 #include <string>
43
44 #include "base/callback.hh"
45 #include "base/misc.hh"
46 #include "base/trace.hh"
47 #include "dev/disk_image.hh"
48 #include "sim/builder.hh"
49 #include "sim/sim_exit.hh"
50 #include "sim/byteswap.hh"
51
52 using namespace std;
53
54 ////////////////////////////////////////////////////////////////////////
55 //
56 // Raw Disk image
57 //
58 RawDiskImage::RawDiskImage(const string &name, const string &filename,
59 bool rd_only)
60 : DiskImage(name), disk_size(0)
61 { open(filename, rd_only); }
62
63 RawDiskImage::~RawDiskImage()
64 { close(); }
65
66 void
67 RawDiskImage::open(const string &filename, bool rd_only)
68 {
69 if (!filename.empty()) {
70 initialized = true;
71 readonly = rd_only;
72 file = filename;
73
74 ios::openmode mode = ios::in | ios::binary;
75 if (!readonly)
76 mode |= ios::out;
77 stream.open(file.c_str(), mode);
78 if (!stream.is_open())
79 panic("Error opening %s", filename);
80 }
81 }
82
83 void
84 RawDiskImage::close()
85 {
86 stream.close();
87 }
88
89 off_t
90 RawDiskImage::size() const
91 {
92 if (disk_size == 0) {
93 if (!stream.is_open())
94 panic("file not open!\n");
95 stream.seekg(0, ios::end);
96 disk_size = stream.tellg();
97 }
98
99 return disk_size / SectorSize;
100 }
101
102 off_t
103 RawDiskImage::read(uint8_t *data, off_t offset) const
104 {
105 if (!initialized)
106 panic("RawDiskImage not initialized");
107
108 if (!stream.is_open())
109 panic("file not open!\n");
110
111 if (stream.seekg(offset * SectorSize, ios::beg) < 0)
112 panic("Could not seek to location in file");
113
114 streampos pos = stream.tellg();
115 stream.read((char *)data, SectorSize);
116
117 DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
118 DDUMP(DiskImageRead, data, SectorSize);
119
120 return stream.tellg() - pos;
121 }
122
123 off_t
124 RawDiskImage::write(const uint8_t *data, off_t offset)
125 {
126 if (!initialized)
127 panic("RawDiskImage not initialized");
128
129 if (readonly)
130 panic("Cannot write to a read only disk image");
131
132 if (!stream.is_open())
133 panic("file not open!\n");
134
135 if (stream.seekp(offset * SectorSize, ios::beg) < 0)
136 panic("Could not seek to location in file");
137
138 DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
139 DDUMP(DiskImageWrite, data, SectorSize);
140
141 streampos pos = stream.tellp();
142 stream.write((const char *)data, SectorSize);
143 return stream.tellp() - pos;
144 }
145
146 DEFINE_SIM_OBJECT_CLASS_NAME("DiskImage", DiskImage)
147
148 BEGIN_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage)
149
150 Param<string> image_file;
151 Param<bool> read_only;
152
153 END_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage)
154
155 BEGIN_INIT_SIM_OBJECT_PARAMS(RawDiskImage)
156
157 INIT_PARAM(image_file, "disk image file"),
158 INIT_PARAM_DFLT(read_only, "read only image", false)
159
160 END_INIT_SIM_OBJECT_PARAMS(RawDiskImage)
161
162
163 CREATE_SIM_OBJECT(RawDiskImage)
164 {
165 return new RawDiskImage(getInstanceName(), image_file, read_only);
166 }
167
168 REGISTER_SIM_OBJECT("RawDiskImage", RawDiskImage)
169
170 ////////////////////////////////////////////////////////////////////////
171 //
172 // Copy on Write Disk image
173 //
174 const int CowDiskImage::VersionMajor = 1;
175 const int CowDiskImage::VersionMinor = 0;
176
177 CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size)
178 : DiskImage(name), child(kid), table(NULL)
179 { init(hash_size); }
180
181 class CowDiskCallback : public Callback
182 {
183 private:
184 CowDiskImage *image;
185
186 public:
187 CowDiskCallback(CowDiskImage *i) : image(i) {}
188 void process() { image->save(); delete this; }
189 };
190
191 CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size,
192 const string &file, bool read_only)
193 : DiskImage(name), filename(file), child(kid), table(NULL)
194 {
195 if (!open(filename)) {
196 assert(!read_only && "why have a non-existent read only file?");
197 init(hash_size);
198 }
199
200 if (!read_only)
201 registerExitCallback(new CowDiskCallback(this));
202 }
203
204 CowDiskImage::~CowDiskImage()
205 {
206 SectorTable::iterator i = table->begin();
207 SectorTable::iterator end = table->end();
208
209 while (i != end) {
210 delete (*i).second;
211 ++i;
212 }
213 }
214
215 void
216 SafeRead(ifstream &stream, void *data, int count)
217 {
218 stream.read((char *)data, count);
219 if (!stream.is_open())
220 panic("file not open");
221
222 if (stream.eof())
223 panic("premature end-of-file");
224
225 if (stream.bad() || stream.fail())
226 panic("error reading cowdisk image");
227 }
228
229 template<class T>
230 void
231 SafeRead(ifstream &stream, T &data)
232 {
233 SafeRead(stream, &data, sizeof(data));
234 }
235
236 template<class T>
237 void
238 SafeReadSwap(ifstream &stream, T &data)
239 {
240 SafeRead(stream, &data, sizeof(data));
241 data = letoh(data); //is this the proper byte order conversion?
242 }
243
244 bool
245 CowDiskImage::open(const string &file)
246 {
247 ifstream stream(file.c_str());
248 if (!stream.is_open())
249 return false;
250
251 if (stream.fail() || stream.bad())
252 panic("Error opening %s", file);
253
254 uint64_t magic;
255 SafeRead(stream, magic);
256
257 if (memcmp(&magic, "COWDISK!", sizeof(magic)) != 0)
258 panic("Could not open %s: Invalid magic", file);
259
260 uint32_t major, minor;
261 SafeReadSwap(stream, major);
262 SafeReadSwap(stream, minor);
263
264 if (major != VersionMajor && minor != VersionMinor)
265 panic("Could not open %s: invalid version %d.%d != %d.%d",
266 file, major, minor, VersionMajor, VersionMinor);
267
268 uint64_t sector_count;
269 SafeReadSwap(stream, sector_count);
270 table = new SectorTable(sector_count);
271
272
273 for (uint64_t i = 0; i < sector_count; i++) {
274 uint64_t offset;
275 SafeReadSwap(stream, offset);
276
277 Sector *sector = new Sector;
278 SafeRead(stream, sector, sizeof(Sector));
279
280 assert(table->find(offset) == table->end());
281 (*table)[offset] = sector;
282 }
283
284 stream.close();
285
286 initialized = true;
287 return true;
288 }
289
290 void
291 CowDiskImage::init(int hash_size)
292 {
293 table = new SectorTable(hash_size);
294
295 initialized = true;
296 }
297
298 void
299 SafeWrite(ofstream &stream, const void *data, int count)
300 {
301 stream.write((const char *)data, count);
302 if (!stream.is_open())
303 panic("file not open");
304
305 if (stream.eof())
306 panic("premature end-of-file");
307
308 if (stream.bad() || stream.fail())
309 panic("error reading cowdisk image");
310 }
311
312 template<class T>
313 void
314 SafeWrite(ofstream &stream, const T &data)
315 {
316 SafeWrite(stream, &data, sizeof(data));
317 }
318
319 template<class T>
320 void
321 SafeWriteSwap(ofstream &stream, const T &data)
322 {
323 T swappeddata = letoh(data); //is this the proper byte order conversion?
324 SafeWrite(stream, &swappeddata, sizeof(data));
325 }
326 void
327 CowDiskImage::save()
328 {
329 save(filename);
330 }
331
332 void
333 CowDiskImage::save(const string &file)
334 {
335 if (!initialized)
336 panic("RawDiskImage not initialized");
337
338 ofstream stream(file.c_str());
339 if (!stream.is_open() || stream.fail() || stream.bad())
340 panic("Error opening %s", file);
341
342 uint64_t magic;
343 memcpy(&magic, "COWDISK!", sizeof(magic));
344 SafeWrite(stream, magic);
345
346 SafeWriteSwap(stream, (uint32_t)VersionMajor);
347 SafeWriteSwap(stream, (uint32_t)VersionMinor);
348 SafeWriteSwap(stream, (uint64_t)table->size());
349
350 uint64_t size = table->size();
351 SectorTable::iterator iter = table->begin();
352 SectorTable::iterator end = table->end();
353
354 for (uint64_t i = 0; i < size; i++) {
355 if (iter == end)
356 panic("Incorrect Table Size during save of COW disk image");
357
358 SafeWriteSwap(stream, (uint64_t)(*iter).first);
359 SafeWrite(stream, (*iter).second->data, sizeof(Sector));
360 ++iter;
361 }
362
363 stream.close();
364 }
365
366 void
367 CowDiskImage::writeback()
368 {
369 SectorTable::iterator i = table->begin();
370 SectorTable::iterator end = table->end();
371
372 while (i != end) {
373 child->write((*i).second->data, (*i).first);
374 ++i;
375 }
376 }
377
378 off_t
379 CowDiskImage::size() const
380 { return child->size(); }
381
382 off_t
383 CowDiskImage::read(uint8_t *data, off_t offset) const
384 {
385 if (!initialized)
386 panic("CowDiskImage not initialized");
387
388 if (offset > size())
389 panic("access out of bounds");
390
391 SectorTable::const_iterator i = table->find(offset);
392 if (i == table->end())
393 return child->read(data, offset);
394 else {
395 memcpy(data, (*i).second->data, SectorSize);
396 DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
397 DDUMP(DiskImageRead, data, SectorSize);
398 return SectorSize;
399 }
400 }
401
402 off_t
403 CowDiskImage::write(const uint8_t *data, off_t offset)
404 {
405 if (!initialized)
406 panic("RawDiskImage not initialized");
407
408 if (offset > size())
409 panic("access out of bounds");
410
411 SectorTable::iterator i = table->find(offset);
412 if (i == table->end()) {
413 Sector *sector = new Sector;
414 memcpy(sector, data, SectorSize);
415 table->insert(make_pair(offset, sector));
416 } else {
417 memcpy((*i).second->data, data, SectorSize);
418 }
419
420 DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
421 DDUMP(DiskImageWrite, data, SectorSize);
422
423 return SectorSize;
424 }
425
426 void
427 CowDiskImage::serialize(ostream &os)
428 {
429 string cowFilename = name() + ".cow";
430 SERIALIZE_SCALAR(cowFilename);
431 save(Checkpoint::dir() + "/" + cowFilename);
432 }
433
434 void
435 CowDiskImage::unserialize(Checkpoint *cp, const string &section)
436 {
437 string cowFilename;
438 UNSERIALIZE_SCALAR(cowFilename);
439 cowFilename = cp->cptDir + "/" + cowFilename;
440 open(cowFilename);
441 }
442
443 BEGIN_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage)
444
445 SimObjectParam<DiskImage *> child;
446 Param<string> image_file;
447 Param<int> table_size;
448 Param<bool> read_only;
449
450 END_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage)
451
452 BEGIN_INIT_SIM_OBJECT_PARAMS(CowDiskImage)
453
454 INIT_PARAM(child, "child image"),
455 INIT_PARAM_DFLT(image_file, "disk image file", ""),
456 INIT_PARAM_DFLT(table_size, "initial table size", 65536),
457 INIT_PARAM_DFLT(read_only, "don't write back to the copy-on-write file",
458 true)
459
460 END_INIT_SIM_OBJECT_PARAMS(CowDiskImage)
461
462
463 CREATE_SIM_OBJECT(CowDiskImage)
464 {
465 if (((string)image_file).empty())
466 return new CowDiskImage(getInstanceName(), child, table_size);
467 else
468 return new CowDiskImage(getInstanceName(), child, table_size,
469 image_file, read_only);
470 }
471
472 REGISTER_SIM_OBJECT("CowDiskImage", CowDiskImage)