summaryrefslogtreecommitdiffstats
path: root/src/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/cpumgr.C101
-rw-r--r--src/kernel/deferred.C268
-rw-r--r--src/kernel/exception.C30
-rw-r--r--src/kernel/kernel.C47
-rw-r--r--src/kernel/makefile8
-rw-r--r--src/kernel/misc.C82
-rw-r--r--src/kernel/start.S181
-rw-r--r--src/kernel/syscall.C53
8 files changed, 618 insertions, 152 deletions
diff --git a/src/kernel/cpumgr.C b/src/kernel/cpumgr.C
index 943ef48fd..909661cca 100644
--- a/src/kernel/cpumgr.C
+++ b/src/kernel/cpumgr.C
@@ -38,13 +38,13 @@
#include <kernel/heapmgr.H>
#include <kernel/intmsghandler.H>
#include <errno.h>
+#include <kernel/deferred.H>
+#include <kernel/misc.H>
cpu_t** CpuManager::cv_cpus = NULL;
bool CpuManager::cv_shutdown_requested = false;
uint64_t CpuManager::cv_shutdown_status = 0;
-Barrier CpuManager::cv_barrier;
-bool CpuManager::cv_defrag = false;
-size_t CpuManager::cv_cpuCount = 0;
+size_t CpuManager::cv_cpuSeq = 0;
bool CpuManager::cv_forcedMemPeriodic = false;
InteractiveDebug CpuManager::cv_interactive_debug;
@@ -95,6 +95,35 @@ void CpuManager::requestShutdown(uint64_t i_status)
cv_shutdown_status = i_status;
__sync_synchronize();
cv_shutdown_requested = true;
+
+ class ExecuteShutdown : public DeferredWork
+ {
+ public:
+ void masterPreWork()
+ {
+ // The stats can be retrieved from global variables as needed.
+ // This can be uncommented for debug if desired
+ #ifdef __MEMSTATS__
+ if(c->master)
+ HeapManager::stats();
+ #endif
+ }
+
+ void activeMainWork()
+ {
+ KernelMisc::shutdown();
+ }
+
+ void nonactiveMainWork()
+ {
+ // Something wasn't synchronized correctly if we got to here.
+ // Should not have CPUs coming online while trying to execute
+ // a shutdown.
+ kassert(false);
+ }
+ };
+
+ DeferredQueue::insert(new ExecuteShutdown());
}
void CpuManager::startCPU(ssize_t i)
@@ -163,8 +192,20 @@ void CpuManager::activateCPU(cpu_t * i_cpu)
{
// Set active.
i_cpu->active = true;
- __sync_add_and_fetch(&cv_cpuCount, 1);
- lwsync();
+
+ // Update sequence ID.
+ do
+ {
+ uint64_t old_seq = cv_cpuSeq;
+ i_cpu->cpu_start_seqid = old_seq + 1 + (1ull << 32);
+
+ if (__sync_bool_compare_and_swap(&cv_cpuSeq, old_seq,
+ i_cpu->cpu_start_seqid))
+ {
+ break;
+ }
+ } while (1);
+ i_cpu->cpu_start_seqid >>= 32;
// Verify / set SPRs.
uint64_t msr = getMSR();
@@ -172,6 +213,24 @@ void CpuManager::activateCPU(cpu_t * i_cpu)
setLPCR(WAKEUP_LPCR_VALUE);
}
+void CpuManager::deactivateCPU(cpu_t * i_cpu)
+{
+ // Set inactive.
+ i_cpu->active = false;
+
+ // Update sequence ID.
+ do
+ {
+ uint64_t old_seq = cv_cpuSeq;
+ uint64_t new_seq = old_seq - 1 + (1ull << 32);
+
+ if (__sync_bool_compare_and_swap(&cv_cpuSeq, old_seq, new_seq))
+ {
+ break;
+ }
+ } while(1);
+}
+
void CpuManager::executePeriodics(cpu_t * i_cpu)
{
if(i_cpu->master)
@@ -211,32 +270,22 @@ void CpuManager::executePeriodics(cpu_t * i_cpu)
if((0 == (i_cpu->periodic_count % CPU_PERIODIC_DEFRAG)) ||
(forceMemoryPeriodic))
{
- // set up barrier based on # cpus cv_barrier;
- // TODO whatif other cpus become active?
- isync(); // Ensure all instructions complete before this point, so
- // we don't get a stale shutdown_requested.
- if(!cv_shutdown_requested)
+ class MemoryCoalesce : public DeferredWork
{
- cv_barrier.init(cv_cpuCount);
- lwsync(); // Ensure barrier init is globally visible before
- // setting defrag = true.
- cv_defrag = true;
- }
+ public:
+ void masterPreWork()
+ {
+ HeapManager::coalesce();
+ PageManager::coalesce();
+ }
+ };
+
+ DeferredQueue::insert(new MemoryCoalesce());
}
}
- if(cv_defrag)
- {
- cv_barrier.wait();
- if(i_cpu->master)
- {
- HeapManager::coalesce();
- PageManager::coalesce();
- cv_defrag = false;
- }
+ DeferredQueue::execute();
- cv_barrier.wait();
- }
}
int CpuManager::startCore(uint64_t pir)
diff --git a/src/kernel/deferred.C b/src/kernel/deferred.C
new file mode 100644
index 000000000..dc6494857
--- /dev/null
+++ b/src/kernel/deferred.C
@@ -0,0 +1,268 @@
+/* IBM_PROLOG_BEGIN_TAG
+ * This is an automatically generated prolog.
+ *
+ * $Source: src/kernel/deferred.C $
+ *
+ * IBM CONFIDENTIAL
+ *
+ * COPYRIGHT International Business Machines Corp. 2012
+ *
+ * p1
+ *
+ * Object Code Only (OCO) source materials
+ * Licensed Internal Code Source Materials
+ * IBM HostBoot Licensed Internal Code
+ *
+ * The source code for this program is not published or other-
+ * wise divested of its trade secrets, irrespective of what has
+ * been deposited with the U.S. Copyright Office.
+ *
+ * Origin: 30
+ *
+ * IBM_PROLOG_END_TAG
+ */
+#include <kernel/deferred.H>
+#include <kernel/cpumgr.H>
+#include <util/singleton.H>
+#include <assert.h>
+#include <arch/ppc.H>
+
+/** Extract the DeferredWork pointer part of a iv_cpus_and_next instance var. */
+#define DEFERRED_QUEUE_GET_NEXT_PTR(item) \
+ reinterpret_cast<DeferredWork*>((item)->iv_cpus_and_next & 0xFFFFFFFF)
+
+/** Set the DeferredWork pointer part of a iv_cpus_and_next instance var. */
+#define DEFERRED_QUEUE_SET_NEXT_PTR(item) \
+ (item)->iv_cpus_and_next = \
+ ((item)->iv_cpus_and_next & 0xFFFFFFFF00000000ull) | \
+ reinterpret_cast<uint64_t>(item)
+
+/** Extract the CPU count portion of a iv_cpus_and_next instance var. */
+#define DEFERRED_QUEUE_GET_CPU_COUNT(item) (item)->iv_cpus_and_next >> 32
+
+// Initialize the work queue.
+DeferredQueue::DeferredQueue() : lock(), iv_cpus_and_next(0) {}
+
+DeferredQueue::~DeferredQueue()
+{
+ // Ensure that all work is completed.
+ kassert(0 == iv_cpus_and_next);
+}
+
+void DeferredQueue::insert(DeferredWork* i_work)
+{
+ // Call singleton insert.
+ Singleton<DeferredQueue>::instance()._insert(i_work);
+}
+
+void DeferredQueue::execute()
+{
+ // Call singleton execute.
+ Singleton<DeferredQueue>::instance()._execute();
+}
+
+void DeferredQueue::_insert(DeferredWork* i_work)
+{
+ lock.lock();
+
+ // NULL pointer implies empty, so just add work item.
+ if (0 == iv_cpus_and_next)
+ {
+ iv_cpus_and_next = reinterpret_cast<uint64_t>(i_work);
+ }
+ else
+ {
+ // Follow linked list to last work item.
+ DeferredWork* tail = DEFERRED_QUEUE_GET_NEXT_PTR(this);
+ while (NULL != DEFERRED_QUEUE_GET_NEXT_PTR(tail))
+ {
+ tail = DEFERRED_QUEUE_GET_NEXT_PTR(tail);
+ }
+
+ // Add work item to the end of the list.
+ DEFERRED_QUEUE_SET_NEXT_PTR(i_work);
+ }
+
+ lock.unlock();
+}
+
+void DeferredQueue::_execute()
+{
+ uint64_t cpus_and_next = 0;
+
+ // Increment the CPU count for pointer references.
+ do
+ {
+ cpus_and_next = iv_cpus_and_next;
+
+ if (0 == cpus_and_next) // No work to execute.
+ {
+ return;
+ }
+
+ } while(!__sync_bool_compare_and_swap(&iv_cpus_and_next,
+ cpus_and_next,
+ cpus_and_next + (1ull << 32)));
+
+ // Extract the item pointer.
+ DeferredWork* item =
+ reinterpret_cast<DeferredWork*>(cpus_and_next & 0xFFFFFFFF);
+
+ // Execute the extracted item.
+ item->start();
+}
+
+void DeferredQueue::_complete(DeferredWork* i_work)
+{
+ lock.lock();
+
+ // Update list-head to pop item off.
+ uint64_t new_ptr =
+ reinterpret_cast<uint64_t>(DEFERRED_QUEUE_GET_NEXT_PTR(i_work));
+ uint64_t old_ptr = 0;
+
+ do
+ {
+ old_ptr = iv_cpus_and_next;
+ } while(!__sync_bool_compare_and_swap(&iv_cpus_and_next, old_ptr, new_ptr));
+
+ // Get the CPU count from the old object pointer and wait until those
+ // CPUs get into i_work.
+ old_ptr >>= 32;
+ while (DEFERRED_QUEUE_GET_CPU_COUNT(i_work) != old_ptr)
+ {
+ setThreadPriorityLow();
+ }
+ setThreadPriorityHigh();
+
+ lock.unlock();
+}
+
+DeferredWork::DeferredWork() : iv_barrier(), iv_cpus_and_next(0),
+ iv_activeSeqId(0),
+ iv_releasePre(false), iv_releasePost(false)
+{
+ uint32_t cpuCount;
+
+ // Read the current CPU count and sequence number.
+ CpuManager::getCpuCountAndSeqId(cpuCount, iv_activeSeqId);
+ // Initialize the barrier with the number of active CPUs.
+ iv_barrier.init(cpuCount);
+}
+
+DeferredWork::~DeferredWork()
+{
+ // Ensure the work item was removed from the queue chain and no
+ // CPUs are still inside it.
+ kassert(0 == iv_cpus_and_next);
+}
+
+void DeferredWork::start()
+{
+ // Increment object reference count.
+ __sync_add_and_fetch(&iv_cpus_and_next, 1ull << 32);
+
+ // Get our CPU object and determine if we were active when the item
+ // was created. (Our sequence # being less or equal work item sequence #)
+ cpu_t* cpu = CpuManager::getCurrentCPU();
+ bool active = cpu->cpu_start_seqid <= iv_activeSeqId;
+
+ // Synchronize active CPUs.
+ if (active)
+ {
+ _waitForCpus();
+ }
+
+ // Call masterPre step.
+ if (cpu->master)
+ {
+ _masterPre();
+ }
+ else
+ {
+ _waitAtPre();
+ }
+
+ // Call MainWork step.
+ if (active)
+ {
+ activeMainWork();
+ _waitForCpus();
+ }
+ else
+ {
+ nonactiveMainWork();
+ }
+
+ // Call masterPost step.
+ if (cpu->master)
+ {
+ _masterPost();
+ }
+ else
+ {
+ _waitAtPost();
+ }
+
+ // Release reference to this object.
+ _cleanup();
+}
+
+void DeferredWork::_waitForCpus()
+{
+ iv_barrier.wait();
+}
+
+void DeferredWork::_masterPre()
+{
+ masterPreWork();
+
+ // Ensure memory ops are globally visible before releasing all CPUs.
+ lwsync();
+ iv_releasePre = true;
+}
+
+void DeferredWork::_waitAtPre()
+{
+ while(!iv_releasePre)
+ {
+ setThreadPriorityLow();
+ }
+ isync(); // Prevent spec. execution past this point until released.
+ setThreadPriorityHigh();
+}
+
+void DeferredWork::_masterPost()
+{
+ masterPostWork();
+
+ // Remove ourself from the queue chain now.
+ Singleton<DeferredQueue>::instance()._complete(this);
+
+ // Ensure memory ops are globally visible before releasing all CPUs.
+ lwsync();
+ iv_releasePost = true;
+}
+
+void DeferredWork::_waitAtPost()
+{
+ while(!iv_releasePost)
+ {
+ setThreadPriorityLow();
+ }
+ isync(); // Prevent spec. execution past this point until released.
+ setThreadPriorityHigh();
+}
+
+void DeferredWork::_cleanup()
+{
+ // Decrement reference count.
+ uint64_t cpu_count =
+ __sync_sub_and_fetch(&iv_cpus_and_next, 1ull << 32) >> 32;
+
+ // If the last object, delete this work item.
+ if (0 == cpu_count)
+ {
+ delete this;
+ }
+}
diff --git a/src/kernel/exception.C b/src/kernel/exception.C
index 0d63d0a73..071f10b1e 100644
--- a/src/kernel/exception.C
+++ b/src/kernel/exception.C
@@ -38,7 +38,7 @@ namespace ExceptionHandles
bool PrivInstr(task_t*);
}
-const uint64_t EXCEPTION_SRR1_MASK = 0x00000000783F0000;
+const uint64_t EXCEPTION_SRR1_MASK = 0x00000000783F0000;
const uint64_t EXCEPTION_SRR1_PRIVINS = 0x0000000000040000;
extern "C"
@@ -56,12 +56,12 @@ void kernel_execute_prog_ex()
}
if (!handled)
{
- printk("Program exception, killing task %d\n", t->tid);
- TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
+ printk("Program exception, killing task %d\n", t->tid);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
}
-const uint64_t EXCEPTION_DSISR_MASK = 0x0000000048000000;
+const uint64_t EXCEPTION_DSISR_MASK = 0x0000000048000000;
const uint64_t EXCEPTION_DSISR_PTEMISS = 0x0000000040000000;
const uint64_t EXCEPTION_DSISR_PERMERR = 0x0000000008000000;
const uint64_t EXCEPTION_DSISR_STORE = 0x0000000002000000;
@@ -75,7 +75,7 @@ void kernel_execute_data_storage()
bool handled = false;
switch(exception)
{
- case EXCEPTION_DSISR_PTEMISS:
+ case EXCEPTION_DSISR_PTEMISS:
{
uint64_t is_store = getDSISR() & EXCEPTION_DSISR_STORE;
handled = VmmManager::pteMiss(t, getDAR(), 0 != is_store);
@@ -87,16 +87,16 @@ void kernel_execute_data_storage()
uint64_t is_store = getDSISR() & EXCEPTION_DSISR_STORE;
if (is_store)
{
- handled = VmmManager::pteMiss(t, getDAR(), true);
+ handled = VmmManager::pteMiss(t, getDAR(), true);
}
- break;
+ break;
}
}
if (!handled)
{
- printk("Data Storage exception on %d: %lx, %lx @ %p\n",
- t->tid, getDAR(), getDSISR(), t->context.nip);
- TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
+ printk("Data Storage exception on %d: %lx, %lx @ %p\n",
+ t->tid, getDAR(), getDSISR(), t->context.nip);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
}
@@ -129,7 +129,7 @@ void kernel_execute_inst_storage()
{
printk("Inst Storage exception on %d: %lx, %lx\n",
t->tid, getSRR0(), getSRR1());
- TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
}
@@ -169,19 +169,19 @@ namespace ExceptionHandles
{
uint32_t* instruction = reinterpret_cast<uint32_t*>(phys_addr);
- // Check for 'doze' and skip over. This avoids a task-crash
+ // Check for 'nap' and skip over. This avoids a task-crash
// if for some reason we entered back into the task without
// priviledge raised.
- if (*instruction == 0x4c000324)
+ if (*instruction == 0x4c000364)
{
- printk("Error: Doze executed with lowered permissions on %d\n",
+ printk("Error: Nap executed with lowered permissions on %d\n",
t->tid);
t->context.nip = static_cast<void*>(instruction + 1);
return true;
}
}
- return false;
+ return false;
}
}
diff --git a/src/kernel/kernel.C b/src/kernel/kernel.C
index cbac9a20f..e29af240e 100644
--- a/src/kernel/kernel.C
+++ b/src/kernel/kernel.C
@@ -1,25 +1,26 @@
-// IBM_PROLOG_BEGIN_TAG
-// This is an automatically generated prolog.
-//
-// $Source: src/kernel/kernel.C $
-//
-// IBM CONFIDENTIAL
-//
-// COPYRIGHT International Business Machines Corp. 2010 - 2011
-//
-// p1
-//
-// Object Code Only (OCO) source materials
-// Licensed Internal Code Source Materials
-// IBM HostBoot Licensed Internal Code
-//
-// The source code for this program is not published or other-
-// wise divested of its trade secrets, irrespective of what has
-// been deposited with the U.S. Copyright Office.
-//
-// Origin: 30
-//
-// IBM_PROLOG_END
+/* IBM_PROLOG_BEGIN_TAG
+ * This is an automatically generated prolog.
+ *
+ * $Source: src/kernel/kernel.C $
+ *
+ * IBM CONFIDENTIAL
+ *
+ * COPYRIGHT International Business Machines Corp. 2010-2012
+ *
+ * p1
+ *
+ * Object Code Only (OCO) source materials
+ * Licensed Internal Code Source Materials
+ * IBM HostBoot Licensed Internal Code
+ *
+ * The source code for this program is not published or other-
+ * wise divested of its trade secrets, irrespective of what has
+ * been deposited with the U.S. Copyright Office.
+ *
+ * Origin: 30
+ *
+ * IBM_PROLOG_END_TAG
+ */
#include <stdint.h>
#include <kernel/console.H>
#include <kernel/pagemgr.H>
@@ -33,6 +34,7 @@
#include <kernel/vmmmgr.H>
#include <kernel/timemgr.H>
#include <sys/vfs.h>
+#include <kernel/deferred.H>
#include <stdlib.h>
@@ -85,6 +87,7 @@ int smp_slave_main(cpu_t* cpu)
CpuManager::init_slave_smp(cpu);
VmmManager::init_slb();
cpu->scheduler->setNextRunnable();
+ DeferredQueue::execute();
kernel_dispatch_task();
return 0;
}
diff --git a/src/kernel/makefile b/src/kernel/makefile
index 06c7536be..c23cb7fe9 100644
--- a/src/kernel/makefile
+++ b/src/kernel/makefile
@@ -1,11 +1,11 @@
-# IBM_PROLOG_BEGIN_TAG
+# IBM_PROLOG_BEGIN_TAG
# This is an automatically generated prolog.
#
# $Source: src/kernel/makefile $
#
# IBM CONFIDENTIAL
#
-# COPYRIGHT International Business Machines Corp. 2010 - 2011
+# COPYRIGHT International Business Machines Corp. 2010-2012
#
# p1
#
@@ -19,14 +19,14 @@
#
# Origin: 30
#
-# IBM_PROLOG_END
+# IBM_PROLOG_END_TAG
ROOTPATH = ../..
OBJS = start.o kernel.o console.o pagemgr.o heapmgr.o taskmgr.o cpumgr.o
OBJS += syscall.o scheduler.o spinlock.o exception.o vmmmgr.o timemgr.o
OBJS += futexmgr.o ptmgr.o segmentmgr.o devicesegment.o basesegment.o
OBJS += block.o cpuid.o misc.o msghandler.o blockmsghdlr.o stacksegment.o
-OBJS += softpatch_p7.o barrier.o idebug.o intmsghandler.o
+OBJS += softpatch_p7.o barrier.o idebug.o intmsghandler.o deferred.o
include ${ROOTPATH}/config.mk
diff --git a/src/kernel/misc.C b/src/kernel/misc.C
index a29aaea21..d45473259 100644
--- a/src/kernel/misc.C
+++ b/src/kernel/misc.C
@@ -26,6 +26,8 @@
#include <kernel/cpuid.H>
#include <kernel/console.H>
#include <kernel/barrier.H>
+#include <kernel/scheduler.H>
+#include <assert.h>
extern "C" void kernel_shutdown(size_t, uint64_t, uint64_t) NO_RETURN;
@@ -82,15 +84,15 @@ namespace KernelMisc
if (c->master)
{
- printk("No payload... doze'ing all threads.\n");
+ printk("No payload... nap'ing all threads.\n");
}
- // Clear LPCR values that wakes up from doze. LPCR[49, 50, 51]
+ // Clear LPCR values that wakes up from nap. LPCR[49, 50, 51]
setLPCR(getLPCR() & (~0x0000000000007000));
while(1)
{
- doze();
+ nap();
}
}
else
@@ -110,4 +112,78 @@ namespace KernelMisc
g_payload_entry);
}
}
+
+ void WinkleCore::masterPreWork()
+ {
+ printk("Winkle threads - ");
+
+ // 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();
+ }
+
+ extern "C" void kernel_execute_winkle(task_t* t);
+
+ void WinkleCore::activeMainWork()
+ {
+ cpu_t* cpu = CpuManager::getCurrentCPU();
+ printk("%d", static_cast<int>(cpu->cpu & 0x7));
+
+ // 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 = 0xC030; // EE, PR, IR, DR.
+ *(reinterpret_cast<task_t**>(cpu->kernel_stack)) = 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();
+
+ // 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 WinkleCore::masterPostWork()
+ {
+ printk(" - Awake!\n");
+
+ // Restore timebase.
+ setTB(iv_timebase);
+
+ // Restore caller of cpu_master_winkle().
+ TaskManager::setCurrentTask(iv_caller);
+
+ }
+
+ void WinkleCore::nonactiveMainWork()
+ {
+ // Race condition that should not occur...
+ //
+ // Attempted to winkle the master and another thread came online in
+ // the process.
+ kassert(false);
+ }
};
diff --git a/src/kernel/start.S b/src/kernel/start.S
index 7d2d77845..f26592245 100644
--- a/src/kernel/start.S
+++ b/src/kernel/start.S
@@ -145,39 +145,8 @@ finished_relocate:
b kernel_dispatch_task; /* Return to task */
.org _start + 0x100
-intvect_system_reset:
- /* TODO: Add handling for SRESET from winkle for master. */
- ;// Need to identify reason for SRESET and then perform appropriate
- ;// action.
- ;// Current support:
- ;// - Decrementer wake-up from doze.
- ;// - External interrupt (also wake-up from doze).
- ;// - IPI wake-up from winkle of slave core.
-
- ;// Free up two registers temporarily.
- mtsprg0 r1
- mtsprg1 r2
-
- ;// Save CR.
- mfcr r1
-
- ;// Find bit 42:44 of SRR1 (reason for SRESET).
- mfsrr1 r2
- extrdi r2, r2, 3, 42
- ;// Check for decrementer (bits = 011).
- cmpi cr0, r2, 0x3
- beq+ intvect_system_reset_decrementer
- ;// Check for external interrupt (bits = 010).
- cmpi cr0, r2, 0x4
- beq+ intvect_system_reset_external
- ;// Check for HMI (bits = 101).
- cmpi cr0, r2, 0x5
- beq+ _start
- ;// Check for bits 000, which is probably a Simics bug right now.
- cmpi cr0, r2, 0x0
- beq+ _start
-1: ;// Unknown reason.
- b 1b
+intvect_system_reset_stub:
+ b intvect_system_reset
.org _start + 0x180
intvect_inst_start:
@@ -272,7 +241,7 @@ softpatch_stub:
mfsprg1 r1 ;// Restore R1 and use normal interrupt code.
STD_INTERRUPT_NOADDR(softpatch)
-.section .text
+.section .text.kernelasm
;// _main:
;// Set up stack and TOC and call kernel's main.
_main:
@@ -302,8 +271,7 @@ _other_thread_spinlock:
1:
ld r3, 0(r2)
;// Loop until value is 1...
- li r4, 1
- cmp cr0, r3, r4
+ cmpi cr0, r3, 1
beq _other_thread_spinlock_complete
or 1,1,1 ;// Lower thread priority.
b 1b
@@ -321,7 +289,7 @@ _other_thread_spinlock_complete:
ld r3, 0(r2) ;// Load CPU object.
cmpi cr0, r3, 0 ;// Check for NULL CPU object.
beq- cr0, 1f ;// Jump to handling if no CPU object found.
- ld r1, 0(r3) ;// Load initial stack.
+ ld r1, CPU_KERNEL_STACK(r3) ;// Load initial stack.
lis r2, smp_slave_main@h ;// Load TOC base.
ori r2, r2, smp_slave_main@l
@@ -329,11 +297,11 @@ _other_thread_spinlock_complete:
bl smp_slave_main ;// Call smp_slave_main
b _main_loop
1:
- ;// No CPU object available, doze this CPU.
+ ;// No CPU object available, nap this CPU.
;// We should only get to this point on simics. SBE will only wake up
;// a single core / thread at a time and we are responsible for
;// further sequencing.
- doze
+ nap
b 1b
@@ -399,8 +367,8 @@ kernel_save_task:
bne- cr0, 1f ;// Jump to FP-save if != NULL.
2:
- ld r1, 0(r1) ;// Get CPU pointer
- ld r1, 0(r1) ;// Get kernel stack pointer.
+ ld r1, TASK_CPUPTR(r1) ;// Get CPU pointer
+ ld r1, CPU_KERNEL_STACK(r1) ;// Get kernel stack pointer.
mfsprg0 r0 ;// Retrieve return address from SPRG0
mtlr r0 ;// Call
@@ -461,8 +429,8 @@ kernel_dispatch_task:
.global kernel_dispatch_task
mfsprg3 r1 ;// Load task structure to r1.
- ldarx r0, 0, r1 ;// Clear the reservation by loading / storing
- stdcx. r0, 0, r1 ;// the CPU pointer in the task.
+ ldarx r0, TASK_CPUPTR, r1 ;// Clear the reservation by loading / storing
+ stdcx. r0, TASK_CPUPTR, r1 ;// the CPU pointer in the task.
mfmsr r2 ;// Get current MSR
ori r2,r2, 0xC030 ;// Enable MSR[EE,PR,IR,DR].
@@ -574,20 +542,88 @@ kernel_dispatch_task:
b 2b
+intvect_system_reset:
+ ;// Need to identify reason for SRESET and then perform appropriate
+ ;// action.
+ ;// Current support:
+ ;// - Initial sreset.
+ ;// - Decrementer wake-up from nap.
+ ;// - External interrupt from nap or winkle.
+ ;// - IPI wake-up from winkle of slave core.
+
+ ;// Raise priority to high.
+ or 3,3,3
+
+ ;// Free up two registers temporarily.
+ mtsprg0 r1
+ mtsprg1 r2
+
+ ;// Check spinlock for 0, which implies we haven't started yet.
+ lis r2, kernel_other_thread_spinlock@h
+ ori r2, r2, kernel_other_thread_spinlock@l
+ ld r2, 0(r2)
+ cmpi cr0, r2, 0
+ beq- _start
+
+ ;// Get CPU object from thread ID, check for NULL which implies not
+ ;// started yet.
+ mfspr r1, PIR
+ lis r2, _ZN10CpuManager7cv_cpusE@h
+ ori r2, r2, _ZN10CpuManager7cv_cpusE@l
+ ld r2, 0(r2) ;// Dereference cv_cpus to get array.
+ muli r1, r1, 8
+ ldx r2, r1, r2 ;// Load CPU object.
+ cmpi cr0, r2, 0 ;// Check for NULL CPU object.
+ beq- _start
+
+ ;// Check for inactive CPU.
+ ld r1, CPU_STATUS(r2)
+ extrdi. r1, r1, 1, CPU_STATUS_ACTIVE
+ beq- intvect_system_reset_inactive
+
+ ;// Now we were an active processor so this must be a nap-wakeup.
+
+ ;// Find bit 42:44 of SRR1 (reason for SRESET).
+ mfsrr1 r2
+ extrdi r2, r2, 3, 42
+ ;// Check for decrementer (bits = 011).
+ cmpi cr0, r2, 0x3
+ beq+ intvect_system_reset_decrementer
+ ;// Check for external interrupt (bits = 010).
+ cmpi cr0, r2, 0x4
+ beq+ intvect_system_reset_external
+ ;// Check for HMI (bits = 101).
+ cmpi cr0, r2, 0x5
+ beq+ 1f ;// TODO: need to handle HMIs?
+1: ;// Unknown reason.
+ b 1b
+
+ ;// @fn intvect_system_reset_inactive
+ ;// Handle SRESET on an inactive processor.
+ ;// This is due to either instruction start or winkle-wakeup.
+intvect_system_reset_inactive:
+ ;// Check winkle state in CPU.
+ ld r1, CPU_STATUS(r2)
+ extrdi. r1, r1, 1, CPU_STATUS_WINKLED
+ beq+ _start
+
+ ;// Now we are a winkled processor that is awoken.
+ ld r1, CPU_KERNEL_STACK(r2)
+ ld r1, 0(r1)
+ mtsprg3 r1
+ b kernel_dispatch_task
+
;// @fn intvect_system_reset_decrementer
;// Handle SRESET due to decrementer wake-up.
- ;// This is a wake-up from 'doze'. Clear priviledge escalation and
+ ;// This is a wake-up from 'nap'. Clear priviledge escalation and
;// perform decrementer.
intvect_system_reset_decrementer:
- ;// Restore CR.
- mtcr r1
-
- ;// Clear MSR mask, since privilaged instruction was now executed (doze).
+ ;// Clear MSR mask, since privilaged instruction was now executed (nap).
mfsprg3 r1 ;// Load task structure to r1.
li r2, 0 ;// Zero r2.
std r2, TASK_MSR_MASK(r1) ;// Zero msr_mask.
- ;// Advance saved NIA (past doze).
+ ;// Advance saved NIA (past nap).
mfsrr0 r1
addi r1, r1, 4
mtsrr0 r1
@@ -600,13 +636,10 @@ intvect_system_reset_decrementer:
;// @fn intvect_system_reset_external
;// Handle SRESET due to wake-up from external interrupt.
- ;// This is a wake-up from 'doze', but not due to the decrementer
- ;// itself firing. Therefore, leave 'doze' process state alone
+ ;// This is a wake-up from 'nap', but not due to the decrementer
+ ;// itself firing. Therefore, leave 'nap' process state alone
;// including NIA and handle the external interrupt.
intvect_system_reset_external:
- ;// Restore CR.
- mtcr r1
-
;// Restore save registers.
mfsprg0 r1
mfsprg1 r2
@@ -638,9 +671,9 @@ system_call_fast_path:
bne cr0, 4f
;// Check for being on master processor.
mfsprg3 r6 ;// Get task structure.
- ld r6, 0(r6) ;// Get CPU structure.
- lbz r6, 12(r6) ;// Read master boolean.
- cmpi cr0, r6, 0x0
+ ld r6, TASK_CPUPTR(r6) ;// Get CPU structure.
+ ld r6, CPU_STATUS(r6) ;// Read master boolean.
+ extrdi. r6, r6, 1, CPU_STATUS_MASTER
beq cr0, 300f ;// Call TASK_MIGRATE_TO_MASTER if not on master.
;// Read scratch.
mtspr 276, r4
@@ -665,9 +698,9 @@ system_call_fast_path:
bne cr0, 5f
;// Check for master processor.
mfsprg3 r6 ;// Get task structure.
- ld r6, 0(r6) ;// Get CPU structure.
- lbz r6, 12(r6) ;// Read master boolean.
- cmpi cr0, r6, 0x0
+ ld r6, TASK_CPUPTR(r6) ;// Get CPU structure.
+ ld r6, CPU_STATUS(r6) ;// Read master boolean.
+ extrdi. r6, r6, 1, CPU_STATUS_MASTER
beq cr0, 300b ;// Call TASK_MIGRATE_TO_MASTER if not on master.
;// Write scratch.
mtspr 276, r4
@@ -785,6 +818,34 @@ kernel_shutdown_ea0_1_mode:
STD_INTERRUPT_NOADDR(hype_emu_assist)
+ ;// @fn kernel_execute_winkle
+ ;//
+ ;// Saves kernel state into a specified task structure and then executes
+ ;// the winkle instruction.
+ ;//
+ ;// @param r3 - task_t* to save kernel state into.
+ ;//
+.global kernel_execute_winkle
+kernel_execute_winkle:
+ ;// Move save area to SPRG3 for kernel_save_task.
+ mtsprg3 r3
+
+ ;// Copy LR to SRR0 (since that is where kernel_save_task gets it from).
+ mflr r3
+ mtsrr0 r3
+
+ ;// Load winkle instruction address into the "return to" address (SPRG0).
+ lis r3, 1f@h
+ ori r3, r3, 1f@l
+ mtsprg0 r3
+
+ ;// Save kernel state.
+ b kernel_save_task
+
+ ;// Execute winkle.
+1:
+ rvwinkle
+
.section .data
.balign 1024
kernel_stack:
diff --git a/src/kernel/syscall.C b/src/kernel/syscall.C
index 6dcbe0c25..645691d70 100644
--- a/src/kernel/syscall.C
+++ b/src/kernel/syscall.C
@@ -52,19 +52,7 @@ void kernel_execute_decrementer()
task_t* current_task = TaskManager::getCurrentTask();
- CpuManager::executePeriodics(c);//TODO is there still a potential deadlock?
-
- if (CpuManager::isShutdownRequested())
- {
- // The code below could cause a hang during shutdown
- // The stats can be retrieved from global variables as needed.
- // This can be uncommented for debug if desired
-#ifdef __MEMSTATS__
- if(c->master)
- HeapManager::stats();
-#endif
- KernelMisc::shutdown();
- }
+ CpuManager::executePeriodics(c);
if (current_task == TaskManager::getCurrentTask())
{
@@ -98,7 +86,8 @@ namespace Systemcalls
void CpuDDLevel(task_t *t);
void CpuStartCore(task_t *t);
void CpuSprValue(task_t *t);
- void CpuDoze(task_t *t);
+ void CpuNap(task_t *t);
+ void CpuWinkle(task_t *t);
void MmAllocBlock(task_t *t);
void MmRemovePages(task_t *t);
void MmSetPermission(task_t *t);
@@ -134,7 +123,8 @@ namespace Systemcalls
&CpuDDLevel, // MISC_CPUDDLEVEL
&CpuStartCore, // MISC_CPUSTARTCORE
&CpuSprValue, // MISC_CPUSPRVALUE
- &CpuDoze, // MISC_CPUDOZE
+ &CpuNap, // MISC_CPUNAP
+ &CpuWinkle, // MISC_CPUWINKLE
&MmAllocBlock, // MM_ALLOC_BLOCK
&MmRemovePages, // MM_REMOVE_PAGES
@@ -670,33 +660,52 @@ namespace Systemcalls
};
/**
- * Allow a task to request priviledge escalation to execute the 'doze'
+ * Allow a task to request priviledge escalation to execute the 'nap'
* instruction.
*
- * Verifies the instruction to execute is, in fact, doze and then sets
+ * Verifies the instruction to execute is, in fact, nap and then sets
* an MSR mask in the task structure to allow escalation on next
* execution.
*
- * When 'doze' is executed the processor will eventually issue an
+ * When 'nap' is executed the processor will eventually issue an
* SRESET exception with flags in srr1 to indication that the
* decrementer caused the wake-up. The kernel will then need to
- * advance the task to the instruction after the doze and remove
+ * advance the task to the instruction after the nap and remove
* priviledge escalation.
*
*/
- void CpuDoze(task_t *t)
+ void CpuNap(task_t *t)
{
uint32_t* instruction = static_cast<uint32_t*>(t->context.nip);
- if (0x4c000324 == (*instruction)) // Verify 'doze' instruction,
+ if (0x4c000364 == (*instruction)) // Verify 'nap' instruction,
// otherwise just return.
{
- // Disable PR, IR, DR so 'doze' can be executed.
+ // Disable PR, IR, DR so 'nap' can be executed.
// (which means to stay in HV state)
t->context.msr_mask = 0x4030;
}
};
+ /** Winkle all the threads so that the runtime SLW image can be loaded. */
+ void CpuWinkle(task_t *t)
+ {
+ cpu_t* cpu = CpuManager::getCurrentCPU();
+
+ if ((CpuManager::getCpuCount() > CpuManager::getThreadCount()) ||
+ (!cpu->master))
+ {
+ TASK_SETRTN(t, -EDEADLK);
+ }
+ else
+ {
+ TASK_SETRTN(t, 0);
+ DeferredQueue::insert(new KernelMisc::WinkleCore(t));
+ TaskManager::setCurrentTask(cpu->idle_task);
+ DeferredQueue::execute();
+ }
+ }
+
/**
* Allocate a block of virtual memory within the base segment
* @param[in] t: The task used to allocate a block in the base segment
OpenPOWER on IntegriCloud