Notification support in the kernel
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 29 Mar 2010 23:40:11 +0000 (16:40 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:40 +0000 (17:35 -0700)
The kernel can send notifications to a process.  Will need some merging
work with the glibc branch, especially regarding transition stacks and
userspace's job.  mhello has example code for what low-level user code
needs to do to request and receive notifications.

Note that you cannot notify vcore0 at this point, due to some glibc
issues.

Documentation/process-internals.txt
Documentation/processes.txt
kern/include/process.h
kern/include/ros/notification.h
kern/src/process.c
tests/mhello.c

index f293d63..918fdec 100644 (file)
@@ -528,12 +528,17 @@ preempt-critical locks.
 It is possible that notifications will mix with preemptions or come while a
 process is not running.  Ultimately, the process wants to be notified on a
 given vcore.  Whenever we send an active notification, we set a flag in
 It is possible that notifications will mix with preemptions or come while a
 process is not running.  Ultimately, the process wants to be notified on a
 given vcore.  Whenever we send an active notification, we set a flag in
-procdata.  If the vcore is offline or is in a preempt-phase, we don't bother
-sending the IPI/notif message.  The kernel will make sure it runs the
-notification handler (as well as restoring the preempt_tf) the next time that
-vcore is restarted.  Note that userspace can toggle this, so they can handle
-the notifications from a different core if it likes, or they can independently
-send a notification.
+procdata (notif_pending).  If the vcore is offline, we don't bother sending the IPI/notif message.  The kernel will make sure it runs
+the notification handler (as well as restoring the preempt_tf) the next time
+that vcore is restarted.  Note that userspace can toggle this, so they can
+handle the notifications from a different core if it likes, or they can
+independently send a notification.
+
+If a vcore has a preempt_pending, we will still send the active notification
+(IPI).  The core ought to get a notification for the preemption anyway, so we
+need to be able to send one.  Additionally, once the vcore is handling that
+preemption notification, it will have notifs disabled, which will prevent us
+from sending any extra notifications anyways.
  
 4.7: Notifs While a Preempt Message is Served
 ---------------------------
  
 4.7: Notifs While a Preempt Message is Served
 ---------------------------
@@ -548,10 +553,10 @@ k_msgs work), the IPI will fire and push us right back into the kernel to
 execute the preemption, and the notif handler's context will be saved in the
 preempt_tf (ready to go when the vcore gets started again).
 
 execute the preemption, and the notif handler's context will be saved in the
 preempt_tf (ready to go when the vcore gets started again).
 
-We could try to just set the notif_pending flag and ignore the message, but
-that would involve inspecting the queue for the preempt k_msg.  Additionally,
-a preempt k_msg can arrive anyway.  Finally, it's possible to have another
-message in the queue between the notif and the preempt, and it gets ugly
+We could try to just leave the notif_pending flag set and ignore the message,
+but that would involve inspecting the queue for the preempt k_msg.
+Additionally, a preempt k_msg can arrive anyway.  Finally, it's possible to have
+another message in the queue between the notif and the preempt, and it gets ugly
 quickly trying to determine what to do.
 
 4.8: When a Pcore is "Free"
 quickly trying to determine what to do.
 
 4.8: When a Pcore is "Free"
index a0b11fa..fb1ab8c 100644 (file)
@@ -309,6 +309,8 @@ In procdata there is an array of per-vcore data, holding some
 preempt/notification information and space for two trapframes: one for
 notification and one for preemption.
 
 preempt/notification information and space for two trapframes: one for
 notification and one for preemption.
 
+4.2.1: Overall
+-----------------------------
 When a notification arrives to a process under normal circumstances, the
 kernel places the previous running context in the notification trapframe, and
 returns to userspace at the program entry point (the elf entry point) on the
 When a notification arrives to a process under normal circumstances, the
 kernel places the previous running context in the notification trapframe, and
 returns to userspace at the program entry point (the elf entry point) on the
@@ -326,23 +328,21 @@ turning interrupts on in hardware).  When a core starts up, this flag is off,
 meaning that notifications are disabled by default.  It is the process's
 responsibility to turn on notifications for a given vcore.
 
 meaning that notifications are disabled by default.  It is the process's
 responsibility to turn on notifications for a given vcore.
 
+4.2.2: Notif Event Details
+-----------------------------
 When the process runs the handler, it is actually starting up at the same
 When the process runs the handler, it is actually starting up at the same
-location in code as it always does, so the kernel will pass it a parameter in
-a register to let it know that it is in a notification.  Additionally, the
-kernel will mask notifications (much like an x86 interrupt gate).  The process
-must check its per-core event queue to see why it was called, and deal with
-all of the events on the queue.  In the case where the event queue overflows,
-the kernel will up a counter so the process can at least be aware things are
-missed.
-
-For missed events, and for events that do not need messages (they have no
-parameters and multiple notifications are irrelevant), the kernel will toggle
-that event's bit in a bitmask.  For the events that don't want messages, we may
-have a flag that userspace sets, meaning they just want to know it happened.
-This might be too much of a pain, so we'll see.  For notification events that
-overflowed the queue, the parameters will be lost, but hopefully the application
-can sort it out.  Again, we'll see.  A specific notif_event should not appear in
-both the event buffers and in the bitmask.
+location in code as it always does.  To determine if it was a notification or
+not, simply check the queue and bitmask.  This has the added benefit of allowing
+a process to notice notifications that it missed previously, or notifs it wanted
+without active notification (IPI).  If we want to bypass this check by having a
+magic register signal, we can add that later.  Additionally, the kernel will
+mask notifications (much like an x86 interrupt gate).  It will also mask
+notifications when starting a core with a fresh trapframe, since the process
+will be executing on its transition stack.  The process must check its per-core
+event queue to see why it was called, and deal with all of the events on the
+queue.  In the case where the event queue overflows, the kernel will up a
+counter so the process can at least be aware things are missed.  At the very
+least, the process will see the notification marked in a bitmask.
 
 These notification events include things such as: an IO is complete, a
 preemption is pending to this core, the process just returned from a
 
 These notification events include things such as: an IO is complete, a
 preemption is pending to this core, the process just returned from a
@@ -357,6 +357,47 @@ masked, the process will simply be killed.    It is up to the process to make
 sure the appropriate pages are pinned, which it should do before entering _M
 mode.
 
 sure the appropriate pages are pinned, which it should do before entering _M
 mode.
 
+4.2.3: Event Overflow and Non-Messages
+-----------------------------
+For missed/overflowed events, and for events that do not need messages (they
+have no parameters and multiple notifications are irrelevant), the kernel will
+toggle that event's bit in a bitmask.  For the events that don't want messages,
+we may have a flag that userspace sets, meaning they just want to know it
+happened.  This might be too much of a pain, so we'll see.  For notification
+events that overflowed the queue, the parameters will be lost, but hopefully the
+application can sort it out.  Again, we'll see.  A specific notif_event should
+not appear in both the event buffers and in the bitmask.
+
+It does not make sense for all events to have messages.  Others, it does not
+make sense to specify a different core on which to run the handler (e.g. page
+faults).  The notification methods that the process expresses via procdata are
+suggestions to the kernel.  When they don't make sense, they will be ignored.
+Some notifications might be unserviceable without messages.  A process needs to
+have a fallback mechanism.  For example, they can read the vcoremap to see who
+was lost, or they can restart a thread to cause it to page fault again.
+
+Event overflow sucks - it leads to a bunch of complications.  Ultimately, what
+we really want is a limitless amount of notification messages (per core), as
+well as a limitless amount of notification types.  And we want these to be
+relayed to userspace without trapping into the kernel. 
+
+We could do this if we had a way to dynamically manage memory in procdata, with
+a distrusted process on one side of the relationship.  We could imagine growing
+procdata dynamically (we plan to, mostly to grow the preempt_data struct as we
+request more vcores), and then run some sort of heap manager / malloc.  Things
+get very tricky since the kernel should never follow pointers that userspace can
+touch.  Additionally, whatever memory management we use becomes a part of the
+kernel interface.  
+
+Even if we had that, dynamic notification *types* is tricky - they are
+identified by a number, not by a specific (list) element.
+
+For now, this all seems like an unnecessary pain in the ass.  We might adjust it
+in the future if we come up with clean, clever ways to deal with the problem,
+which we aren't even sure is a problem yet.
+
+4.2.4: How to Use and Leave a Transition Stack
+-----------------------------
 We considered having the kernel be aware of a process's transition stacks and
 sizes so that it can detect if a vcore is in a notification handler based on
 the stack pointer in the trapframe when a trap or interrupt fires.  While
 We considered having the kernel be aware of a process's transition stacks and
 sizes so that it can detect if a vcore is in a notification handler based on
 the stack pointer in the trapframe when a trap or interrupt fires.  While
@@ -382,6 +423,19 @@ notification queue/list is empty before moving back to real code.  Then it
 should jump back to a real stack, unmask notifications, and jump to the newly
 scheduled thread.
 
 should jump back to a real stack, unmask notifications, and jump to the newly
 scheduled thread.
 
+This can be really tricky.  When userspace is changing threads, it will need to
+unmask notifs as well as jump to the new thread.  There is a slight race here,
+but it is okay.  The race is that an IPI can arrive after notifs are unmasked,
+but before returning to the real user thread.  Then the code will think the
+notif_tf represents the new user thread, even though it hasn't started (and the
+PC is wrong).  The trick is to make sure that all state required to start the
+new thread, as well as future instructions, are all saved within the "stuff"
+that gets saved in the notif_tf.  When these threading packages change contexts,
+they ought to push the PC on the stack of the new thread, (then enable notifs)
+and then execute a return.  If an IPI arrives before the "function return", then
+when that context gets restarted, it will run the "return" with the appropriate
+value on the stack still.
+
 4.3: Preemption Specifics
 -------------------------------
 When a vcore is preempted, the kernel takes whatever context was running (which
 4.3: Preemption Specifics
 -------------------------------
 When a vcore is preempted, the kernel takes whatever context was running (which
index 3b77871..dacd79d 100644 (file)
@@ -81,6 +81,7 @@ void proc_run(struct proc *SAFE p);
 void proc_restartcore(struct proc *SAFE p, trapframe_t *SAFE tf);
 void proc_destroy(struct proc *SAFE p);
 void proc_yield(struct proc *SAFE p);
 void proc_restartcore(struct proc *SAFE p, trapframe_t *SAFE tf);
 void proc_destroy(struct proc *SAFE p);
 void proc_yield(struct proc *SAFE p);
+
 /* Exposed for sys_getvcoreid(), til it's unnecessary */
 uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid);
 
 /* Exposed for sys_getvcoreid(), til it's unnecessary */
 uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid);
 
@@ -143,10 +144,9 @@ void __startcore(trapframe_t *tf, uint32_t srcid, struct proc *CT(1) a0,
 void __death(trapframe_t *tf, uint32_t srcid, void *SNT a0, void *SNT a1,
              void *SNT a2);
 #else
 void __death(trapframe_t *tf, uint32_t srcid, void *SNT a0, void *SNT a1,
              void *SNT a2);
 #else
-void __startcore(trapframe_t *tf, uint32_t srcid, void * a0, void * a1,
-                 void * a2);
-void __death(trapframe_t *tf, uint32_t srcid, void * a0, void * a1,
-             void * a2);
+void __startcore(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2);
+void __death(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2);
+void __notify(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2);
 #endif
 
 /* Arch Specific */
 #endif
 
 /* Arch Specific */
index 3ec0e3e..3aa2c7e 100644 (file)
@@ -20,8 +20,8 @@ struct notif_method {
 
 /* Notification Flags.  vcore0 stuff might be implemented. */
 #define NOTIF_WANTED                   0x001   /* wanted, process-wide */
 
 /* Notification Flags.  vcore0 stuff might be implemented. */
 #define NOTIF_WANTED                   0x001   /* wanted, process-wide */
-#define NOTIF_NO_IPI                   0x002   /* do not IPI the core */
-#define NOTIF_NO_MSG                   0x004   /* no message, just flip the bit */
+#define NOTIF_IPI                              0x002   /* IPI the core */
+#define NOTIF_MSG                              0x004   /* send a message (notif event) */
 #define NOTIF_VCORE0_IPI               0x008   /* fall back to vcore0 for an IPI */
 #define NOTIF_VCORE0_EVENT             0x010   /* fall back to vcore0 for an event */
 
 #define NOTIF_VCORE0_IPI               0x008   /* fall back to vcore0 for an IPI */
 #define NOTIF_VCORE0_EVENT             0x010   /* fall back to vcore0 for an event */
 
index 3637852..a519689 100644 (file)
@@ -8,6 +8,7 @@
 #pragma nosharc
 #endif
 
 #pragma nosharc
 #endif
 
+#include <ros/bcq.h>
 #include <arch/arch.h>
 #include <arch/bitmask.h>
 #include <process.h>
 #include <arch/arch.h>
 #include <arch/bitmask.h>
 #include <process.h>
@@ -690,6 +691,63 @@ void proc_yield(struct proc *SAFE p)
        abandon_core();
 }
 
        abandon_core();
 }
 
+/* If you expect to notify yourself, cleanup state and process_routine_kmsg() */
+void do_notify(struct proc *p, uint32_t vcoreid, unsigned int notif,
+               struct notif_event *ne)
+{
+       assert(notif < MAX_NR_NOTIF);
+       struct notif_method *nm = &p->procdata->notif_methods[notif];
+       struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
+
+       /* enqueue notif message or toggle bits */
+       if (ne && nm->flags & NOTIF_MSG) {
+               if (bcq_enqueue(&vcpd->notif_evts, ne, NR_PERCORE_EVENTS, 4)) {
+                       atomic_inc((atomic_t)&vcpd->event_overflows); // careful here
+                       SET_BITMASK_BIT_ATOMIC(vcpd->notif_bmask, notif);
+               }
+       } else {
+               SET_BITMASK_BIT_ATOMIC(vcpd->notif_bmask, notif);
+       }
+
+       /* Active notification */
+       /* TODO: Currently, there is a race for notif_pending, and multiple senders
+        * can send an IPI.  Worst thing is that the process gets interrupted
+        * briefly and the kernel immediately returns back once it realizes notifs
+        * are masked.  To fix it, we'll need atomic_swapb() (right answer), or not
+        * use a bool. (wrong answer). */
+       if (nm->flags & NOTIF_IPI && vcpd->notif_enabled && !vcpd->notif_pending) {
+               vcpd->notif_pending = TRUE;
+               spin_lock_irqsave(&p->proc_lock);
+                       if ((p->state & PROC_RUNNING_M) && // TODO: (VC#) (_S state)
+                                     (p->procinfo->vcoremap[vcoreid].valid)) {
+                               printk("[kernel] sending notif to vcore %d\n", vcoreid);
+                               send_kernel_message(p->procinfo->vcoremap[vcoreid].pcoreid,
+                                                   __notify, p, 0, 0, KMSG_ROUTINE);
+                       } else { // TODO: think about this, fallback, etc
+                               warn("Vcore unmapped, not receiving an active notif");
+                       }
+               spin_unlock_irqsave(&p->proc_lock);
+       }
+}
+
+/* Sends notification number notif to proc p.  Meant for generic notifications /
+ * reference implementation.  do_notify does the real work.  This one mostly
+ * just determines where the notif should be sent, other checks, etc.
+ * Specifically, it handles the parameters of notif_methods.  If you happen to
+ * notify yourself, make sure you process routine kmsgs. */
+void proc_notify(struct proc *p, unsigned int notif)
+{
+       assert(notif < MAX_NR_NOTIF); // notifs start at 0
+       struct notif_method *nm = &p->procdata->notif_methods[notif];
+       struct notif_event ne;
+       
+       ne.ne_type = notif;
+
+       if (!(nm->flags & NOTIF_WANTED))
+               return;
+       do_notify(p, nm->vcoreid, notif, &ne);
+}
+
 /* Global version of the helper, for sys_get_vcoreid (might phase that syscall
  * out). */
 uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid)
 /* Global version of the helper, for sys_get_vcoreid (might phase that syscall
  * out). */
 uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid)
@@ -997,6 +1055,7 @@ void __startcore(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
        uint32_t pcoreid = core_id(), vcoreid;
        struct proc *p_to_run = (struct proc *CT(1))a0;
        struct trapframe local_tf, *tf_to_pop;
        uint32_t pcoreid = core_id(), vcoreid;
        struct proc *p_to_run = (struct proc *CT(1))a0;
        struct trapframe local_tf, *tf_to_pop;
+       struct preempt_data *vcpd;
 
        assert(p_to_run);
        vcoreid = get_vcoreid(p_to_run, pcoreid);
 
        assert(p_to_run);
        vcoreid = get_vcoreid(p_to_run, pcoreid);
@@ -1009,6 +1068,9 @@ void __startcore(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
                memset(tf_to_pop, 0, sizeof(*tf_to_pop));
                proc_init_trapframe(tf_to_pop, vcoreid, p_to_run->env_entry,
                                    p_to_run->procdata->stack_pointers[vcoreid]);
                memset(tf_to_pop, 0, sizeof(*tf_to_pop));
                proc_init_trapframe(tf_to_pop, vcoreid, p_to_run->env_entry,
                                    p_to_run->procdata->stack_pointers[vcoreid]);
+               /* Disable/mask active notifications for fresh vcores */
+               vcpd = &p_to_run->procdata->vcore_preempt_data[vcoreid];
+               vcpd->notif_enabled = FALSE;
        } else {
                /* Don't want to accidentally reuse this tf (saves on a for loop in
                 * proc_run, though we check there to be safe for now). */
        } else {
                /* Don't want to accidentally reuse this tf (saves on a for loop in
                 * proc_run, though we check there to be safe for now). */
@@ -1020,6 +1082,42 @@ void __startcore(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
        __proc_startcore(p_to_run, tf_to_pop);
 }
 
        __proc_startcore(p_to_run, tf_to_pop);
 }
 
+/* Bail out if it's the wrong process, or if they no longer want a notif */
+// TODO: think about what TF this is: make sure it's the user one, and not a
+// kernel one (was it interrupted, or proc_kmsgs())
+void __notify(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
+{
+       struct user_trapframe local_tf;
+       struct preempt_data *vcpd;
+       uint32_t vcoreid;
+       struct proc *p = (struct proc*)a0;
+
+       if (p != current)
+               return;
+       // TODO: think about locking here.  document why we don't need this
+       vcoreid = p->procinfo->pcoremap[core_id()].vcoreid;
+       vcpd = &p->procdata->vcore_preempt_data[vcoreid];
+
+       printd("received active notification for proc %d's vcore %d on pcore %d\n",
+              p->procinfo->pid, vcoreid, core_id());
+
+       /* sort signals.  notifs are now masked, like an interrupt gate */
+       if (!vcpd->notif_enabled)
+               return;
+       vcpd->notif_enabled = FALSE;
+       vcpd->notif_pending = FALSE; // no longer pending - it made it here
+       
+       /* save the old tf in the notify slot, build and pop a new one.  Note that
+        * silly state isn't our business for a notification. */        
+       // TODO: this is assuming the struct user_tf is the same as a regular TF
+       vcpd->notif_tf = *tf;
+       memset(&local_tf, 0, sizeof(local_tf));
+       proc_init_trapframe(&local_tf, vcoreid, p->env_entry,
+                           p->procdata->stack_pointers[vcoreid]);
+       __proc_startcore(p, &local_tf);
+
+}
+
 /* Stop running whatever context is on this core, load a known-good cr3, and
  * 'idle'.  Note this leaves no trace of what was running. This "leaves the
  * process's context. */
 /* Stop running whatever context is on this core, load a known-good cr3, and
  * 'idle'.  Note this leaves no trace of what was running. This "leaves the
  * process's context. */
index e2b5e3f..7f2a476 100644 (file)
@@ -1,6 +1,10 @@
 #include <parlib.h>
 #include <ros/mman.h>
 #include <ros/resource.h>
 #include <parlib.h>
 #include <ros/mman.h>
 #include <ros/resource.h>
+#include <ros/procdata.h>
+#include <ros/notification.h>
+#include <ros/bcq.h>
+#include <arch/arch.h>
 #include <stdio.h>
 #include <hart.h>
 
 #include <stdio.h>
 #include <hart.h>
 
@@ -15,6 +19,23 @@ int main(int argc, char** argv)
 
        hart_barrier_init(&b,hart_max_harts()-1);
 
 
        hart_barrier_init(&b,hart_max_harts()-1);
 
+/* begin: stuff userspace needs to do before switching to multi-mode */
+
+       /* tell the kernel where and how we want to receive notifications */
+       struct notif_method *nm;
+       for (int i = 1; i < MAX_NR_NOTIF; i++) {
+               nm = &__procdata.notif_methods[i];
+               nm->flags |= NOTIF_WANTED | NOTIF_MSG | NOTIF_IPI;
+               nm->vcoreid = i % 2; // vcore0 or 1, keepin' it fresh.
+       }
+
+       /* don't forget to enable notifs on vcore0 at some point */
+       struct preempt_data *vcpd;
+       vcpd = &__procdata.vcore_preempt_data[0];
+       vcpd->notif_enabled = TRUE;
+       
+/* end: stuff userspace needs to do before switching to multi-mode */
+
        if ((vcoreid = hart_self())) {
                printf("Should never see me! (from vcore %d)\n", vcoreid);
        } else { // core 0
        if ((vcoreid = hart_self())) {
                printf("Should never see me! (from vcore %d)\n", vcoreid);
        } else { // core 0
@@ -23,7 +44,8 @@ int main(int argc, char** argv)
                       vcoreid, &temp, temp);
                printf("Multi-Goodbye, world, from PID: %d!\n", sys_getpid());
                //retval = sys_resource_req(RES_CORES, 2, 0);
                       vcoreid, &temp, temp);
                printf("Multi-Goodbye, world, from PID: %d!\n", sys_getpid());
                //retval = sys_resource_req(RES_CORES, 2, 0);
-               retval = hart_request(hart_max_harts()-2);
+               //retval = hart_request(hart_max_harts()-2);
+               retval = hart_request(2); // doesn't do what you think.  this gives 3.
                //debug("retval = %d\n", retval);
        }
        printf("Vcore %d Done!\n", vcoreid);
                //debug("retval = %d\n", retval);
        }
        printf("Vcore %d Done!\n", vcoreid);
@@ -31,12 +53,37 @@ int main(int argc, char** argv)
        hart_barrier_wait(&b,hart_self());
 
        printf("All Cores Done!\n", vcoreid);
        hart_barrier_wait(&b,hart_self());
 
        printf("All Cores Done!\n", vcoreid);
+       while(1); // manually kill from the monitor
        return 0;
 }
 
 void hart_entry(void)
 {
        uint32_t vcoreid = hart_self();
        return 0;
 }
 
 void hart_entry(void)
 {
        uint32_t vcoreid = hart_self();
+
+/* begin: stuff userspace needs to do to handle notifications */
+
+       struct preempt_data *vcpd;
+       vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       
+       /* here is how you receive a notif_event */
+       struct notif_event ne = {0};
+       bcq_dequeue(&vcpd->notif_evts, &ne, NR_PERCORE_EVENTS);
+       printf("the queue is on vcore %d and has a ne with type %d\n", vcoreid,
+              ne.ne_type);
+       /* it might be in bitmask form too: */
+       //printf("and the bitmask looks like: ");
+       //PRINT_BITMASK(__procdata.vcore_preempt_data[vcoreid].notif_bmask, MAX_NR_NOTIF);
+       /* can see how many messages had to be sent as bits */
+       printf("Number of event overflows: %d\n", vcpd->event_overflows);
+
+       /* unmask notifications once you can let go of the notif_tf and it is okay
+        * to clobber the transition stack.
+        * Check Documentation/processes.txt: 4.2.4 */
+       vcpd->notif_enabled = TRUE;
+       
+/* end: stuff userspace needs to do to handle notifications */
+
        temp = 0xcafebabe;
        printf("Hello from hart_entry in vcore %d with temp addr %p and temp %p\n",
               vcoreid, &temp, temp);
        temp = 0xcafebabe;
        printf("Hello from hart_entry in vcore %d with temp addr %p and temp %p\n",
               vcoreid, &temp, temp);