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