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