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