disk_image.cc revision 56
14166Sgblack@eecs.umich.edu/* 24166Sgblack@eecs.umich.edu * Copyright (c) 2003 The Regents of The University of Michigan 34166Sgblack@eecs.umich.edu * All rights reserved. 44166Sgblack@eecs.umich.edu * 57087Snate@binkert.org * Redistribution and use in source and binary forms, with or without 67087Snate@binkert.org * modification, are permitted provided that the following conditions are 77087Snate@binkert.org * met: redistributions of source code must retain the above copyright 87087Snate@binkert.org * notice, this list of conditions and the following disclaimer; 97087Snate@binkert.org * redistributions in binary form must reproduce the above copyright 107087Snate@binkert.org * notice, this list of conditions and the following disclaimer in the 117087Snate@binkert.org * documentation and/or other materials provided with the distribution; 127087Snate@binkert.org * neither the name of the copyright holders nor the names of its 134166Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from 147087Snate@binkert.org * this software without specific prior written permission. 157087Snate@binkert.org * 167087Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 177087Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 187087Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 197087Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 207087Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 217087Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 224166Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 237087Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 244166Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 254166Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 264166Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 274166Sgblack@eecs.umich.edu */ 284166Sgblack@eecs.umich.edu 294166Sgblack@eecs.umich.edu/* @file 304166Sgblack@eecs.umich.edu * Disk Image Definitions 314166Sgblack@eecs.umich.edu */ 324166Sgblack@eecs.umich.edu 334166Sgblack@eecs.umich.edu#include <sys/types.h> 344166Sgblack@eecs.umich.edu#include <sys/uio.h> 354166Sgblack@eecs.umich.edu#include <errno.h> 364166Sgblack@eecs.umich.edu#include <string.h> 374166Sgblack@eecs.umich.edu#include <unistd.h> 384166Sgblack@eecs.umich.edu 394166Sgblack@eecs.umich.edu#include <cstdio> 408229Snate@binkert.org#include <fstream> 418229Snate@binkert.org#include <string> 424166Sgblack@eecs.umich.edu 434166Sgblack@eecs.umich.edu#include "dev/disk_image.hh" 444166Sgblack@eecs.umich.edu#include "base/misc.hh" 454759Sgblack@eecs.umich.edu#include "base/trace.hh" 464166Sgblack@eecs.umich.edu#include "sim/sim_exit.hh" 474166Sgblack@eecs.umich.edu#include "base/callback.hh" 484166Sgblack@eecs.umich.edu 494166Sgblack@eecs.umich.eduusing namespace std; 504166Sgblack@eecs.umich.edu 514166Sgblack@eecs.umich.edu//////////////////////////////////////////////////////////////////////// 524166Sgblack@eecs.umich.edu// 534166Sgblack@eecs.umich.edu// Raw Disk image 544166Sgblack@eecs.umich.edu// 554166Sgblack@eecs.umich.eduRawDiskImage::RawDiskImage(const string &name, const string &filename, 564166Sgblack@eecs.umich.edu bool rd_only) 574166Sgblack@eecs.umich.edu : DiskImage(name), disk_size(0) 584166Sgblack@eecs.umich.edu{ open(filename, rd_only); } 594166Sgblack@eecs.umich.edu 604166Sgblack@eecs.umich.eduRawDiskImage::~RawDiskImage() 614166Sgblack@eecs.umich.edu{ close(); } 624166Sgblack@eecs.umich.edu 634166Sgblack@eecs.umich.eduvoid 644166Sgblack@eecs.umich.eduRawDiskImage::open(const string &filename, bool rd_only) 654166Sgblack@eecs.umich.edu{ 664166Sgblack@eecs.umich.edu if (!filename.empty()) { 674166Sgblack@eecs.umich.edu initialized = true; 684166Sgblack@eecs.umich.edu readonly = rd_only; 694166Sgblack@eecs.umich.edu file = filename; 704166Sgblack@eecs.umich.edu 714166Sgblack@eecs.umich.edu ios::openmode mode = ios::in | ios::binary; 724166Sgblack@eecs.umich.edu if (!readonly) 734166Sgblack@eecs.umich.edu mode |= ios::out; 744166Sgblack@eecs.umich.edu stream.open(file.c_str(), mode); 754166Sgblack@eecs.umich.edu if (!stream.is_open()) 764166Sgblack@eecs.umich.edu panic("Error opening %s", filename); 774166Sgblack@eecs.umich.edu } 784166Sgblack@eecs.umich.edu} 794759Sgblack@eecs.umich.edu 804759Sgblack@eecs.umich.eduvoid 814759Sgblack@eecs.umich.eduRawDiskImage::close() 824166Sgblack@eecs.umich.edu{ 835960Sgblack@eecs.umich.edu stream.close(); 845960Sgblack@eecs.umich.edu} 855960Sgblack@eecs.umich.edu 865960Sgblack@eecs.umich.eduoff_t 875960Sgblack@eecs.umich.eduRawDiskImage::size() const 885960Sgblack@eecs.umich.edu{ 895960Sgblack@eecs.umich.edu if (disk_size == 0) { 905960Sgblack@eecs.umich.edu if (!stream.is_open()) 915960Sgblack@eecs.umich.edu panic("file not open!\n"); 925960Sgblack@eecs.umich.edu stream.seekg(0, ios::end); 935960Sgblack@eecs.umich.edu disk_size = stream.tellg(); 945960Sgblack@eecs.umich.edu } 955960Sgblack@eecs.umich.edu 965960Sgblack@eecs.umich.edu return disk_size / SectorSize; 975960Sgblack@eecs.umich.edu} 985960Sgblack@eecs.umich.edu 995960Sgblack@eecs.umich.eduoff_t 1005960Sgblack@eecs.umich.eduRawDiskImage::read(uint8_t *data, off_t offset) const 1015960Sgblack@eecs.umich.edu{ 1025960Sgblack@eecs.umich.edu if (!initialized) 1035960Sgblack@eecs.umich.edu panic("RawDiskImage not initialized"); 1045960Sgblack@eecs.umich.edu 1055960Sgblack@eecs.umich.edu if (!stream.is_open()) 1065960Sgblack@eecs.umich.edu panic("file not open!\n"); 1075960Sgblack@eecs.umich.edu 1085960Sgblack@eecs.umich.edu if (stream.seekg(offset * SectorSize, ios::beg) < 0) 1095960Sgblack@eecs.umich.edu panic("Could not seek to location in file"); 1105960Sgblack@eecs.umich.edu 1115960Sgblack@eecs.umich.edu off_t pos = stream.tellg(); 1125960Sgblack@eecs.umich.edu stream.read((char *)data, SectorSize); 1135960Sgblack@eecs.umich.edu 1145960Sgblack@eecs.umich.edu DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset); 1155960Sgblack@eecs.umich.edu DDUMP(DiskImageRead, data, SectorSize); 1165960Sgblack@eecs.umich.edu 1175960Sgblack@eecs.umich.edu return stream.tellg() - pos; 1185960Sgblack@eecs.umich.edu} 1195960Sgblack@eecs.umich.edu 1205960Sgblack@eecs.umich.eduoff_t 1215960Sgblack@eecs.umich.eduRawDiskImage::write(const uint8_t *data, off_t offset) 122{ 123 if (!initialized) 124 panic("RawDiskImage not initialized"); 125 126 if (readonly) 127 panic("Cannot write to a read only disk image"); 128 129 if (!stream.is_open()) 130 panic("file not open!\n"); 131 132 if (stream.seekp(offset * SectorSize, ios::beg) < 0) 133 panic("Could not seek to location in file"); 134 135 DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset); 136 DDUMP(DiskImageWrite, data, SectorSize); 137 138 off_t pos = stream.tellp(); 139 stream.write((const char *)data, SectorSize); 140 return stream.tellp() - pos; 141} 142 143DEFINE_SIM_OBJECT_CLASS_NAME("DiskImage", DiskImage) 144 145BEGIN_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage) 146 147 Param<string> image_file; 148 Param<bool> read_only; 149 150END_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage) 151 152BEGIN_INIT_SIM_OBJECT_PARAMS(RawDiskImage) 153 154 INIT_PARAM(image_file, "disk image file"), 155 INIT_PARAM_DFLT(read_only, "read only image", false) 156 157END_INIT_SIM_OBJECT_PARAMS(RawDiskImage) 158 159 160CREATE_SIM_OBJECT(RawDiskImage) 161{ 162 return new RawDiskImage(getInstanceName(), image_file, read_only); 163} 164 165REGISTER_SIM_OBJECT("RawDiskImage", RawDiskImage) 166 167//////////////////////////////////////////////////////////////////////// 168// 169// Copy on Write Disk image 170// 171const int CowDiskImage::VersionMajor = 1; 172const int CowDiskImage::VersionMinor = 0; 173 174CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size) 175 : DiskImage(name), child(kid), table(NULL) 176{ init(hash_size); } 177 178class CowDiskCallback : public Callback 179{ 180 private: 181 CowDiskImage *image; 182 183 public: 184 CowDiskCallback(CowDiskImage *i) : image(i) {} 185 void process() { image->save(); delete this; } 186}; 187 188CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size, 189 const string &file, bool read_only) 190 : DiskImage(name), filename(file), child(kid), table(NULL) 191{ 192 if (!open()) { 193 assert(!read_only && "why have a non-existent read only file?"); 194 init(hash_size); 195 } 196 197 if (!read_only) 198 registerExitCallback(new CowDiskCallback(this)); 199} 200 201CowDiskImage::~CowDiskImage() 202{ 203 SectorTable::iterator i = table->begin(); 204 SectorTable::iterator end = table->end(); 205 206 while (i != end) { 207 delete (*i).second; 208 ++i; 209 } 210} 211 212void 213SafeRead(ifstream &stream, void *data, int count) 214{ 215 stream.read((char *)data, count); 216 if (!stream.is_open()) 217 panic("file not open"); 218 219 if (stream.eof()) 220 panic("premature end-of-file"); 221 222 if (stream.bad() || stream.fail()) 223 panic("error reading cowdisk image"); 224} 225 226template<class T> 227void 228SafeRead(ifstream &stream, T &data) 229{ SafeRead(stream, &data, sizeof(data)); } 230 231bool 232CowDiskImage::open() 233{ 234 ifstream stream(filename.c_str()); 235 if (!stream.is_open()) 236 return false; 237 238 if (stream.fail() || stream.bad()) 239 panic("Error opening %s", filename); 240 241 uint64_t magic; 242 SafeRead(stream, magic); 243 244 if (memcmp(&magic, "COWDISK!", sizeof(magic)) != 0) 245 panic("Could not open %s: Invalid magic", filename); 246 247 uint32_t major, minor; 248 SafeRead(stream, major); 249 SafeRead(stream, minor); 250 251 if (major != VersionMajor && minor != VersionMinor) 252 panic("Could not open %s: invalid version %d.%d != %d.%d", 253 filename, major, minor, VersionMajor, VersionMinor); 254 255 uint64_t sector_count; 256 SafeRead(stream, sector_count); 257 table = new SectorTable(sector_count); 258 259 260 for (uint64_t i = 0; i < sector_count; i++) { 261 uint64_t offset; 262 SafeRead(stream, offset); 263 264 Sector *sector = new Sector; 265 SafeRead(stream, sector, sizeof(Sector)); 266 267 assert(table->find(offset) == table->end()); 268 (*table)[offset] = sector; 269 } 270 271 stream.close(); 272 273 initialized = true; 274 return true; 275} 276 277void 278CowDiskImage::init(int hash_size) 279{ 280 table = new SectorTable(hash_size); 281 282 initialized = true; 283} 284 285void 286SafeWrite(ofstream &stream, const void *data, int count) 287{ 288 stream.write((const char *)data, count); 289 if (!stream.is_open()) 290 panic("file not open"); 291 292 if (stream.eof()) 293 panic("premature end-of-file"); 294 295 if (stream.bad() || stream.fail()) 296 panic("error reading cowdisk image"); 297} 298 299template<class T> 300void 301SafeWrite(ofstream &stream, const T &data) 302{ SafeWrite(stream, &data, sizeof(data)); } 303 304void 305CowDiskImage::save() 306{ 307 if (!initialized) 308 panic("RawDiskImage not initialized"); 309 310 ofstream stream(filename.c_str()); 311 if (!stream.is_open() || stream.fail() || stream.bad()) 312 panic("Error opening %s", filename); 313 314 uint64_t magic; 315 memcpy(&magic, "COWDISK!", sizeof(magic)); 316 SafeWrite(stream, magic); 317 318 SafeWrite(stream, (uint32_t)VersionMajor); 319 SafeWrite(stream, (uint32_t)VersionMinor); 320 SafeWrite(stream, (uint64_t)table->size()); 321 322 uint64_t size = table->size(); 323 SectorTable::iterator iter = table->begin(); 324 SectorTable::iterator end = table->end(); 325 326 for (uint64_t i = 0; i < size; i++) { 327 if (iter == end) 328 panic("Incorrect Table Size during save of COW disk image"); 329 330 SafeWrite(stream, (uint64_t)(*iter).first); 331 SafeWrite(stream, (*iter).second->data, sizeof(Sector)); 332 ++iter; 333 } 334 335 stream.close(); 336} 337 338void 339CowDiskImage::writeback() 340{ 341 SectorTable::iterator i = table->begin(); 342 SectorTable::iterator end = table->end(); 343 344 while (i != end) { 345 child->write((*i).second->data, (*i).first); 346 ++i; 347 } 348} 349 350off_t 351CowDiskImage::size() const 352{ return child->size(); } 353 354off_t 355CowDiskImage::read(uint8_t *data, off_t offset) const 356{ 357 if (!initialized) 358 panic("CowDiskImage not initialized"); 359 360 if (offset > size()) 361 panic("access out of bounds"); 362 363 SectorTable::const_iterator i = table->find(offset); 364 if (i == table->end()) 365 return child->read(data, offset); 366 else { 367 memcpy(data, (*i).second->data, SectorSize); 368 DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset); 369 DDUMP(DiskImageRead, data, SectorSize); 370 return SectorSize; 371 } 372} 373 374off_t 375CowDiskImage::write(const uint8_t *data, off_t offset) 376{ 377 if (!initialized) 378 panic("RawDiskImage not initialized"); 379 380 if (offset > size()) 381 panic("access out of bounds"); 382 383 SectorTable::iterator i = table->find(offset); 384 if (i == table->end()) { 385 Sector *sector = new Sector; 386 memcpy(sector, data, SectorSize); 387 table->insert(make_pair(offset, sector)); 388 } else { 389 memcpy((*i).second->data, data, SectorSize); 390 } 391 392 DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset); 393 DDUMP(DiskImageWrite, data, SectorSize); 394 395 return SectorSize; 396} 397 398BEGIN_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage) 399 400 SimObjectParam<DiskImage *> child; 401 Param<string> image_file; 402 Param<int> table_size; 403 Param<bool> read_only; 404 405END_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage) 406 407BEGIN_INIT_SIM_OBJECT_PARAMS(CowDiskImage) 408 409 INIT_PARAM(child, "child image"), 410 INIT_PARAM_DFLT(image_file, "disk image file", ""), 411 INIT_PARAM_DFLT(table_size, "initial table size", 65536), 412 INIT_PARAM_DFLT(read_only, "don't write back to the copy-on-write file", 413 true) 414 415END_INIT_SIM_OBJECT_PARAMS(CowDiskImage) 416 417 418CREATE_SIM_OBJECT(CowDiskImage) 419{ 420 if (((string)image_file).empty()) 421 return new CowDiskImage(getInstanceName(), child, table_size); 422 else 423 return new CowDiskImage(getInstanceName(), child, table_size, 424 image_file, read_only); 425} 426 427REGISTER_SIM_OBJECT("CowDiskImage", CowDiskImage) 428