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