diff options
Diffstat (limited to 'src/kernel')
-rw-r--r-- | src/kernel/intmsghandler.C | 17 | ||||
-rw-r--r-- | src/kernel/misc.C | 95 | ||||
-rw-r--r-- | src/kernel/syscall.C | 15 |
3 files changed, 121 insertions, 6 deletions
diff --git a/src/kernel/intmsghandler.C b/src/kernel/intmsghandler.C index a2f2fb096..538d322ae 100644 --- a/src/kernel/intmsghandler.C +++ b/src/kernel/intmsghandler.C @@ -5,7 +5,7 @@ /* */ /* IBM CONFIDENTIAL */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2012 */ +/* COPYRIGHT International Business Machines Corp. 2011,2013 */ /* */ /* p1 */ /* */ @@ -76,7 +76,7 @@ void InterruptMsgHdlr::handleInterrupt() if( cv_ipc_base_address != 0 ) { - uint64_t xirrAddress = cv_ipc_base_address; + uint64_t xirrAddress = cv_ipc_base_address; xirrAddress += mmio_offset(pir); // Add offset for this cpu xirrAddress += XIRR_ADDR_OFFSET; // Add offset for XIRR register @@ -144,6 +144,19 @@ void InterruptMsgHdlr::addCpuCore(uint64_t i_pir) } } +void InterruptMsgHdlr::sendIPI(uint64_t i_pir) +{ + uint64_t mfrrAddress = cv_ipc_base_address; + mfrrAddress += mmio_offset(i_pir); + mfrrAddress += MFRR_ADDR_OFFSET; + + register uint8_t data = 0; + + eieio(); sync(); + MAGIC_INSTRUCTION(MAGIC_SIMICS_CORESTATESAVE); + asm volatile("stbcix %0,0,%1" :: "r" (data) , "r" (mfrrAddress)); +} + MessageHandler::HandleResult InterruptMsgHdlr::handleResponse ( msg_sys_types_t i_type, diff --git a/src/kernel/misc.C b/src/kernel/misc.C index f73bf2947..9fd511c33 100644 --- a/src/kernel/misc.C +++ b/src/kernel/misc.C @@ -34,6 +34,7 @@ #include <kernel/pagemgr.H> #include <kernel/vmmmgr.H> // INITIAL_MEM_SIZE #include <kernel/memstate.H> +#include <kernel/intmsghandler.H> extern "C" void kernel_shutdown(size_t, uint64_t, uint64_t, uint64_t) NO_RETURN; @@ -66,7 +67,7 @@ namespace KernelMisc status); } - // Call to set the Core Scratch Reg 0 with the status + // Call to set the Core Scratch Reg 0 with the status updateScratchReg(MMIO_SCRATCH_PROGRESS_CODE, status); } @@ -223,6 +224,98 @@ namespace KernelMisc kassert(false); } + void WinkleAll::masterPreWork() + { + printk("Winkle all - "); + + // Save away the current timebase. All threads are in this object + // now so they're not going to be using the time for anything else. + iv_timebase = getTB(); + } + + void WinkleAll::activeMainWork() + { + cpu_t* cpu = CpuManager::getCurrentCPU(); + + // Return current task to run-queue so it isn't lost. + cpu->scheduler->returnRunnable(); + TaskManager::setCurrentTask(cpu->idle_task); + + // Clear LPCR values that wakes up from winkle. LPCR[49, 50, 51] + // Otherwise, there may be an interrupt pending or something that + // prevents us from fully entering winkle. + setLPCR(getLPCR() & (~0x0000000000007000)); + + // Deactivate CPU from kernel. + cpu->winkled = true; + CpuManager::deactivateCPU(cpu); + + // Create kernel save area and store ptr in bottom of kernel stack. + task_t* saveArea = new task_t; + memset(saveArea, '\0', sizeof(task_t)); + saveArea->context.msr_mask = 0xD030; // EE, ME, PR, IR, DR. + *(reinterpret_cast<task_t**>(cpu->kernel_stack_bottom)) = saveArea; + + // Execute winkle. + kernel_execute_winkle(saveArea); + + // Re-activate CPU in kernel and re-init VMM SPRs. + delete saveArea; + cpu->winkled = false; + CpuManager::activateCPU(cpu); + VmmManager::init_slb(); + + // Wake up all the other threads. + if(__sync_bool_compare_and_swap(&iv_firstThread, 0, 1)) + { + for(uint64_t i = 0; i < KERNEL_MAX_SUPPORTED_CPUS_PER_NODE * + KERNEL_MAX_SUPPORTED_NODES; i++) + { + cpu_t* slave = CpuManager::getCpu(i); + + if ((NULL != slave) && (slave != cpu)) + { + if (slave->winkled) + { + InterruptMsgHdlr::sendIPI(i); + } + } + } + }; + + // Sync timebase. + if (getTB() < iv_timebase) + { + setTB(iv_timebase); + } + + // Select a new task if not the master CPU. Master CPU will resume + // the code that called cpu_master_winkle(). + if (!cpu->master) + { + cpu->scheduler->setNextRunnable(); + } + + } + + void WinkleAll::masterPostWork() + { + printk("Awake!\n"); + + // Restore caller of cpu_all_winkle(). + iv_caller->state = TASK_STATE_RUNNING; + TaskManager::setCurrentTask(iv_caller); + } + + void WinkleAll::nonactiveMainWork() + { + // Race condition that should not occur... + // + // Attempted to winkle the threads and another thread came online in + // the process. + kassert(false); + } + int expand_full_cache() { static bool executed = false; diff --git a/src/kernel/syscall.C b/src/kernel/syscall.C index 71bd1a995..985d9c0ab 100644 --- a/src/kernel/syscall.C +++ b/src/kernel/syscall.C @@ -700,12 +700,13 @@ namespace Systemcalls } }; - /** Winkle all the threads so that the runtime SLW image can be loaded. */ + /** Winkle all the threads. */ void CpuWinkle(task_t *t) { cpu_t* cpu = CpuManager::getCurrentCPU(); - if ((CpuManager::getCpuCount() > CpuManager::getThreadCount()) || + if ((WINKLE_SCOPE_MASTER == TASK_GETARG0(t) && + (CpuManager::getCpuCount() > CpuManager::getThreadCount())) || (!cpu->master)) { TASK_SETRTN(t, -EDEADLK); @@ -713,7 +714,15 @@ namespace Systemcalls else { TASK_SETRTN(t, 0); - KernelMisc::WinkleCore* deferred = new KernelMisc::WinkleCore(t); + DeferredWork* deferred = NULL; + if (WINKLE_SCOPE_MASTER == TASK_GETARG0(t)) + { + deferred = new KernelMisc::WinkleCore(t); + } + else + { + deferred = new KernelMisc::WinkleAll(t); + } t->state = TASK_STATE_BLOCK_USRSPACE; t->state_info = deferred; DeferredQueue::insert(deferred); |