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