disk_image.cc revision 11264:dc389d2d2f79
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
54using namespace std;
55
56////////////////////////////////////////////////////////////////////////
57//
58// Raw Disk image
59//
60RawDiskImage::RawDiskImage(const Params* p)
61    : DiskImage(p), disk_size(0)
62{ open(p->image_file, p->read_only); }
63
64RawDiskImage::~RawDiskImage()
65{ close(); }
66
67void
68RawDiskImage::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
84void
85RawDiskImage::close()
86{
87    stream.close();
88}
89
90std::streampos
91RawDiskImage::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
103std::streampos
104RawDiskImage::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
125std::streampos
126RawDiskImage::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
149RawDiskImage *
150RawDiskImageParams::create()
151{
152    return new RawDiskImage(this);
153}
154
155////////////////////////////////////////////////////////////////////////
156//
157// Copy on Write Disk image
158//
159const uint32_t CowDiskImage::VersionMajor = 1;
160const uint32_t CowDiskImage::VersionMinor = 0;
161
162class 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
172CowDiskImage::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
189CowDiskImage::~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
200void
201SafeRead(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
214template<class T>
215void
216SafeRead(ifstream &stream, T &data)
217{
218    SafeRead(stream, &data, sizeof(data));
219}
220
221template<class T>
222void
223SafeReadSwap(ifstream &stream, T &data)
224{
225    SafeRead(stream, &data, sizeof(data));
226    data = letoh(data); //is this the proper byte order conversion?
227}
228
229bool
230CowDiskImage::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
275void
276CowDiskImage::initSectorTable(int hash_size)
277{
278    table = new SectorTable(hash_size);
279
280    initialized = true;
281}
282
283void
284SafeWrite(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
297template<class T>
298void
299SafeWrite(ofstream &stream, const T &data)
300{
301    SafeWrite(stream, &data, sizeof(data));
302}
303
304template<class T>
305void
306SafeWriteSwap(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}
311void
312CowDiskImage::save() const
313{
314    save(filename);
315}
316
317void
318CowDiskImage::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
351void
352CowDiskImage::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
363std::streampos
364CowDiskImage::size() const
365{ return child->size(); }
366
367std::streampos
368CowDiskImage::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
387std::streampos
388CowDiskImage::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
411void
412CowDiskImage::serialize(CheckpointOut &cp) const
413{
414    string cowFilename = name() + ".cow";
415    SERIALIZE_SCALAR(cowFilename);
416    save(CheckpointIn::dir() + "/" + cowFilename);
417}
418
419void
420CowDiskImage::unserialize(CheckpointIn &cp)
421{
422    string cowFilename;
423    UNSERIALIZE_SCALAR(cowFilename);
424    cowFilename = cp.cptDir + "/" + cowFilename;
425    open(cowFilename);
426}
427
428CowDiskImage *
429CowDiskImageParams::create()
430{
431    return new CowDiskImage(this);
432}
433