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