base.cc revision 9684:00dca8a9b560
16019Shines@cs.fsu.edu/* 27093Sgblack@eecs.umich.edu * Copyright (c) 2012 ARM Limited 37093Sgblack@eecs.umich.edu * All rights reserved 47093Sgblack@eecs.umich.edu * 57093Sgblack@eecs.umich.edu * The license below extends only to copyright in the software and shall 67093Sgblack@eecs.umich.edu * not be construed as granting a license to any other intellectual 77093Sgblack@eecs.umich.edu * property including but not limited to intellectual property relating 87093Sgblack@eecs.umich.edu * to a hardware implementation of the functionality of the software 97093Sgblack@eecs.umich.edu * licensed hereunder. You may use the software subject to the license 107093Sgblack@eecs.umich.edu * terms below provided that you ensure that this notice is replicated 117093Sgblack@eecs.umich.edu * unmodified and in its entirety in all distributions of the software, 127093Sgblack@eecs.umich.edu * modified or unmodified, in source code or in binary form. 137093Sgblack@eecs.umich.edu * 146019Shines@cs.fsu.edu * Redistribution and use in source and binary forms, with or without 156019Shines@cs.fsu.edu * modification, are permitted provided that the following conditions are 166019Shines@cs.fsu.edu * met: redistributions of source code must retain the above copyright 176019Shines@cs.fsu.edu * notice, this list of conditions and the following disclaimer; 186019Shines@cs.fsu.edu * redistributions in binary form must reproduce the above copyright 196019Shines@cs.fsu.edu * notice, this list of conditions and the following disclaimer in the 206019Shines@cs.fsu.edu * documentation and/or other materials provided with the distribution; 216019Shines@cs.fsu.edu * neither the name of the copyright holders nor the names of its 226019Shines@cs.fsu.edu * contributors may be used to endorse or promote products derived from 236019Shines@cs.fsu.edu * this software without specific prior written permission. 246019Shines@cs.fsu.edu * 256019Shines@cs.fsu.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 266019Shines@cs.fsu.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 276019Shines@cs.fsu.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 286019Shines@cs.fsu.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 296019Shines@cs.fsu.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 306019Shines@cs.fsu.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 316019Shines@cs.fsu.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 326019Shines@cs.fsu.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 336019Shines@cs.fsu.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 346019Shines@cs.fsu.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 356019Shines@cs.fsu.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 366019Shines@cs.fsu.edu * 376019Shines@cs.fsu.edu * Authors: Andreas Sandberg 386019Shines@cs.fsu.edu */ 396019Shines@cs.fsu.edu 406019Shines@cs.fsu.edu#include <linux/kvm.h> 416735Sgblack@eecs.umich.edu#include <sys/ioctl.h> 426735Sgblack@eecs.umich.edu#include <sys/mman.h> 436019Shines@cs.fsu.edu#include <unistd.h> 446019Shines@cs.fsu.edu 456019Shines@cs.fsu.edu#include <cerrno> 468229Snate@binkert.org#include <csignal> 478229Snate@binkert.org#include <ostream> 486019Shines@cs.fsu.edu 498232Snate@binkert.org#include "arch/utility.hh" 506019Shines@cs.fsu.edu#include "cpu/kvm/base.hh" 516019Shines@cs.fsu.edu#include "debug/Checkpoint.hh" 526019Shines@cs.fsu.edu#include "debug/Kvm.hh" 536019Shines@cs.fsu.edu#include "debug/KvmIO.hh" 547362Sgblack@eecs.umich.edu#include "debug/KvmRun.hh" 556735Sgblack@eecs.umich.edu#include "params/BaseKvmCPU.hh" 566019Shines@cs.fsu.edu#include "sim/process.hh" 577362Sgblack@eecs.umich.edu#include "sim/system.hh" 586735Sgblack@eecs.umich.edu 596019Shines@cs.fsu.edu/* Used by some KVM macros */ 607362Sgblack@eecs.umich.edu#define PAGE_SIZE pageSize 616735Sgblack@eecs.umich.edu 626019Shines@cs.fsu.eduvolatile bool timerOverflowed = false; 637362Sgblack@eecs.umich.edu 646735Sgblack@eecs.umich.edustatic void 656019Shines@cs.fsu.eduonTimerOverflow(int signo, siginfo_t *si, void *data) 667362Sgblack@eecs.umich.edu{ 676735Sgblack@eecs.umich.edu timerOverflowed = true; 686019Shines@cs.fsu.edu} 697362Sgblack@eecs.umich.edu 706735Sgblack@eecs.umich.eduBaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params) 716019Shines@cs.fsu.edu : BaseCPU(params), 727362Sgblack@eecs.umich.edu vm(*params->kvmVM), 736735Sgblack@eecs.umich.edu _status(Idle), 746019Shines@cs.fsu.edu dataPort(name() + ".dcache_port", this), 757652Sminkyu.jeong@arm.com instPort(name() + ".icache_port", this), 767652Sminkyu.jeong@arm.com threadContextDirty(true), 777652Sminkyu.jeong@arm.com kvmStateDirty(false), 788202SAli.Saidi@ARM.com vcpuID(vm.allocVCPUID()), vcpuFD(-1), vcpuMMapSize(0), 798202SAli.Saidi@ARM.com _kvmRun(NULL), mmioRing(NULL), 808202SAli.Saidi@ARM.com pageSize(sysconf(_SC_PAGE_SIZE)), 818518Sgeoffrey.blake@arm.com tickEvent(*this), 828518Sgeoffrey.blake@arm.com perfControlledByTimer(params->usePerfOverflow), 836735Sgblack@eecs.umich.edu hostFactor(params->hostFactor) 847362Sgblack@eecs.umich.edu{ 856735Sgblack@eecs.umich.edu if (pageSize == -1) 866735Sgblack@eecs.umich.edu panic("KVM: Failed to determine host page size (%i)\n", 876019Shines@cs.fsu.edu errno); 886735Sgblack@eecs.umich.edu 897400SAli.Saidi@ARM.com thread = new SimpleThread(this, 0, params->system, 906735Sgblack@eecs.umich.edu params->itb, params->dtb, params->isa[0]); 916735Sgblack@eecs.umich.edu thread->setStatus(ThreadContext::Halted); 926735Sgblack@eecs.umich.edu tc = thread->getTC(); 937400SAli.Saidi@ARM.com threadContexts.push_back(tc); 946735Sgblack@eecs.umich.edu 956735Sgblack@eecs.umich.edu setupCounters(); 966735Sgblack@eecs.umich.edu setupSignalHandler(); 976019Shines@cs.fsu.edu 986019Shines@cs.fsu.edu if (params->usePerfOverflow) 996019Shines@cs.fsu.edu runTimer.reset(new PerfKvmTimer(hwCycles, 1006735Sgblack@eecs.umich.edu KVM_TIMER_SIGNAL, 1016735Sgblack@eecs.umich.edu params->hostFactor, 1026735Sgblack@eecs.umich.edu params->clock)); 1037678Sgblack@eecs.umich.edu else 1046019Shines@cs.fsu.edu runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC, 1056735Sgblack@eecs.umich.edu params->hostFactor, 1066735Sgblack@eecs.umich.edu params->clock)); 1076735Sgblack@eecs.umich.edu} 1086019Shines@cs.fsu.edu 1096735Sgblack@eecs.umich.eduBaseKvmCPU::~BaseKvmCPU() 1106735Sgblack@eecs.umich.edu{ 1118303SAli.Saidi@ARM.com if (_kvmRun) 1128303SAli.Saidi@ARM.com munmap(_kvmRun, vcpuMMapSize); 1138303SAli.Saidi@ARM.com close(vcpuFD); 1148303SAli.Saidi@ARM.com} 1158303SAli.Saidi@ARM.com 1168303SAli.Saidi@ARM.comvoid 1177720Sgblack@eecs.umich.eduBaseKvmCPU::init() 1188205SAli.Saidi@ARM.com{ 1198205SAli.Saidi@ARM.com BaseCPU::init(); 1208205SAli.Saidi@ARM.com 1216735Sgblack@eecs.umich.edu if (numThreads != 1) 1226735Sgblack@eecs.umich.edu fatal("KVM: Multithreading not supported"); 1236735Sgblack@eecs.umich.edu 1246735Sgblack@eecs.umich.edu tc->initMemProxies(tc); 1256735Sgblack@eecs.umich.edu 1267093Sgblack@eecs.umich.edu // initialize CPU, including PC 1276735Sgblack@eecs.umich.edu if (FullSystem && !switchedOut()) 1286735Sgblack@eecs.umich.edu TheISA::initCPU(tc, tc->contextId()); 1296735Sgblack@eecs.umich.edu 1307302Sgblack@eecs.umich.edu mmio_req.setThreadContext(tc->contextId(), 0); 1316735Sgblack@eecs.umich.edu} 1328518Sgeoffrey.blake@arm.com 1338518Sgeoffrey.blake@arm.comvoid 1347720Sgblack@eecs.umich.eduBaseKvmCPU::startup() 1356735Sgblack@eecs.umich.edu{ 1366735Sgblack@eecs.umich.edu Kvm &kvm(vm.kvm); 1376735Sgblack@eecs.umich.edu 1386735Sgblack@eecs.umich.edu BaseCPU::startup(); 1396735Sgblack@eecs.umich.edu 1406735Sgblack@eecs.umich.edu assert(vcpuFD == -1); 1416735Sgblack@eecs.umich.edu 1426735Sgblack@eecs.umich.edu // Tell the VM that a CPU is about to start. 1436735Sgblack@eecs.umich.edu vm.cpuStartup(); 1446735Sgblack@eecs.umich.edu 1456735Sgblack@eecs.umich.edu // We can't initialize KVM CPUs in BaseKvmCPU::init() since we are 1466735Sgblack@eecs.umich.edu // not guaranteed that the parent KVM VM has initialized at that 1476735Sgblack@eecs.umich.edu // point. Initialize virtual CPUs here instead. 1486735Sgblack@eecs.umich.edu vcpuFD = vm.createVCPU(vcpuID); 1496735Sgblack@eecs.umich.edu 1506735Sgblack@eecs.umich.edu // Map the KVM run structure */ 1516735Sgblack@eecs.umich.edu vcpuMMapSize = kvm.getVCPUMMapSize(); 1526735Sgblack@eecs.umich.edu _kvmRun = (struct kvm_run *)mmap(0, vcpuMMapSize, 1536735Sgblack@eecs.umich.edu PROT_READ | PROT_WRITE, MAP_SHARED, 1546735Sgblack@eecs.umich.edu vcpuFD, 0); 1557093Sgblack@eecs.umich.edu if (_kvmRun == MAP_FAILED) 1567093Sgblack@eecs.umich.edu panic("KVM: Failed to map run data structure\n"); 1577720Sgblack@eecs.umich.edu 1587585SAli.Saidi@arm.com // Setup a pointer to the MMIO ring buffer if coalesced MMIO is 1597720Sgblack@eecs.umich.edu // available. The offset into the KVM's communication page is 1607720Sgblack@eecs.umich.edu // provided by the coalesced MMIO capability. 1617720Sgblack@eecs.umich.edu int mmioOffset(kvm.capCoalescedMMIO()); 1627720Sgblack@eecs.umich.edu if (mmioOffset) { 1637720Sgblack@eecs.umich.edu inform("KVM: Coalesced IO available\n"); 1647720Sgblack@eecs.umich.edu mmioRing = (struct kvm_coalesced_mmio_ring *)( 1657720Sgblack@eecs.umich.edu (char *)_kvmRun + (mmioOffset * pageSize)); 1666019Shines@cs.fsu.edu } else { 1677189Sgblack@eecs.umich.edu inform("KVM: Coalesced not supported by host OS\n"); 1687400SAli.Saidi@ARM.com } 1697678Sgblack@eecs.umich.edu} 1707400SAli.Saidi@ARM.com 1717400SAli.Saidi@ARM.comvoid 1727400SAli.Saidi@ARM.comBaseKvmCPU::regStats() 1738205SAli.Saidi@ARM.com{ 1747400SAli.Saidi@ARM.com using namespace Stats; 1757400SAli.Saidi@ARM.com 1767189Sgblack@eecs.umich.edu BaseCPU::regStats(); 1777189Sgblack@eecs.umich.edu 1787189Sgblack@eecs.umich.edu numInsts 1797678Sgblack@eecs.umich.edu .name(name() + ".committedInsts") 1807189Sgblack@eecs.umich.edu .desc("Number of instructions committed") 1817640Sgblack@eecs.umich.edu ; 1827189Sgblack@eecs.umich.edu 1837640Sgblack@eecs.umich.edu numVMExits 1847640Sgblack@eecs.umich.edu .name(name() + ".numVMExits") 1857640Sgblack@eecs.umich.edu .desc("total number of KVM exits") 1867640Sgblack@eecs.umich.edu ; 1877426Sgblack@eecs.umich.edu 1887426Sgblack@eecs.umich.edu numMMIO 1897189Sgblack@eecs.umich.edu .name(name() + ".numMMIO") 1907426Sgblack@eecs.umich.edu .desc("number of VM exits due to memory mapped IO") 1917426Sgblack@eecs.umich.edu ; 1927189Sgblack@eecs.umich.edu 1937189Sgblack@eecs.umich.edu numCoalescedMMIO 1947189Sgblack@eecs.umich.edu .name(name() + ".numCoalescedMMIO") 1957197Sgblack@eecs.umich.edu .desc("number of coalesced memory mapped IO requests") 1967678Sgblack@eecs.umich.edu ; 1977197Sgblack@eecs.umich.edu 1987197Sgblack@eecs.umich.edu numIO 1997197Sgblack@eecs.umich.edu .name(name() + ".numIO") 2007197Sgblack@eecs.umich.edu .desc("number of VM exits due to legacy IO") 2018063SAli.Saidi@ARM.com ; 2027197Sgblack@eecs.umich.edu 2037197Sgblack@eecs.umich.edu numHalt 2047197Sgblack@eecs.umich.edu .name(name() + ".numHalt") 2057720Sgblack@eecs.umich.edu .desc("number of VM exits due to wait for interrupt instructions") 2067720Sgblack@eecs.umich.edu ; 2077720Sgblack@eecs.umich.edu 2087720Sgblack@eecs.umich.edu numInterrupts 2097197Sgblack@eecs.umich.edu .name(name() + ".numInterrupts") 2107197Sgblack@eecs.umich.edu .desc("number of interrupts delivered") 2116019Shines@cs.fsu.edu ; 2126019Shines@cs.fsu.edu 2137362Sgblack@eecs.umich.edu numHypercalls 2147362Sgblack@eecs.umich.edu .name(name() + ".numHypercalls") 2157678Sgblack@eecs.umich.edu .desc("number of hypercalls") 2167362Sgblack@eecs.umich.edu ; 2178205SAli.Saidi@ARM.com} 2187362Sgblack@eecs.umich.edu 2197362Sgblack@eecs.umich.eduvoid 2207362Sgblack@eecs.umich.eduBaseKvmCPU::serializeThread(std::ostream &os, ThreadID tid) 2217362Sgblack@eecs.umich.edu{ 2227362Sgblack@eecs.umich.edu if (DTRACE(Checkpoint)) { 2237362Sgblack@eecs.umich.edu DPRINTF(Checkpoint, "KVM: Serializing thread %i:\n", tid); 2247362Sgblack@eecs.umich.edu dump(); 2257362Sgblack@eecs.umich.edu } 2268314Sgeoffrey.blake@arm.com 2278314Sgeoffrey.blake@arm.com // Update the thread context so we have something to serialize. 2287362Sgblack@eecs.umich.edu syncThreadContext(); 2297362Sgblack@eecs.umich.edu 2307652Sminkyu.jeong@arm.com assert(tid == 0); 2317678Sgblack@eecs.umich.edu assert(_status == Idle); 2327652Sminkyu.jeong@arm.com thread->serialize(os); 2337652Sminkyu.jeong@arm.com} 2347652Sminkyu.jeong@arm.com 2357652Sminkyu.jeong@arm.comvoid 2367652Sminkyu.jeong@arm.comBaseKvmCPU::unserializeThread(Checkpoint *cp, const std::string §ion, 2377720Sgblack@eecs.umich.edu ThreadID tid) 2387720Sgblack@eecs.umich.edu{ 2397720Sgblack@eecs.umich.edu DPRINTF(Checkpoint, "KVM: Unserialize thread %i:\n", tid); 2407720Sgblack@eecs.umich.edu 2417652Sminkyu.jeong@arm.com assert(tid == 0); 2427652Sminkyu.jeong@arm.com assert(_status == Idle); 2438202SAli.Saidi@ARM.com thread->unserialize(cp, section); 2448202SAli.Saidi@ARM.com threadContextDirty = true; 2458202SAli.Saidi@ARM.com} 2468202SAli.Saidi@ARM.com 2478202SAli.Saidi@ARM.comunsigned int 2488202SAli.Saidi@ARM.comBaseKvmCPU::drain(DrainManager *dm) 2498202SAli.Saidi@ARM.com{ 2508202SAli.Saidi@ARM.com if (switchedOut()) 2518202SAli.Saidi@ARM.com return 0; 2528202SAli.Saidi@ARM.com 2538202SAli.Saidi@ARM.com DPRINTF(Kvm, "drain\n"); 2547678Sgblack@eecs.umich.edu 2557678Sgblack@eecs.umich.edu // De-schedule the tick event so we don't insert any more MMIOs 2567678Sgblack@eecs.umich.edu // into the system while it is draining. 2577678Sgblack@eecs.umich.edu if (tickEvent.scheduled()) 2587362Sgblack@eecs.umich.edu deschedule(tickEvent); 2598518Sgeoffrey.blake@arm.com 2608518Sgeoffrey.blake@arm.com _status = Idle; 2618518Sgeoffrey.blake@arm.com return 0; 2628518Sgeoffrey.blake@arm.com} 2638518Sgeoffrey.blake@arm.com 2648518Sgeoffrey.blake@arm.comvoid 2658518Sgeoffrey.blake@arm.comBaseKvmCPU::drainResume() 2668518Sgeoffrey.blake@arm.com{ 2678518Sgeoffrey.blake@arm.com assert(!tickEvent.scheduled()); 2688518Sgeoffrey.blake@arm.com 2698518Sgeoffrey.blake@arm.com // We might have been switched out. In that case, we don't need to 2708518Sgeoffrey.blake@arm.com // do anything. 2716735Sgblack@eecs.umich.edu if (switchedOut()) 2726019Shines@cs.fsu.edu return; 2736019Shines@cs.fsu.edu 274 DPRINTF(Kvm, "drainResume\n"); 275 verifyMemoryMode(); 276 277 // The tick event is de-scheduled as a part of the draining 278 // process. Re-schedule it if the thread context is active. 279 if (tc->status() == ThreadContext::Active) { 280 schedule(tickEvent, nextCycle()); 281 _status = Running; 282 } else { 283 _status = Idle; 284 } 285} 286 287void 288BaseKvmCPU::switchOut() 289{ 290 DPRINTF(Kvm, "switchOut\n"); 291 292 // Make sure to update the thread context in case, the new CPU 293 // will need to access it. 294 syncThreadContext(); 295 296 BaseCPU::switchOut(); 297 298 // We should have drained prior to executing a switchOut, which 299 // means that the tick event shouldn't be scheduled and the CPU is 300 // idle. 301 assert(!tickEvent.scheduled()); 302 assert(_status == Idle); 303} 304 305void 306BaseKvmCPU::takeOverFrom(BaseCPU *cpu) 307{ 308 DPRINTF(Kvm, "takeOverFrom\n"); 309 310 BaseCPU::takeOverFrom(cpu); 311 312 // We should have drained prior to executing a switchOut, which 313 // means that the tick event shouldn't be scheduled and the CPU is 314 // idle. 315 assert(!tickEvent.scheduled()); 316 assert(_status == Idle); 317 assert(threadContexts.size() == 1); 318 319 // The BaseCPU updated the thread context, make sure that we 320 // synchronize next time we enter start the CPU. 321 threadContextDirty = true; 322} 323 324void 325BaseKvmCPU::verifyMemoryMode() const 326{ 327 if (!(system->isAtomicMode() && system->bypassCaches())) { 328 fatal("The KVM-based CPUs requires the memory system to be in the " 329 "'atomic_noncaching' mode.\n"); 330 } 331} 332 333void 334BaseKvmCPU::wakeup() 335{ 336 DPRINTF(Kvm, "wakeup()\n"); 337 338 if (thread->status() != ThreadContext::Suspended) 339 return; 340 341 thread->activate(); 342} 343 344void 345BaseKvmCPU::activateContext(ThreadID thread_num, Cycles delay) 346{ 347 DPRINTF(Kvm, "ActivateContext %d (%d cycles)\n", thread_num, delay); 348 349 assert(thread_num == 0); 350 assert(thread); 351 352 assert(_status == Idle); 353 assert(!tickEvent.scheduled()); 354 355 numCycles += ticksToCycles(thread->lastActivate - thread->lastSuspend) 356 * hostFactor; 357 358 schedule(tickEvent, clockEdge(delay)); 359 _status = Running; 360} 361 362 363void 364BaseKvmCPU::suspendContext(ThreadID thread_num) 365{ 366 DPRINTF(Kvm, "SuspendContext %d\n", thread_num); 367 368 assert(thread_num == 0); 369 assert(thread); 370 371 if (_status == Idle) 372 return; 373 374 assert(_status == Running); 375 376 // The tick event may no be scheduled if the quest has requested 377 // the monitor to wait for interrupts. The normal CPU models can 378 // get their tick events descheduled by quiesce instructions, but 379 // that can't happen here. 380 if (tickEvent.scheduled()) 381 deschedule(tickEvent); 382 383 _status = Idle; 384} 385 386void 387BaseKvmCPU::deallocateContext(ThreadID thread_num) 388{ 389 // for now, these are equivalent 390 suspendContext(thread_num); 391} 392 393void 394BaseKvmCPU::haltContext(ThreadID thread_num) 395{ 396 // for now, these are equivalent 397 suspendContext(thread_num); 398} 399 400ThreadContext * 401BaseKvmCPU::getContext(int tn) 402{ 403 assert(tn == 0); 404 syncThreadContext(); 405 return tc; 406} 407 408 409Counter 410BaseKvmCPU::totalInsts() const 411{ 412 return hwInstructions.read(); 413} 414 415Counter 416BaseKvmCPU::totalOps() const 417{ 418 hack_once("Pretending totalOps is equivalent to totalInsts()\n"); 419 return hwInstructions.read(); 420} 421 422void 423BaseKvmCPU::dump() 424{ 425 inform("State dumping not implemented."); 426} 427 428void 429BaseKvmCPU::tick() 430{ 431 assert(_status == Running); 432 433 DPRINTF(KvmRun, "Entering KVM...\n"); 434 435 Tick ticksToExecute(mainEventQueue.nextTick() - curTick()); 436 Tick ticksExecuted(kvmRun(ticksToExecute)); 437 438 Tick delay(ticksExecuted + handleKvmExit()); 439 440 switch (_status) { 441 case Running: 442 schedule(tickEvent, clockEdge(ticksToCycles(delay))); 443 break; 444 445 default: 446 /* The CPU is halted or waiting for an interrupt from a 447 * device. Don't start it. */ 448 break; 449 } 450} 451 452Tick 453BaseKvmCPU::kvmRun(Tick ticks) 454{ 455 uint64_t baseCycles(hwCycles.read()); 456 uint64_t baseInstrs(hwInstructions.read()); 457 458 // We might need to update the KVM state. 459 syncKvmState(); 460 // Entering into KVM implies that we'll have to reload the thread 461 // context from KVM if we want to access it. Flag the KVM state as 462 // dirty with respect to the cached thread context. 463 kvmStateDirty = true; 464 465 if (ticks < runTimer->resolution()) { 466 DPRINTF(KvmRun, "KVM: Adjusting tick count (%i -> %i)\n", 467 ticks, runTimer->resolution()); 468 ticks = runTimer->resolution(); 469 } 470 471 DPRINTF(KvmRun, "KVM: Executing for %i ticks\n", ticks); 472 timerOverflowed = false; 473 474 // Arm the run timer and start the cycle timer if it isn't 475 // controlled by the overflow timer. Starting/stopping the cycle 476 // timer automatically starts the other perf timers as they are in 477 // the same counter group. 478 runTimer->arm(ticks); 479 if (!perfControlledByTimer) 480 hwCycles.start(); 481 482 if (ioctl(KVM_RUN) == -1) { 483 if (errno != EINTR) 484 panic("KVM: Failed to start virtual CPU (errno: %i)\n", 485 errno); 486 } 487 488 runTimer->disarm(); 489 if (!perfControlledByTimer) 490 hwCycles.stop(); 491 492 493 const uint64_t hostCyclesExecuted(hwCycles.read() - baseCycles); 494 const uint64_t simCyclesExecuted(hostCyclesExecuted * hostFactor); 495 const uint64_t instsExecuted(hwInstructions.read() - baseInstrs); 496 const Tick ticksExecuted(runTimer->ticksFromHostCycles(hostCyclesExecuted)); 497 498 if (ticksExecuted < ticks && 499 timerOverflowed && 500 _kvmRun->exit_reason == KVM_EXIT_INTR) { 501 // TODO: We should probably do something clever here... 502 warn("KVM: Early timer event, requested %i ticks but got %i ticks.\n", 503 ticks, ticksExecuted); 504 } 505 506 /* Update statistics */ 507 numCycles += simCyclesExecuted;; 508 ++numVMExits; 509 numInsts += instsExecuted; 510 511 DPRINTF(KvmRun, "KVM: Executed %i instructions in %i cycles (%i ticks, sim cycles: %i).\n", 512 instsExecuted, hostCyclesExecuted, ticksExecuted, simCyclesExecuted); 513 514 return ticksExecuted + flushCoalescedMMIO(); 515} 516 517void 518BaseKvmCPU::kvmNonMaskableInterrupt() 519{ 520 ++numInterrupts; 521 if (ioctl(KVM_NMI) == -1) 522 panic("KVM: Failed to deliver NMI to virtual CPU\n"); 523} 524 525void 526BaseKvmCPU::kvmInterrupt(const struct kvm_interrupt &interrupt) 527{ 528 ++numInterrupts; 529 if (ioctl(KVM_INTERRUPT, (void *)&interrupt) == -1) 530 panic("KVM: Failed to deliver interrupt to virtual CPU\n"); 531} 532 533void 534BaseKvmCPU::getRegisters(struct kvm_regs ®s) const 535{ 536 if (ioctl(KVM_GET_REGS, ®s) == -1) 537 panic("KVM: Failed to get guest registers\n"); 538} 539 540void 541BaseKvmCPU::setRegisters(const struct kvm_regs ®s) 542{ 543 if (ioctl(KVM_SET_REGS, (void *)®s) == -1) 544 panic("KVM: Failed to set guest registers\n"); 545} 546 547void 548BaseKvmCPU::getSpecialRegisters(struct kvm_sregs ®s) const 549{ 550 if (ioctl(KVM_GET_SREGS, ®s) == -1) 551 panic("KVM: Failed to get guest special registers\n"); 552} 553 554void 555BaseKvmCPU::setSpecialRegisters(const struct kvm_sregs ®s) 556{ 557 if (ioctl(KVM_SET_SREGS, (void *)®s) == -1) 558 panic("KVM: Failed to set guest special registers\n"); 559} 560 561void 562BaseKvmCPU::getFPUState(struct kvm_fpu &state) const 563{ 564 if (ioctl(KVM_GET_FPU, &state) == -1) 565 panic("KVM: Failed to get guest FPU state\n"); 566} 567 568void 569BaseKvmCPU::setFPUState(const struct kvm_fpu &state) 570{ 571 if (ioctl(KVM_SET_FPU, (void *)&state) == -1) 572 panic("KVM: Failed to set guest FPU state\n"); 573} 574 575 576void 577BaseKvmCPU::setOneReg(uint64_t id, const void *addr) 578{ 579#ifdef KVM_SET_ONE_REG 580 struct kvm_one_reg reg; 581 reg.id = id; 582 reg.addr = (uint64_t)addr; 583 584 if (ioctl(KVM_SET_ONE_REG, ®) == -1) { 585 panic("KVM: Failed to set register (0x%x) value (errno: %i)\n", 586 id, errno); 587 } 588#else 589 panic("KVM_SET_ONE_REG is unsupported on this platform.\n"); 590#endif 591} 592 593void 594BaseKvmCPU::getOneReg(uint64_t id, void *addr) const 595{ 596#ifdef KVM_GET_ONE_REG 597 struct kvm_one_reg reg; 598 reg.id = id; 599 reg.addr = (uint64_t)addr; 600 601 if (ioctl(KVM_GET_ONE_REG, ®) == -1) { 602 panic("KVM: Failed to get register (0x%x) value (errno: %i)\n", 603 id, errno); 604 } 605#else 606 panic("KVM_GET_ONE_REG is unsupported on this platform.\n"); 607#endif 608} 609 610std::string 611BaseKvmCPU::getAndFormatOneReg(uint64_t id) const 612{ 613#ifdef KVM_GET_ONE_REG 614 std::ostringstream ss; 615 616 ss.setf(std::ios::hex, std::ios::basefield); 617 ss.setf(std::ios::showbase); 618#define HANDLE_INTTYPE(len) \ 619 case KVM_REG_SIZE_U ## len: { \ 620 uint ## len ## _t value; \ 621 getOneReg(id, &value); \ 622 ss << value; \ 623 } break 624 625#define HANDLE_ARRAY(len) \ 626 case KVM_REG_SIZE_U ## len: { \ 627 uint8_t value[len / 8]; \ 628 getOneReg(id, value); \ 629 ss << "[" << value[0]; \ 630 for (int i = 1; i < len / 8; ++i) \ 631 ss << ", " << value[i]; \ 632 ss << "]"; \ 633 } break 634 635 switch (id & KVM_REG_SIZE_MASK) { 636 HANDLE_INTTYPE(8); 637 HANDLE_INTTYPE(16); 638 HANDLE_INTTYPE(32); 639 HANDLE_INTTYPE(64); 640 HANDLE_ARRAY(128); 641 HANDLE_ARRAY(256); 642 HANDLE_ARRAY(512); 643 HANDLE_ARRAY(1024); 644 default: 645 ss << "??"; 646 } 647 648#undef HANDLE_INTTYPE 649#undef HANDLE_ARRAY 650 651 return ss.str(); 652#else 653 panic("KVM_GET_ONE_REG is unsupported on this platform.\n"); 654#endif 655} 656 657void 658BaseKvmCPU::syncThreadContext() 659{ 660 if (!kvmStateDirty) 661 return; 662 663 assert(!threadContextDirty); 664 665 updateThreadContext(); 666 kvmStateDirty = false; 667} 668 669void 670BaseKvmCPU::syncKvmState() 671{ 672 if (!threadContextDirty) 673 return; 674 675 assert(!kvmStateDirty); 676 677 updateKvmState(); 678 threadContextDirty = false; 679} 680 681Tick 682BaseKvmCPU::handleKvmExit() 683{ 684 DPRINTF(KvmRun, "handleKvmExit (exit_reason: %i)\n", _kvmRun->exit_reason); 685 686 switch (_kvmRun->exit_reason) { 687 case KVM_EXIT_UNKNOWN: 688 return handleKvmExitUnknown(); 689 690 case KVM_EXIT_EXCEPTION: 691 return handleKvmExitException(); 692 693 case KVM_EXIT_IO: 694 ++numIO; 695 return handleKvmExitIO(); 696 697 case KVM_EXIT_HYPERCALL: 698 ++numHypercalls; 699 return handleKvmExitHypercall(); 700 701 case KVM_EXIT_HLT: 702 /* The guest has halted and is waiting for interrupts */ 703 DPRINTF(Kvm, "handleKvmExitHalt\n"); 704 ++numHalt; 705 706 // Suspend the thread until the next interrupt arrives 707 thread->suspend(); 708 709 // This is actually ignored since the thread is suspended. 710 return 0; 711 712 case KVM_EXIT_MMIO: 713 /* Service memory mapped IO requests */ 714 DPRINTF(KvmIO, "KVM: Handling MMIO (w: %u, addr: 0x%x, len: %u)\n", 715 _kvmRun->mmio.is_write, 716 _kvmRun->mmio.phys_addr, _kvmRun->mmio.len); 717 718 ++numMMIO; 719 return doMMIOAccess(_kvmRun->mmio.phys_addr, _kvmRun->mmio.data, 720 _kvmRun->mmio.len, _kvmRun->mmio.is_write); 721 722 case KVM_EXIT_IRQ_WINDOW_OPEN: 723 return handleKvmExitIRQWindowOpen(); 724 725 case KVM_EXIT_FAIL_ENTRY: 726 return handleKvmExitFailEntry(); 727 728 case KVM_EXIT_INTR: 729 /* KVM was interrupted by a signal, restart it in the next 730 * tick. */ 731 return 0; 732 733 case KVM_EXIT_INTERNAL_ERROR: 734 panic("KVM: Internal error (suberror: %u)\n", 735 _kvmRun->internal.suberror); 736 737 default: 738 panic("KVM: Unexpected exit (exit_reason: %u)\n", _kvmRun->exit_reason); 739 } 740} 741 742Tick 743BaseKvmCPU::handleKvmExitIO() 744{ 745 panic("KVM: Unhandled guest IO (dir: %i, size: %i, port: 0x%x, count: %i)\n", 746 _kvmRun->io.direction, _kvmRun->io.size, 747 _kvmRun->io.port, _kvmRun->io.count); 748} 749 750Tick 751BaseKvmCPU::handleKvmExitHypercall() 752{ 753 panic("KVM: Unhandled hypercall\n"); 754} 755 756Tick 757BaseKvmCPU::handleKvmExitIRQWindowOpen() 758{ 759 warn("KVM: Unhandled IRQ window.\n"); 760 return 0; 761} 762 763 764Tick 765BaseKvmCPU::handleKvmExitUnknown() 766{ 767 panic("KVM: Unknown error when starting vCPU (hw reason: 0x%llx)\n", 768 _kvmRun->hw.hardware_exit_reason); 769} 770 771Tick 772BaseKvmCPU::handleKvmExitException() 773{ 774 panic("KVM: Got exception when starting vCPU " 775 "(exception: %u, error_code: %u)\n", 776 _kvmRun->ex.exception, _kvmRun->ex.error_code); 777} 778 779Tick 780BaseKvmCPU::handleKvmExitFailEntry() 781{ 782 panic("KVM: Failed to enter virtualized mode (hw reason: 0x%llx)\n", 783 _kvmRun->fail_entry.hardware_entry_failure_reason); 784} 785 786Tick 787BaseKvmCPU::doMMIOAccess(Addr paddr, void *data, int size, bool write) 788{ 789 mmio_req.setPhys(paddr, size, Request::UNCACHEABLE, dataMasterId()); 790 791 const MemCmd cmd(write ? MemCmd::WriteReq : MemCmd::ReadReq); 792 Packet pkt(&mmio_req, cmd); 793 pkt.dataStatic(data); 794 return dataPort.sendAtomic(&pkt); 795} 796 797int 798BaseKvmCPU::ioctl(int request, long p1) const 799{ 800 if (vcpuFD == -1) 801 panic("KVM: CPU ioctl called before initialization\n"); 802 803 return ::ioctl(vcpuFD, request, p1); 804} 805 806Tick 807BaseKvmCPU::flushCoalescedMMIO() 808{ 809 if (!mmioRing) 810 return 0; 811 812 DPRINTF(KvmIO, "KVM: Flushing the coalesced MMIO ring buffer\n"); 813 814 // TODO: We might need to do synchronization when we start to 815 // support multiple CPUs 816 Tick ticks(0); 817 while (mmioRing->first != mmioRing->last) { 818 struct kvm_coalesced_mmio &ent( 819 mmioRing->coalesced_mmio[mmioRing->first]); 820 821 DPRINTF(KvmIO, "KVM: Handling coalesced MMIO (addr: 0x%x, len: %u)\n", 822 ent.phys_addr, ent.len); 823 824 ++numCoalescedMMIO; 825 ticks += doMMIOAccess(ent.phys_addr, ent.data, ent.len, true); 826 827 mmioRing->first = (mmioRing->first + 1) % KVM_COALESCED_MMIO_MAX; 828 } 829 830 return ticks; 831} 832 833void 834BaseKvmCPU::setupSignalHandler() 835{ 836 struct sigaction sa; 837 838 memset(&sa, 0, sizeof(sa)); 839 sa.sa_sigaction = onTimerOverflow; 840 sa.sa_flags = SA_SIGINFO | SA_RESTART; 841 if (sigaction(KVM_TIMER_SIGNAL, &sa, NULL) == -1) 842 panic("KVM: Failed to setup vCPU signal handler\n"); 843} 844 845void 846BaseKvmCPU::setupCounters() 847{ 848 DPRINTF(Kvm, "Attaching cycle counter...\n"); 849 PerfKvmCounterConfig cfgCycles(PERF_TYPE_HARDWARE, 850 PERF_COUNT_HW_CPU_CYCLES); 851 cfgCycles.disabled(true) 852 .pinned(true); 853 854 if (perfControlledByTimer) { 855 // We need to configure the cycles counter to send overflows 856 // since we are going to use it to trigger timer signals that 857 // trap back into m5 from KVM. In practice, this means that we 858 // need to set some non-zero sample period that gets 859 // overridden when the timer is armed. 860 cfgCycles.wakeupEvents(1) 861 .samplePeriod(42); 862 } 863 864 hwCycles.attach(cfgCycles, 865 0); // TID (0 => currentThread) 866 867 DPRINTF(Kvm, "Attaching instruction counter...\n"); 868 PerfKvmCounterConfig cfgInstructions(PERF_TYPE_HARDWARE, 869 PERF_COUNT_HW_INSTRUCTIONS); 870 hwInstructions.attach(cfgInstructions, 871 0, // TID (0 => currentThread) 872 hwCycles); 873} 874