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