disk_image.cc revision 259
1/* 2 * Copyright (c) 2003 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 50using namespace std; 51 52//////////////////////////////////////////////////////////////////////// 53// 54// Raw Disk image 55// 56RawDiskImage::RawDiskImage(const string &name, const string &filename, 57 bool rd_only) 58 : DiskImage(name), disk_size(0) 59{ open(filename, rd_only); } 60 61RawDiskImage::~RawDiskImage() 62{ close(); } 63 64void 65RawDiskImage::open(const string &filename, bool rd_only) 66{ 67 if (!filename.empty()) { 68 initialized = true; 69 readonly = rd_only; 70 file = filename; 71 72 ios::openmode mode = ios::in | ios::binary; 73 if (!readonly) 74 mode |= ios::out; 75 stream.open(file.c_str(), mode); 76 if (!stream.is_open()) 77 panic("Error opening %s", filename); 78 } 79} 80 81void 82RawDiskImage::close() 83{ 84 stream.close(); 85} 86 87off_t 88RawDiskImage::size() const 89{ 90 if (disk_size == 0) { 91 if (!stream.is_open()) 92 panic("file not open!\n"); 93 stream.seekg(0, ios::end); 94 disk_size = stream.tellg(); 95 } 96 97 return disk_size / SectorSize; 98} 99 100off_t 101RawDiskImage::read(uint8_t *data, off_t offset) const 102{ 103 if (!initialized) 104 panic("RawDiskImage not initialized"); 105 106 if (!stream.is_open()) 107 panic("file not open!\n"); 108 109 if (stream.seekg(offset * SectorSize, ios::beg) < 0) 110 panic("Could not seek to location in file"); 111 112 off_t pos = stream.tellg(); 113 stream.read((char *)data, SectorSize); 114 115 DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset); 116 DDUMP(DiskImageRead, data, SectorSize); 117 118 return stream.tellg() - pos; 119} 120 121off_t 122RawDiskImage::write(const uint8_t *data, off_t offset) 123{ 124 if (!initialized) 125 panic("RawDiskImage not initialized"); 126 127 if (readonly) 128 panic("Cannot write to a read only disk image"); 129 130 if (!stream.is_open()) 131 panic("file not open!\n"); 132 133 if (stream.seekp(offset * SectorSize, ios::beg) < 0) 134 panic("Could not seek to location in file"); 135 136 DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset); 137 DDUMP(DiskImageWrite, data, SectorSize); 138 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(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 = serializeFilename + ".cow"; 409 SERIALIZE_SCALAR(cowFilename); 410 save(cowFilename); 411} 412 413void 414CowDiskImage::unserialize(Checkpoint *cp, const string §ion) 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