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