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