disk_image.cc revision 146
19020Sgblack@eecs.umich.edu/*
29020Sgblack@eecs.umich.edu * Copyright (c) 2003 The Regents of The University of Michigan
39020Sgblack@eecs.umich.edu * All rights reserved.
49020Sgblack@eecs.umich.edu *
59020Sgblack@eecs.umich.edu * Redistribution and use in source and binary forms, with or without
69020Sgblack@eecs.umich.edu * modification, are permitted provided that the following conditions are
79020Sgblack@eecs.umich.edu * met: redistributions of source code must retain the above copyright
89020Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer;
99020Sgblack@eecs.umich.edu * redistributions in binary form must reproduce the above copyright
109020Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the
119020Sgblack@eecs.umich.edu * documentation and/or other materials provided with the distribution;
129020Sgblack@eecs.umich.edu * neither the name of the copyright holders nor the names of its
139020Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from
149020Sgblack@eecs.umich.edu * this software without specific prior written permission.
159020Sgblack@eecs.umich.edu *
169020Sgblack@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
179020Sgblack@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
189020Sgblack@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
199020Sgblack@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
209020Sgblack@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
219020Sgblack@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
229020Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
239020Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
249020Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
259020Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
269020Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
279020Sgblack@eecs.umich.edu */
289020Sgblack@eecs.umich.edu
299020Sgblack@eecs.umich.edu/* @file
309020Sgblack@eecs.umich.edu * Disk Image Definitions
319020Sgblack@eecs.umich.edu */
329020Sgblack@eecs.umich.edu
339020Sgblack@eecs.umich.edu#include <sys/types.h>
349023Sgblack@eecs.umich.edu#include <sys/uio.h>
359022Sgblack@eecs.umich.edu#include <errno.h>
369022Sgblack@eecs.umich.edu#include <unistd.h>
379022Sgblack@eecs.umich.edu
389023Sgblack@eecs.umich.edu#include <cstdio>
399023Sgblack@eecs.umich.edu#include <cstring>
409023Sgblack@eecs.umich.edu#include <fstream>
419020Sgblack@eecs.umich.edu#include <string>
429020Sgblack@eecs.umich.edu
439020Sgblack@eecs.umich.edu#include "base/callback.hh"
449020Sgblack@eecs.umich.edu#include "base/misc.hh"
459022Sgblack@eecs.umich.edu#include "base/trace.hh"
469022Sgblack@eecs.umich.edu#include "dev/disk_image.hh"
479022Sgblack@eecs.umich.edu#include "sim/builder.hh"
489023Sgblack@eecs.umich.edu#include "sim/sim_exit.hh"
499023Sgblack@eecs.umich.edu
509023Sgblack@eecs.umich.eduusing namespace std;
519023Sgblack@eecs.umich.edu
529023Sgblack@eecs.umich.edu////////////////////////////////////////////////////////////////////////
539023Sgblack@eecs.umich.edu//
549023Sgblack@eecs.umich.edu// Raw Disk image
559023Sgblack@eecs.umich.edu//
569023Sgblack@eecs.umich.eduRawDiskImage::RawDiskImage(const string &name, const string &filename,
579023Sgblack@eecs.umich.edu                           bool rd_only)
589023Sgblack@eecs.umich.edu    : DiskImage(name), disk_size(0)
599023Sgblack@eecs.umich.edu{ open(filename, rd_only); }
609023Sgblack@eecs.umich.edu
619023Sgblack@eecs.umich.eduRawDiskImage::~RawDiskImage()
629023Sgblack@eecs.umich.edu{ close(); }
639023Sgblack@eecs.umich.edu
649023Sgblack@eecs.umich.eduvoid
659023Sgblack@eecs.umich.eduRawDiskImage::open(const string &filename, bool rd_only)
669023Sgblack@eecs.umich.edu{
679023Sgblack@eecs.umich.edu    if (!filename.empty()) {
689023Sgblack@eecs.umich.edu        initialized = true;
699023Sgblack@eecs.umich.edu        readonly = rd_only;
709023Sgblack@eecs.umich.edu        file = filename;
719023Sgblack@eecs.umich.edu
729023Sgblack@eecs.umich.edu        ios::openmode mode = ios::in | ios::binary;
739023Sgblack@eecs.umich.edu        if (!readonly)
749023Sgblack@eecs.umich.edu            mode |= ios::out;
759023Sgblack@eecs.umich.edu        stream.open(file.c_str(), mode);
769023Sgblack@eecs.umich.edu        if (!stream.is_open())
779023Sgblack@eecs.umich.edu            panic("Error opening %s", filename);
789023Sgblack@eecs.umich.edu    }
799023Sgblack@eecs.umich.edu}
809023Sgblack@eecs.umich.edu
819023Sgblack@eecs.umich.eduvoid
829023Sgblack@eecs.umich.eduRawDiskImage::close()
839023Sgblack@eecs.umich.edu{
849023Sgblack@eecs.umich.edu    stream.close();
859023Sgblack@eecs.umich.edu}
869023Sgblack@eecs.umich.edu
879023Sgblack@eecs.umich.eduoff_t
889023Sgblack@eecs.umich.eduRawDiskImage::size() const
899023Sgblack@eecs.umich.edu{
909023Sgblack@eecs.umich.edu    if (disk_size == 0) {
919023Sgblack@eecs.umich.edu        if (!stream.is_open())
929023Sgblack@eecs.umich.edu            panic("file not open!\n");
939023Sgblack@eecs.umich.edu        stream.seekg(0, ios::end);
949023Sgblack@eecs.umich.edu        disk_size = stream.tellg();
959023Sgblack@eecs.umich.edu    }
969023Sgblack@eecs.umich.edu
979023Sgblack@eecs.umich.edu    return disk_size / SectorSize;
989023Sgblack@eecs.umich.edu}
999023Sgblack@eecs.umich.edu
1009023Sgblack@eecs.umich.eduoff_t
1019023Sgblack@eecs.umich.eduRawDiskImage::read(uint8_t *data, off_t offset) const
1029023Sgblack@eecs.umich.edu{
1039023Sgblack@eecs.umich.edu    if (!initialized)
1049023Sgblack@eecs.umich.edu        panic("RawDiskImage not initialized");
1059023Sgblack@eecs.umich.edu
1069023Sgblack@eecs.umich.edu    if (!stream.is_open())
1079023Sgblack@eecs.umich.edu        panic("file not open!\n");
1089023Sgblack@eecs.umich.edu
1099023Sgblack@eecs.umich.edu    if (stream.seekg(offset * SectorSize, ios::beg) < 0)
1109023Sgblack@eecs.umich.edu        panic("Could not seek to location in file");
1119022Sgblack@eecs.umich.edu
1129022Sgblack@eecs.umich.edu    off_t pos = stream.tellg();
1139022Sgblack@eecs.umich.edu    stream.read((char *)data, SectorSize);
1149022Sgblack@eecs.umich.edu
1159022Sgblack@eecs.umich.edu    DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
1169022Sgblack@eecs.umich.edu    DDUMP(DiskImageRead, data, SectorSize);
1179022Sgblack@eecs.umich.edu
1189022Sgblack@eecs.umich.edu    return stream.tellg() - pos;
1199022Sgblack@eecs.umich.edu}
1209022Sgblack@eecs.umich.edu
1219022Sgblack@eecs.umich.eduoff_t
1229022Sgblack@eecs.umich.eduRawDiskImage::write(const uint8_t *data, off_t offset)
1239022Sgblack@eecs.umich.edu{
1249022Sgblack@eecs.umich.edu    if (!initialized)
1259023Sgblack@eecs.umich.edu        panic("RawDiskImage not initialized");
1269023Sgblack@eecs.umich.edu
1279023Sgblack@eecs.umich.edu    if (readonly)
1289023Sgblack@eecs.umich.edu        panic("Cannot write to a read only disk image");
1299023Sgblack@eecs.umich.edu
1309023Sgblack@eecs.umich.edu    if (!stream.is_open())
1319023Sgblack@eecs.umich.edu        panic("file not open!\n");
1329023Sgblack@eecs.umich.edu
1339023Sgblack@eecs.umich.edu    if (stream.seekp(offset * SectorSize, ios::beg) < 0)
1349022Sgblack@eecs.umich.edu        panic("Could not seek to location in file");
1359020Sgblack@eecs.umich.edu
1369020Sgblack@eecs.umich.edu    DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
1379020Sgblack@eecs.umich.edu    DDUMP(DiskImageWrite, data, SectorSize);
1389020Sgblack@eecs.umich.edu
139    off_t pos = stream.tellp();
140    stream.write((const char *)data, SectorSize);
141    return stream.tellp() - pos;
142}
143
144DEFINE_SIM_OBJECT_CLASS_NAME("DiskImage", DiskImage)
145
146BEGIN_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage)
147
148    Param<string> image_file;
149    Param<bool> read_only;
150
151END_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage)
152
153BEGIN_INIT_SIM_OBJECT_PARAMS(RawDiskImage)
154
155    INIT_PARAM(image_file, "disk image file"),
156    INIT_PARAM_DFLT(read_only, "read only image", false)
157
158END_INIT_SIM_OBJECT_PARAMS(RawDiskImage)
159
160
161CREATE_SIM_OBJECT(RawDiskImage)
162{
163    return new RawDiskImage(getInstanceName(), image_file, read_only);
164}
165
166REGISTER_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()) {
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()
234{
235    ifstream stream(filename.c_str());
236    if (!stream.is_open())
237        return false;
238
239    if (stream.fail() || stream.bad())
240        panic("Error opening %s", filename);
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", filename);
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              filename, 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    if (!initialized)
309        panic("RawDiskImage not initialized");
310
311    ofstream stream(filename.c_str());
312    if (!stream.is_open() || stream.fail() || stream.bad())
313        panic("Error opening %s", filename);
314
315    uint64_t magic;
316    memcpy(&magic, "COWDISK!", sizeof(magic));
317    SafeWrite(stream, magic);
318
319    SafeWrite(stream, (uint32_t)VersionMajor);
320    SafeWrite(stream, (uint32_t)VersionMinor);
321    SafeWrite(stream, (uint64_t)table->size());
322
323    uint64_t size = table->size();
324    SectorTable::iterator iter = table->begin();
325    SectorTable::iterator end = table->end();
326
327    for (uint64_t i = 0; i < size; i++) {
328        if (iter == end)
329            panic("Incorrect Table Size during save of COW disk image");
330
331        SafeWrite(stream, (uint64_t)(*iter).first);
332        SafeWrite(stream, (*iter).second->data, sizeof(Sector));
333        ++iter;
334    }
335
336    stream.close();
337}
338
339void
340CowDiskImage::writeback()
341{
342    SectorTable::iterator i = table->begin();
343    SectorTable::iterator end = table->end();
344
345    while (i != end) {
346        child->write((*i).second->data, (*i).first);
347        ++i;
348    }
349}
350
351off_t
352CowDiskImage::size() const
353{ return child->size(); }
354
355off_t
356CowDiskImage::read(uint8_t *data, off_t offset) const
357{
358    if (!initialized)
359        panic("CowDiskImage not initialized");
360
361    if (offset > size())
362        panic("access out of bounds");
363
364    SectorTable::const_iterator i = table->find(offset);
365    if (i == table->end())
366        return child->read(data, offset);
367    else {
368        memcpy(data, (*i).second->data, SectorSize);
369        DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
370        DDUMP(DiskImageRead, data, SectorSize);
371        return SectorSize;
372    }
373}
374
375off_t
376CowDiskImage::write(const uint8_t *data, off_t offset)
377{
378    if (!initialized)
379        panic("RawDiskImage not initialized");
380
381    if (offset > size())
382        panic("access out of bounds");
383
384    SectorTable::iterator i = table->find(offset);
385    if (i == table->end()) {
386        Sector *sector = new Sector;
387        memcpy(sector, data, SectorSize);
388        table->insert(make_pair(offset, sector));
389    } else {
390        memcpy((*i).second->data, data, SectorSize);
391    }
392
393    DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
394    DDUMP(DiskImageWrite, data, SectorSize);
395
396    return SectorSize;
397}
398
399BEGIN_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage)
400
401    SimObjectParam<DiskImage *> child;
402    Param<string> image_file;
403    Param<int> table_size;
404    Param<bool> read_only;
405
406END_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage)
407
408BEGIN_INIT_SIM_OBJECT_PARAMS(CowDiskImage)
409
410    INIT_PARAM(child, "child image"),
411    INIT_PARAM_DFLT(image_file, "disk image file", ""),
412    INIT_PARAM_DFLT(table_size, "initial table size", 65536),
413    INIT_PARAM_DFLT(read_only, "don't write back to the copy-on-write file",
414                    true)
415
416END_INIT_SIM_OBJECT_PARAMS(CowDiskImage)
417
418
419CREATE_SIM_OBJECT(CowDiskImage)
420{
421    if (((string)image_file).empty())
422        return new CowDiskImage(getInstanceName(), child, table_size);
423    else
424        return new CowDiskImage(getInstanceName(), child, table_size,
425                                image_file, read_only);
426}
427
428REGISTER_SIM_OBJECT("CowDiskImage", CowDiskImage)
429