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