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