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