disk_image.cc revision 412
12139SN/A/*
22139SN/A * Copyright (c) 2003 The Regents of The University of Michigan
32139SN/A * All rights reserved.
42139SN/A *
52139SN/A * Redistribution and use in source and binary forms, with or without
62139SN/A * modification, are permitted provided that the following conditions are
72139SN/A * met: redistributions of source code must retain the above copyright
82139SN/A * notice, this list of conditions and the following disclaimer;
92139SN/A * redistributions in binary form must reproduce the above copyright
102139SN/A * notice, this list of conditions and the following disclaimer in the
112139SN/A * documentation and/or other materials provided with the distribution;
122139SN/A * neither the name of the copyright holders nor the names of its
132139SN/A * contributors may be used to endorse or promote products derived from
142139SN/A * this software without specific prior written permission.
152139SN/A *
162139SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
172139SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
182139SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
192139SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
202139SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
212139SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
222139SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232139SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242139SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252139SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
262139SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272139SN/A */
282665Ssaidi@eecs.umich.edu
292665Ssaidi@eecs.umich.edu/* @file
302139SN/A * Disk Image Definitions
312718Sstever@eecs.umich.edu */
322139SN/A
332139SN/A#include <sys/types.h>
342139SN/A#include <sys/uio.h>
352139SN/A#include <errno.h>
362152SN/A#include <unistd.h>
372152SN/A
382152SN/A#include <cstdio>
392152SN/A#include <cstring>
402139SN/A#include <fstream>
412139SN/A#include <string>
422139SN/A
432139SN/A#include "base/callback.hh"
442139SN/A#include "base/misc.hh"
452152SN/A#include "base/trace.hh"
462152SN/A#include "dev/disk_image.hh"
472139SN/A#include "sim/builder.hh"
482139SN/A#include "sim/sim_exit.hh"
492139SN/A
502984Sgblack@eecs.umich.eduusing namespace std;
512439SN/A
523520Sgblack@eecs.umich.edu////////////////////////////////////////////////////////////////////////
532139SN/A//
543170Sstever@eecs.umich.edu// Raw Disk image
552439SN/A//
562460SN/ARawDiskImage::RawDiskImage(const string &name, const string &filename,
572439SN/A                           bool rd_only)
582972Sgblack@eecs.umich.edu    : DiskImage(name), disk_size(0)
592171SN/A{ open(filename, rd_only); }
602439SN/A
612439SN/ARawDiskImage::~RawDiskImage()
622170SN/A{ close(); }
632139SN/A
642139SN/Avoid
652139SN/ARawDiskImage::open(const string &filename, bool rd_only)
662139SN/A{
672139SN/A    if (!filename.empty()) {
682139SN/A        initialized = true;
692139SN/A        readonly = rd_only;
702139SN/A        file = filename;
712139SN/A
722139SN/A        ios::openmode mode = ios::in | ios::binary;
732139SN/A        if (!readonly)
742139SN/A            mode |= ios::out;
752139SN/A        stream.open(file.c_str(), mode);
762139SN/A        if (!stream.is_open())
772139SN/A            panic("Error opening %s", filename);
782139SN/A    }
792139SN/A}
802139SN/A
812139SN/Avoid
822139SN/ARawDiskImage::close()
832139SN/A{
842139SN/A    stream.close();
852139SN/A}
862139SN/A
872178SN/Aoff_t
882139SN/ARawDiskImage::size() const
892139SN/A{
902139SN/A    if (disk_size == 0) {
912139SN/A        if (!stream.is_open())
922139SN/A            panic("file not open!\n");
932139SN/A        stream.seekg(0, ios::end);
942139SN/A        disk_size = stream.tellg();
952152SN/A    }
962152SN/A
972152SN/A    return disk_size / SectorSize;
982152SN/A}
992152SN/A
1002152SN/Aoff_t
1012152SN/ARawDiskImage::read(uint8_t *data, off_t offset) const
1022152SN/A{
1032152SN/A    if (!initialized)
1042152SN/A        panic("RawDiskImage not initialized");
1052152SN/A
1062152SN/A    if (!stream.is_open())
1072504SN/A        panic("file not open!\n");
1082504SN/A
1092504SN/A    if (stream.seekg(offset * SectorSize, ios::beg) < 0)
1102504SN/A        panic("Could not seek to location in file");
1112152SN/A
1122504SN/A    streampos pos = stream.tellg();
1132152SN/A    stream.read((char *)data, SectorSize);
1142152SN/A
1152152SN/A    DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
1162152SN/A    DDUMP(DiskImageRead, data, SectorSize);
1172152SN/A
1182152SN/A    return stream.tellg() - pos;
1192152SN/A}
1202152SN/A
1212632Sstever@eecs.umich.eduoff_t
1222155SN/ARawDiskImage::write(const uint8_t *data, off_t offset)
1232155SN/A{
1242155SN/A    if (!initialized)
1252155SN/A        panic("RawDiskImage not initialized");
1262155SN/A
1272155SN/A    if (readonly)
1282155SN/A        panic("Cannot write to a read only disk image");
1292155SN/A
1302155SN/A    if (!stream.is_open())
1312155SN/A        panic("file not open!\n");
1322152SN/A
1332766Sktlim@umich.edu    if (stream.seekp(offset * SectorSize, ios::beg) < 0)
1342766Sktlim@umich.edu        panic("Could not seek to location in file");
1352766Sktlim@umich.edu
1362766Sktlim@umich.edu    DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
1372766Sktlim@umich.edu    DDUMP(DiskImageWrite, data, SectorSize);
1382152SN/A
1392152SN/A    streampos pos = stream.tellp();
1402152SN/A    stream.write((const char *)data, SectorSize);
1412155SN/A    return stream.tellp() - pos;
1422152SN/A}
1432152SN/A
1442718Sstever@eecs.umich.eduDEFINE_SIM_OBJECT_CLASS_NAME("DiskImage", DiskImage)
1452921Sktlim@umich.edu
1462921Sktlim@umich.eduBEGIN_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage)
1472921Sktlim@umich.edu
1482921Sktlim@umich.edu    Param<string> image_file;
1492921Sktlim@umich.edu    Param<bool> read_only;
1502921Sktlim@umich.edu
1512921Sktlim@umich.eduEND_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage)
1522921Sktlim@umich.edu
1532921Sktlim@umich.eduBEGIN_INIT_SIM_OBJECT_PARAMS(RawDiskImage)
1542152SN/A
1552152SN/A    INIT_PARAM(image_file, "disk image file"),
1562152SN/A    INIT_PARAM_DFLT(read_only, "read only image", false)
1572152SN/A
1582152SN/AEND_INIT_SIM_OBJECT_PARAMS(RawDiskImage)
1592152SN/A
1602152SN/A
1612152SN/ACREATE_SIM_OBJECT(RawDiskImage)
1622152SN/A{
1632152SN/A    return new RawDiskImage(getInstanceName(), image_file, read_only);
1642667Sstever@eecs.umich.edu}
1652152SN/A
1662152SN/AREGISTER_SIM_OBJECT("RawDiskImage", RawDiskImage)
167
168////////////////////////////////////////////////////////////////////////
169//
170// Copy on Write Disk image
171//
172const int CowDiskImage::VersionMajor = 1;
173const int CowDiskImage::VersionMinor = 0;
174
175CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size)
176    : DiskImage(name), child(kid), table(NULL)
177{ init(hash_size); }
178
179class CowDiskCallback : public Callback
180{
181  private:
182    CowDiskImage *image;
183
184  public:
185    CowDiskCallback(CowDiskImage *i) : image(i) {}
186    void process() { image->save(); delete this; }
187};
188
189CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size,
190                           const string &file, bool read_only)
191    : DiskImage(name), filename(file), child(kid), table(NULL)
192{
193    if (!open(filename)) {
194        assert(!read_only && "why have a non-existent read only file?");
195        init(hash_size);
196    }
197
198    if (!read_only)
199        registerExitCallback(new CowDiskCallback(this));
200}
201
202CowDiskImage::~CowDiskImage()
203{
204    SectorTable::iterator i = table->begin();
205    SectorTable::iterator end = table->end();
206
207    while (i != end) {
208        delete (*i).second;
209        ++i;
210    }
211}
212
213void
214SafeRead(ifstream &stream, void *data, int count)
215{
216    stream.read((char *)data, count);
217    if (!stream.is_open())
218        panic("file not open");
219
220    if (stream.eof())
221        panic("premature end-of-file");
222
223    if (stream.bad() || stream.fail())
224        panic("error reading cowdisk image");
225}
226
227template<class T>
228void
229SafeRead(ifstream &stream, T &data)
230{ SafeRead(stream, &data, sizeof(data)); }
231
232bool
233CowDiskImage::open(const string &file)
234{
235    ifstream stream(file.c_str());
236    if (!stream.is_open())
237        return false;
238
239    if (stream.fail() || stream.bad())
240        panic("Error opening %s", file);
241
242    uint64_t magic;
243    SafeRead(stream, magic);
244
245    if (memcmp(&magic, "COWDISK!", sizeof(magic)) != 0)
246        panic("Could not open %s: Invalid magic", file);
247
248    uint32_t major, minor;
249    SafeRead(stream, major);
250    SafeRead(stream, minor);
251
252    if (major != VersionMajor && minor != VersionMinor)
253        panic("Could not open %s: invalid version %d.%d != %d.%d",
254              file, major, minor, VersionMajor, VersionMinor);
255
256    uint64_t sector_count;
257    SafeRead(stream, sector_count);
258    table = new SectorTable(sector_count);
259
260
261    for (uint64_t i = 0; i < sector_count; i++) {
262        uint64_t offset;
263        SafeRead(stream, offset);
264
265        Sector *sector = new Sector;
266        SafeRead(stream, sector, sizeof(Sector));
267
268        assert(table->find(offset) == table->end());
269        (*table)[offset] = sector;
270    }
271
272    stream.close();
273
274    initialized = true;
275    return true;
276}
277
278void
279CowDiskImage::init(int hash_size)
280{
281    table = new SectorTable(hash_size);
282
283    initialized = true;
284}
285
286void
287SafeWrite(ofstream &stream, const void *data, int count)
288{
289    stream.write((const char *)data, count);
290    if (!stream.is_open())
291        panic("file not open");
292
293    if (stream.eof())
294        panic("premature end-of-file");
295
296    if (stream.bad() || stream.fail())
297        panic("error reading cowdisk image");
298}
299
300template<class T>
301void
302SafeWrite(ofstream &stream, const T &data)
303{ SafeWrite(stream, &data, sizeof(data)); }
304
305void
306CowDiskImage::save()
307{
308    save(filename);
309}
310
311void
312CowDiskImage::save(const string &file)
313{
314    if (!initialized)
315        panic("RawDiskImage not initialized");
316
317    ofstream stream(file.c_str());
318    if (!stream.is_open() || stream.fail() || stream.bad())
319        panic("Error opening %s", file);
320
321    uint64_t magic;
322    memcpy(&magic, "COWDISK!", sizeof(magic));
323    SafeWrite(stream, magic);
324
325    SafeWrite(stream, (uint32_t)VersionMajor);
326    SafeWrite(stream, (uint32_t)VersionMinor);
327    SafeWrite(stream, (uint64_t)table->size());
328
329    uint64_t size = table->size();
330    SectorTable::iterator iter = table->begin();
331    SectorTable::iterator end = table->end();
332
333    for (uint64_t i = 0; i < size; i++) {
334        if (iter == end)
335            panic("Incorrect Table Size during save of COW disk image");
336
337        SafeWrite(stream, (uint64_t)(*iter).first);
338        SafeWrite(stream, (*iter).second->data, sizeof(Sector));
339        ++iter;
340    }
341
342    stream.close();
343}
344
345void
346CowDiskImage::writeback()
347{
348    SectorTable::iterator i = table->begin();
349    SectorTable::iterator end = table->end();
350
351    while (i != end) {
352        child->write((*i).second->data, (*i).first);
353        ++i;
354    }
355}
356
357off_t
358CowDiskImage::size() const
359{ return child->size(); }
360
361off_t
362CowDiskImage::read(uint8_t *data, off_t offset) const
363{
364    if (!initialized)
365        panic("CowDiskImage not initialized");
366
367    if (offset > size())
368        panic("access out of bounds");
369
370    SectorTable::const_iterator i = table->find(offset);
371    if (i == table->end())
372        return child->read(data, offset);
373    else {
374        memcpy(data, (*i).second->data, SectorSize);
375        DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
376        DDUMP(DiskImageRead, data, SectorSize);
377        return SectorSize;
378    }
379}
380
381off_t
382CowDiskImage::write(const uint8_t *data, off_t offset)
383{
384    if (!initialized)
385        panic("RawDiskImage not initialized");
386
387    if (offset > size())
388        panic("access out of bounds");
389
390    SectorTable::iterator i = table->find(offset);
391    if (i == table->end()) {
392        Sector *sector = new Sector;
393        memcpy(sector, data, SectorSize);
394        table->insert(make_pair(offset, sector));
395    } else {
396        memcpy((*i).second->data, data, SectorSize);
397    }
398
399    DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
400    DDUMP(DiskImageWrite, data, SectorSize);
401
402    return SectorSize;
403}
404
405void
406CowDiskImage::serialize(ostream &os)
407{
408    string cowFilename = CheckpointDir() + name() + ".cow";
409    SERIALIZE_SCALAR(cowFilename);
410    save(cowFilename);
411}
412
413void
414CowDiskImage::unserialize(Checkpoint *cp, const string &section)
415{
416    string cowFilename;
417    UNSERIALIZE_SCALAR(cowFilename);
418    open(cowFilename);
419}
420
421BEGIN_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage)
422
423    SimObjectParam<DiskImage *> child;
424    Param<string> image_file;
425    Param<int> table_size;
426    Param<bool> read_only;
427
428END_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage)
429
430BEGIN_INIT_SIM_OBJECT_PARAMS(CowDiskImage)
431
432    INIT_PARAM(child, "child image"),
433    INIT_PARAM_DFLT(image_file, "disk image file", ""),
434    INIT_PARAM_DFLT(table_size, "initial table size", 65536),
435    INIT_PARAM_DFLT(read_only, "don't write back to the copy-on-write file",
436                    true)
437
438END_INIT_SIM_OBJECT_PARAMS(CowDiskImage)
439
440
441CREATE_SIM_OBJECT(CowDiskImage)
442{
443    if (((string)image_file).empty())
444        return new CowDiskImage(getInstanceName(), child, table_size);
445    else
446        return new CowDiskImage(getInstanceName(), child, table_size,
447                                image_file, read_only);
448}
449
450REGISTER_SIM_OBJECT("CowDiskImage", CowDiskImage)
451