Split env_run into proc_startcore, early work
[akaros.git] / kern / src / process.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  */
6
7 #include <process.h>
8 #include <atomic.h>
9 #include <assert.h>
10
11 /*
12  * While this could be done with just an assignment, this gives us the
13  * opportunity to check for bad transitions.  Might compile these out later, so
14  * we shouldn't rely on them for sanity checking from userspace.
15  */
16 int proc_set_state(struct proc *p, uint32_t state)
17 {
18         uint32_t curstate = p->state;
19         /* Valid transitions:
20          * C   -> RBS
21          * RBS -> RGS
22          * RGS -> RBS
23          * RGS -> W
24          * W   -> RBS
25          * RGS -> RBM
26          * RBM -> RGM
27          * RGM -> RBM
28          * RGM -> RBS
29          * RGS -> D
30          * RGM -> D
31          * 
32          * These ought to be implemented later (allowed, not thought through yet).
33          * RBS -> D
34          * RBM -> D
35          *
36          * This isn't allowed yet, may be later.
37          * C   -> D
38          */
39         #if 1 // some sort of correctness flag
40         switch (curstate) {
41                 case PROC_CREATED:
42                         if (state != PROC_RUNNABLE_S)
43                                 panic("Invalid State Transition! PROC_CREATED to %d", state);
44                         break;
45                 case PROC_RUNNABLE_S:
46                         if (!(state & (PROC_RUNNING_S | PROC_DYING)))
47                                 panic("Invalid State Transition! PROC_RUNNABLE_S to %d", state);
48                         break;
49                 case PROC_RUNNING_S:
50                         if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M | PROC_WAITING |
51                                        PROC_DYING)))
52                                 panic("Invalid State Transition! PROC_RUNNING_S to %d", state);
53                         break;
54                 case PROC_WAITING:
55                         if (state != PROC_RUNNABLE_S)
56                                 panic("Invalid State Transition! PROC_WAITING to %d", state);
57                         break;
58                 case PROC_DYING:
59                         if (state != PROC_CREATED) // when it is reused (TODO)
60                                 panic("Invalid State Transition! PROC_DYING to %d", state);
61                         break;
62                 case PROC_RUNNABLE_M:
63                         if (!(state & (PROC_RUNNING_M | PROC_DYING)))
64                                 panic("Invalid State Transition! PROC_RUNNABLE_M to %d", state);
65                         break;
66                 case PROC_RUNNING_M:
67                         if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M | PROC_DYING)))
68                                 panic("Invalid State Transition! PROC_RUNNING_M to %d", state);
69                         break;
70         }
71         #endif
72         p->state = state;
73         return 0;
74 }
75
76 /* Change this when we aren't using an array */
77 struct proc *get_proc(unsigned pid)
78 {
79         // should have some error checking when we do this for real
80         return &envs[ENVX(pid)];
81 }
82
83 /* Whether or not actor can control target */
84 bool proc_controls(struct proc *actor, struct proc *target)
85 {
86         return target->env_parent_id == actor->env_id;
87 }
88
89 /*
90  * Runs the given context (trapframe) of process p on the core this code
91  * executes on.  The refcnt tracks how many cores have "an interest" in this
92  * process, which so far just means it uses the process's page table.  See the
93  * massive comments around the incref function
94  *
95  * TODO: think about how an interrupt could abort this, esp when we want to
96  * destroy it.  need a way to not lose the kernel stack.  For example, we could
97  * receive an IPI that tells us to preempt this process (or even kill it) and
98  * run something different.
99  * TODO: in general, think about when we no longer need the stack, in case we
100  * are preempted and expected to run again from somewhere else.  we can't
101  * expect to have the kernel stack around anymore.
102  *
103  * I think we need to make it such that the kernel in "process context"
104  * never gets removed from the core (displaced from its stack)
105  * would like to leave interrupts on too, so long as we come back.
106  * Consider a moveable flag or something.
107  *
108  * Perhaps we could have a workqueue with the todo item put there by the
109  * interrupt handler when it realizes we were in the kernel in the first place.
110  * disable ints before checking the queue and deciding to pop out or whatever to
111  * ensure atomicity.
112  */
113 void proc_startcore(struct proc *p, trapframe_t *tf) {
114         /*
115          * TODO: okay, we have this.  now handle scenarios based on these
116          * assumptions (transitions from these states) like:
117          *              death attempt
118          *              preempt attempt
119          */
120         assert(p->state & (PROC_RUNNING_S | PROC_RUNNING_M));
121         /* If the process wasn't here, then we need to load its address space. */
122         if (p != current) {
123                 if (env_incref(p))
124                         // getting here would mean someone tried killing this while we tried
125                         // to start one of it's contexts (from scratch, o/w we had it's CR3
126                         // loaded already)
127                         panic("Proc is dying, handle me!"); lcr3(p->env_cr3);
128                 // we unloaded the old cr3, so decref it (if it exists)
129                 // TODO: Consider moving this to wherever we really "mean to leave the
130                 // process's context".
131                 if (current)
132                         env_decref(current);
133                 current = p;
134                 /* also need to load our silly state, though this implies it's the same
135                  * context, and not just the same process
136                  * TODO: this is probably a lie, think about page faults
137                  */
138                 // load_our_silly_state();
139         }
140         /* If the process entered the kernel via sysenter, we need to leave via
141          * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
142          * sysenter_handler.
143          */
144         if (tf->tf_cs)
145                 env_pop_tf(tf);
146         else
147                 env_pop_tf_sysexit(tf);
148 }