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