physical.cc revision 9565
1/* 2 * Copyright (c) 2012 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Andreas Hansson 38 */ 39 40#include <sys/mman.h> 41#include <sys/types.h> 42#include <sys/user.h> 43#include <fcntl.h> 44#include <unistd.h> 45#include <zlib.h> 46 47#include <cerrno> 48#include <climits> 49#include <cstdio> 50#include <iostream> 51#include <string> 52 53#include "base/trace.hh" 54#include "debug/BusAddrRanges.hh" 55#include "debug/Checkpoint.hh" 56#include "mem/abstract_mem.hh" 57#include "mem/physical.hh" 58 59using namespace std; 60 61PhysicalMemory::PhysicalMemory(const string& _name, 62 const vector<AbstractMemory*>& _memories) : 63 _name(_name), size(0) 64{ 65 // add the memories from the system to the address map as 66 // appropriate 67 for (vector<AbstractMemory*>::const_iterator m = _memories.begin(); 68 m != _memories.end(); ++m) { 69 // only add the memory if it is part of the global address map 70 if ((*m)->isInAddrMap()) { 71 memories.push_back(*m); 72 73 // calculate the total size once and for all 74 size += (*m)->size(); 75 76 // add the range to our interval tree and make sure it does not 77 // intersect an existing range 78 if (addrMap.insert((*m)->getAddrRange(), *m) == addrMap.end()) 79 fatal("Memory address range for %s is overlapping\n", 80 (*m)->name()); 81 } else { 82 DPRINTF(BusAddrRanges, 83 "Skipping memory %s that is not in global address map\n", 84 (*m)->name()); 85 // this type of memory is used e.g. as reference memory by 86 // Ruby, and they also needs a backing store, but should 87 // not be part of the global address map 88 89 // simply do it independently, also note that this kind of 90 // memories are allowed to overlap in the logic address 91 // map 92 vector<AbstractMemory*> unmapped_mems; 93 unmapped_mems.push_back(*m); 94 createBackingStore((*m)->getAddrRange(), unmapped_mems); 95 } 96 } 97 98 // iterate over the increasing addresses and chunks of contigous 99 // space to be mapped to backing store, also remember what 100 // memories constitute the range so we can go and find out if we 101 // have to init their parts to zero 102 vector<AddrRange> intlv_ranges; 103 vector<AbstractMemory*> curr_memories; 104 for (AddrRangeMap<AbstractMemory*>::const_iterator r = addrMap.begin(); 105 r != addrMap.end(); ++r) { 106 // simply skip past all memories that are null and hence do 107 // not need any backing store 108 if (!r->second->isNull()) { 109 // if the range is interleaved then save it for now 110 if (r->first.interleaved()) { 111 // if we already got interleaved ranges that are not 112 // part of the same range, then first do a merge 113 // before we add the new one 114 if (!intlv_ranges.empty() && 115 !intlv_ranges.back().mergesWith(r->first)) { 116 AddrRange merged_range(intlv_ranges); 117 createBackingStore(merged_range, curr_memories); 118 intlv_ranges.clear(); 119 curr_memories.clear(); 120 } 121 intlv_ranges.push_back(r->first); 122 curr_memories.push_back(r->second); 123 } else { 124 vector<AbstractMemory*> single_memory; 125 single_memory.push_back(r->second); 126 createBackingStore(r->first, single_memory); 127 } 128 } 129 } 130 131 // if there is still interleaved ranges waiting to be merged, go 132 // ahead and do it 133 if (!intlv_ranges.empty()) { 134 AddrRange merged_range(intlv_ranges); 135 createBackingStore(merged_range, curr_memories); 136 } 137} 138 139void 140PhysicalMemory::createBackingStore(AddrRange range, 141 const vector<AbstractMemory*>& _memories) 142{ 143 if (range.interleaved()) 144 panic("Cannot create backing store for interleaved range %s\n", 145 range.to_string()); 146 147 // perform the actual mmap 148 DPRINTF(BusAddrRanges, "Creating backing store for range %s with size %d\n", 149 range.to_string(), range.size()); 150 int map_flags = MAP_ANON | MAP_PRIVATE; 151 uint8_t* pmem = (uint8_t*) mmap(NULL, range.size(), 152 PROT_READ | PROT_WRITE, 153 map_flags, -1, 0); 154 155 if (pmem == (uint8_t*) MAP_FAILED) { 156 perror("mmap"); 157 fatal("Could not mmap %d bytes for range %s!\n", range.size(), 158 range.to_string()); 159 } 160 161 // remember this backing store so we can checkpoint it and unmap 162 // it appropriately 163 backingStore.push_back(make_pair(range, pmem)); 164 165 // count how many of the memories are to be zero initialized so we 166 // can see if some but not all have this parameter set 167 uint32_t init_to_zero = 0; 168 169 // point the memories to their backing store, and if requested, 170 // initialize the memory range to 0 171 for (vector<AbstractMemory*>::const_iterator m = _memories.begin(); 172 m != _memories.end(); ++m) { 173 DPRINTF(BusAddrRanges, "Mapping memory %s to backing store\n", 174 (*m)->name()); 175 (*m)->setBackingStore(pmem); 176 177 // if it should be zero, then go and make it so 178 if ((*m)->initToZero()) { 179 ++init_to_zero; 180 } 181 } 182 183 if (init_to_zero != 0) { 184 if (init_to_zero != _memories.size()) 185 fatal("Some, but not all memories in range %s are set zero\n", 186 range.to_string()); 187 188 memset(pmem, 0, range.size()); 189 } 190} 191 192PhysicalMemory::~PhysicalMemory() 193{ 194 // unmap the backing store 195 for (vector<pair<AddrRange, uint8_t*> >::iterator s = backingStore.begin(); 196 s != backingStore.end(); ++s) 197 munmap((char*)s->second, s->first.size()); 198} 199 200bool 201PhysicalMemory::isMemAddr(Addr addr) const 202{ 203 // see if the address is within the last matched range 204 if (!rangeCache.contains(addr)) { 205 // lookup in the interval tree 206 AddrRangeMap<AbstractMemory*>::const_iterator r = addrMap.find(addr); 207 if (r == addrMap.end()) { 208 // not in the cache, and not in the tree 209 return false; 210 } 211 // the range is in the tree, update the cache 212 rangeCache = r->first; 213 } 214 215 assert(addrMap.find(addr) != addrMap.end()); 216 217 // either matched the cache or found in the tree 218 return true; 219} 220 221AddrRangeList 222PhysicalMemory::getConfAddrRanges() const 223{ 224 // this could be done once in the constructor, but since it is unlikely to 225 // be called more than once the iteration should not be a problem 226 AddrRangeList ranges; 227 vector<AddrRange> intlv_ranges; 228 for (AddrRangeMap<AbstractMemory*>::const_iterator r = addrMap.begin(); 229 r != addrMap.end(); ++r) { 230 if (r->second->isConfReported()) { 231 // if the range is interleaved then save it for now 232 if (r->first.interleaved()) { 233 // if we already got interleaved ranges that are not 234 // part of the same range, then first do a merge 235 // before we add the new one 236 if (!intlv_ranges.empty() && 237 !intlv_ranges.back().mergesWith(r->first)) { 238 ranges.push_back(AddrRange(intlv_ranges)); 239 intlv_ranges.clear(); 240 } 241 intlv_ranges.push_back(r->first); 242 } else { 243 // keep the current range 244 ranges.push_back(r->first); 245 } 246 } 247 } 248 249 // if there is still interleaved ranges waiting to be merged, 250 // go ahead and do it 251 if (!intlv_ranges.empty()) { 252 ranges.push_back(AddrRange(intlv_ranges)); 253 } 254 255 return ranges; 256} 257 258void 259PhysicalMemory::access(PacketPtr pkt) 260{ 261 assert(pkt->isRequest()); 262 Addr addr = pkt->getAddr(); 263 AddrRangeMap<AbstractMemory*>::const_iterator m = addrMap.find(addr); 264 assert(m != addrMap.end()); 265 m->second->access(pkt); 266} 267 268void 269PhysicalMemory::functionalAccess(PacketPtr pkt) 270{ 271 assert(pkt->isRequest()); 272 Addr addr = pkt->getAddr(); 273 AddrRangeMap<AbstractMemory*>::const_iterator m = addrMap.find(addr); 274 assert(m != addrMap.end()); 275 m->second->functionalAccess(pkt); 276} 277 278void 279PhysicalMemory::serialize(ostream& os) 280{ 281 // serialize all the locked addresses and their context ids 282 vector<Addr> lal_addr; 283 vector<int> lal_cid; 284 285 for (vector<AbstractMemory*>::iterator m = memories.begin(); 286 m != memories.end(); ++m) { 287 const list<LockedAddr>& locked_addrs = (*m)->getLockedAddrList(); 288 for (list<LockedAddr>::const_iterator l = locked_addrs.begin(); 289 l != locked_addrs.end(); ++l) { 290 lal_addr.push_back(l->addr); 291 lal_cid.push_back(l->contextId); 292 } 293 } 294 295 arrayParamOut(os, "lal_addr", lal_addr); 296 arrayParamOut(os, "lal_cid", lal_cid); 297 298 // serialize the backing stores 299 unsigned int nbr_of_stores = backingStore.size(); 300 SERIALIZE_SCALAR(nbr_of_stores); 301 302 unsigned int store_id = 0; 303 // store each backing store memory segment in a file 304 for (vector<pair<AddrRange, uint8_t*> >::iterator s = backingStore.begin(); 305 s != backingStore.end(); ++s) { 306 nameOut(os, csprintf("%s.store%d", name(), store_id)); 307 serializeStore(os, store_id++, s->first, s->second); 308 } 309} 310 311void 312PhysicalMemory::serializeStore(ostream& os, unsigned int store_id, 313 AddrRange range, uint8_t* pmem) 314{ 315 // we cannot use the address range for the name as the 316 // memories that are not part of the address map can overlap 317 string filename = name() + ".store" + to_string(store_id) + ".pmem"; 318 long range_size = range.size(); 319 320 DPRINTF(Checkpoint, "Serializing physical memory %s with size %d\n", 321 filename, range_size); 322 323 SERIALIZE_SCALAR(store_id); 324 SERIALIZE_SCALAR(filename); 325 SERIALIZE_SCALAR(range_size); 326 327 // write memory file 328 string filepath = Checkpoint::dir() + "/" + filename.c_str(); 329 int fd = creat(filepath.c_str(), 0664); 330 if (fd < 0) { 331 perror("creat"); 332 fatal("Can't open physical memory checkpoint file '%s'\n", 333 filename); 334 } 335 336 gzFile compressed_mem = gzdopen(fd, "wb"); 337 if (compressed_mem == NULL) 338 fatal("Insufficient memory to allocate compression state for %s\n", 339 filename); 340 341 uint64_t pass_size = 0; 342 343 // gzwrite fails if (int)len < 0 (gzwrite returns int) 344 for (uint64_t written = 0; written < range.size(); 345 written += pass_size) { 346 pass_size = (uint64_t)INT_MAX < (range.size() - written) ? 347 (uint64_t)INT_MAX : (range.size() - written); 348 349 if (gzwrite(compressed_mem, pmem + written, 350 (unsigned int) pass_size) != (int) pass_size) { 351 fatal("Write failed on physical memory checkpoint file '%s'\n", 352 filename); 353 } 354 } 355 356 // close the compressed stream and check that the exit status 357 // is zero 358 if (gzclose(compressed_mem)) 359 fatal("Close failed on physical memory checkpoint file '%s'\n", 360 filename); 361 362} 363 364void 365PhysicalMemory::unserialize(Checkpoint* cp, const string& section) 366{ 367 // unserialize the locked addresses and map them to the 368 // appropriate memory controller 369 vector<Addr> lal_addr; 370 vector<int> lal_cid; 371 arrayParamIn(cp, section, "lal_addr", lal_addr); 372 arrayParamIn(cp, section, "lal_cid", lal_cid); 373 for(size_t i = 0; i < lal_addr.size(); ++i) { 374 AddrRangeMap<AbstractMemory*>::const_iterator m = 375 addrMap.find(lal_addr[i]); 376 m->second->addLockedAddr(LockedAddr(lal_addr[i], lal_cid[i])); 377 } 378 379 // unserialize the backing stores 380 unsigned int nbr_of_stores; 381 UNSERIALIZE_SCALAR(nbr_of_stores); 382 383 for (unsigned int i = 0; i < nbr_of_stores; ++i) { 384 unserializeStore(cp, csprintf("%s.store%d", section, i)); 385 } 386 387} 388 389void 390PhysicalMemory::unserializeStore(Checkpoint* cp, const string& section) 391{ 392 const uint32_t chunk_size = 16384; 393 394 unsigned int store_id; 395 UNSERIALIZE_SCALAR(store_id); 396 397 string filename; 398 UNSERIALIZE_SCALAR(filename); 399 string filepath = cp->cptDir + "/" + filename; 400 401 // mmap memoryfile 402 int fd = open(filepath.c_str(), O_RDONLY); 403 if (fd < 0) { 404 perror("open"); 405 fatal("Can't open physical memory checkpoint file '%s'", filename); 406 } 407 408 gzFile compressed_mem = gzdopen(fd, "rb"); 409 if (compressed_mem == NULL) 410 fatal("Insufficient memory to allocate compression state for %s\n", 411 filename); 412 413 uint8_t* pmem = backingStore[store_id].second; 414 AddrRange range = backingStore[store_id].first; 415 416 // unmap file that was mmapped in the constructor, this is 417 // done here to make sure that gzip and open don't muck with 418 // our nice large space of memory before we reallocate it 419 munmap((char*) pmem, range.size()); 420 421 long range_size; 422 UNSERIALIZE_SCALAR(range_size); 423 424 DPRINTF(Checkpoint, "Unserializing physical memory %s with size %d\n", 425 filename, range_size); 426 427 if (range_size != range.size()) 428 fatal("Memory range size has changed! Saw %lld, expected %lld\n", 429 range_size, range.size()); 430 431 pmem = (uint8_t*) mmap(NULL, range.size(), PROT_READ | PROT_WRITE, 432 MAP_ANON | MAP_PRIVATE, -1, 0); 433 434 if (pmem == (void*) MAP_FAILED) { 435 perror("mmap"); 436 fatal("Could not mmap physical memory!\n"); 437 } 438 439 uint64_t curr_size = 0; 440 long* temp_page = new long[chunk_size]; 441 long* pmem_current; 442 uint32_t bytes_read; 443 while (curr_size < range.size()) { 444 bytes_read = gzread(compressed_mem, temp_page, chunk_size); 445 if (bytes_read == 0) 446 break; 447 448 assert(bytes_read % sizeof(long) == 0); 449 450 for (uint32_t x = 0; x < bytes_read / sizeof(long); x++) { 451 // Only copy bytes that are non-zero, so we don't give 452 // the VM system hell 453 if (*(temp_page + x) != 0) { 454 pmem_current = (long*)(pmem + curr_size + x * sizeof(long)); 455 *pmem_current = *(temp_page + x); 456 } 457 } 458 curr_size += bytes_read; 459 } 460 461 delete[] temp_page; 462 463 if (gzclose(compressed_mem)) 464 fatal("Close failed on physical memory checkpoint file '%s'\n", 465 filename); 466} 467