disk_image.cc revision 56
14166Sgblack@eecs.umich.edu/*
24166Sgblack@eecs.umich.edu * Copyright (c) 2003 The Regents of The University of Michigan
34166Sgblack@eecs.umich.edu * All rights reserved.
44166Sgblack@eecs.umich.edu *
57087Snate@binkert.org * Redistribution and use in source and binary forms, with or without
67087Snate@binkert.org * modification, are permitted provided that the following conditions are
77087Snate@binkert.org * met: redistributions of source code must retain the above copyright
87087Snate@binkert.org * notice, this list of conditions and the following disclaimer;
97087Snate@binkert.org * redistributions in binary form must reproduce the above copyright
107087Snate@binkert.org * notice, this list of conditions and the following disclaimer in the
117087Snate@binkert.org * documentation and/or other materials provided with the distribution;
127087Snate@binkert.org * neither the name of the copyright holders nor the names of its
134166Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from
147087Snate@binkert.org * this software without specific prior written permission.
157087Snate@binkert.org *
167087Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
177087Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
187087Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
197087Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
207087Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
217087Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
224166Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
237087Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
244166Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
254166Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
264166Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
274166Sgblack@eecs.umich.edu */
284166Sgblack@eecs.umich.edu
294166Sgblack@eecs.umich.edu/* @file
304166Sgblack@eecs.umich.edu * Disk Image Definitions
314166Sgblack@eecs.umich.edu */
324166Sgblack@eecs.umich.edu
334166Sgblack@eecs.umich.edu#include <sys/types.h>
344166Sgblack@eecs.umich.edu#include <sys/uio.h>
354166Sgblack@eecs.umich.edu#include <errno.h>
364166Sgblack@eecs.umich.edu#include <string.h>
374166Sgblack@eecs.umich.edu#include <unistd.h>
384166Sgblack@eecs.umich.edu
394166Sgblack@eecs.umich.edu#include <cstdio>
408229Snate@binkert.org#include <fstream>
418229Snate@binkert.org#include <string>
424166Sgblack@eecs.umich.edu
434166Sgblack@eecs.umich.edu#include "dev/disk_image.hh"
444166Sgblack@eecs.umich.edu#include "base/misc.hh"
454759Sgblack@eecs.umich.edu#include "base/trace.hh"
464166Sgblack@eecs.umich.edu#include "sim/sim_exit.hh"
474166Sgblack@eecs.umich.edu#include "base/callback.hh"
484166Sgblack@eecs.umich.edu
494166Sgblack@eecs.umich.eduusing namespace std;
504166Sgblack@eecs.umich.edu
514166Sgblack@eecs.umich.edu////////////////////////////////////////////////////////////////////////
524166Sgblack@eecs.umich.edu//
534166Sgblack@eecs.umich.edu// Raw Disk image
544166Sgblack@eecs.umich.edu//
554166Sgblack@eecs.umich.eduRawDiskImage::RawDiskImage(const string &name, const string &filename,
564166Sgblack@eecs.umich.edu                           bool rd_only)
574166Sgblack@eecs.umich.edu    : DiskImage(name), disk_size(0)
584166Sgblack@eecs.umich.edu{ open(filename, rd_only); }
594166Sgblack@eecs.umich.edu
604166Sgblack@eecs.umich.eduRawDiskImage::~RawDiskImage()
614166Sgblack@eecs.umich.edu{ close(); }
624166Sgblack@eecs.umich.edu
634166Sgblack@eecs.umich.eduvoid
644166Sgblack@eecs.umich.eduRawDiskImage::open(const string &filename, bool rd_only)
654166Sgblack@eecs.umich.edu{
664166Sgblack@eecs.umich.edu    if (!filename.empty()) {
674166Sgblack@eecs.umich.edu        initialized = true;
684166Sgblack@eecs.umich.edu        readonly = rd_only;
694166Sgblack@eecs.umich.edu        file = filename;
704166Sgblack@eecs.umich.edu
714166Sgblack@eecs.umich.edu        ios::openmode mode = ios::in | ios::binary;
724166Sgblack@eecs.umich.edu        if (!readonly)
734166Sgblack@eecs.umich.edu            mode |= ios::out;
744166Sgblack@eecs.umich.edu        stream.open(file.c_str(), mode);
754166Sgblack@eecs.umich.edu        if (!stream.is_open())
764166Sgblack@eecs.umich.edu            panic("Error opening %s", filename);
774166Sgblack@eecs.umich.edu    }
784166Sgblack@eecs.umich.edu}
794759Sgblack@eecs.umich.edu
804759Sgblack@eecs.umich.eduvoid
814759Sgblack@eecs.umich.eduRawDiskImage::close()
824166Sgblack@eecs.umich.edu{
835960Sgblack@eecs.umich.edu    stream.close();
845960Sgblack@eecs.umich.edu}
855960Sgblack@eecs.umich.edu
865960Sgblack@eecs.umich.eduoff_t
875960Sgblack@eecs.umich.eduRawDiskImage::size() const
885960Sgblack@eecs.umich.edu{
895960Sgblack@eecs.umich.edu    if (disk_size == 0) {
905960Sgblack@eecs.umich.edu        if (!stream.is_open())
915960Sgblack@eecs.umich.edu            panic("file not open!\n");
925960Sgblack@eecs.umich.edu        stream.seekg(0, ios::end);
935960Sgblack@eecs.umich.edu        disk_size = stream.tellg();
945960Sgblack@eecs.umich.edu    }
955960Sgblack@eecs.umich.edu
965960Sgblack@eecs.umich.edu    return disk_size / SectorSize;
975960Sgblack@eecs.umich.edu}
985960Sgblack@eecs.umich.edu
995960Sgblack@eecs.umich.eduoff_t
1005960Sgblack@eecs.umich.eduRawDiskImage::read(uint8_t *data, off_t offset) const
1015960Sgblack@eecs.umich.edu{
1025960Sgblack@eecs.umich.edu    if (!initialized)
1035960Sgblack@eecs.umich.edu        panic("RawDiskImage not initialized");
1045960Sgblack@eecs.umich.edu
1055960Sgblack@eecs.umich.edu    if (!stream.is_open())
1065960Sgblack@eecs.umich.edu        panic("file not open!\n");
1075960Sgblack@eecs.umich.edu
1085960Sgblack@eecs.umich.edu    if (stream.seekg(offset * SectorSize, ios::beg) < 0)
1095960Sgblack@eecs.umich.edu        panic("Could not seek to location in file");
1105960Sgblack@eecs.umich.edu
1115960Sgblack@eecs.umich.edu    off_t pos = stream.tellg();
1125960Sgblack@eecs.umich.edu    stream.read((char *)data, SectorSize);
1135960Sgblack@eecs.umich.edu
1145960Sgblack@eecs.umich.edu    DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
1155960Sgblack@eecs.umich.edu    DDUMP(DiskImageRead, data, SectorSize);
1165960Sgblack@eecs.umich.edu
1175960Sgblack@eecs.umich.edu    return stream.tellg() - pos;
1185960Sgblack@eecs.umich.edu}
1195960Sgblack@eecs.umich.edu
1205960Sgblack@eecs.umich.eduoff_t
1215960Sgblack@eecs.umich.eduRawDiskImage::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