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