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