1 /*
2  * Copyright (c) 2013-2023 Ali Mashtizadeh
3  * All rights reserved.
4  */
5 
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <string.h>
9 
10 #include <errno.h>
11 #include <sys/syscall.h>
12 
13 #include <sys/kassert.h>
14 #include <sys/kconfig.h>
15 #include <sys/kdebug.h>
16 #include <sys/kmem.h>
17 #include <sys/ktime.h>
18 #include <sys/mp.h>
19 #include <sys/spinlock.h>
20 #include <sys/thread.h>
21 
22 #include <machine/trap.h>
23 #include <machine/pmap.h>
24 
25 extern Thread *curProc[MAX_CPUS];
26 
27 // Process List
28 Spinlock procLock;
29 uint64_t nextProcessID;
30 ProcessQueue processList;
31 
32 // Memory Pools
33 Slab processSlab;
34 
35 /*
36  * Process
37  */
38 
39 /**
40  * Process_Create --
41  *
42  * Create a process.
43  *
44  * @param [in] parent Parent process.
45  * @param [in] title Process title.
46  *
47  * @return The newly created process.
48  */
49 Process *
Process_Create(Process * parent,const char * title)50 Process_Create(Process *parent, const char *title)
51 {
52     Process *proc = (Process *)Slab_Alloc(&processSlab);
53 
54     if (!proc)
55 	return NULL;
56 
57     memset(proc, 0, sizeof(*proc));
58 
59     proc->pid = nextProcessID++;
60     proc->threads = 0;
61     proc->refCount = 1;
62     proc->procState = PROC_STATE_NULL;
63     TAILQ_INIT(&proc->threadList);
64 
65     if (title) {
66 	strncpy((char *)&proc->title, title, PROCESS_TITLE_LENGTH);
67     } else {
68 	proc->title[0] = '\0';
69     }
70 
71     proc->space = PMap_NewAS();
72     if (proc->space == NULL) {
73 	Slab_Free(&processSlab, proc);
74 	return NULL;
75     }
76     proc->ustackNext = MEM_USERSPACE_STKBASE;
77 
78     Spinlock_Init(&proc->lock, "Process Lock", SPINLOCK_TYPE_NORMAL);
79 
80     Semaphore_Init(&proc->zombieSemaphore, 0, "Zombie Semaphore");
81     TAILQ_INIT(&proc->zombieQueue);
82 
83     Handle_Init(proc);
84 
85     proc->parent = parent;
86     if (parent) {
87 	Spinlock_Lock(&parent->lock);
88 	TAILQ_INSERT_TAIL(&parent->childrenList, proc, siblingList);
89 	Spinlock_Unlock(&parent->lock);
90     }
91     TAILQ_INIT(&proc->childrenList);
92     TAILQ_INIT(&proc->zombieProc);
93     Mutex_Init(&proc->zombieProcLock, "Zombie Process Lock");
94     CV_Init(&proc->zombieProcCV, "Zombie Process CV");
95     CV_Init(&proc->zombieProcPCV, "Zombie Process PCV");
96 
97     Spinlock_Lock(&procLock);
98     TAILQ_INSERT_TAIL(&processList, proc, processList);
99     Spinlock_Unlock(&procLock);
100 
101     return proc;
102 }
103 
104 /**
105  * Process_Destroy --
106  *
107  * Destroy a process.  This should not be called direct, but rather by
108  * Process_Release.
109  *
110  * @param [in] proc Process to destroy.
111  */
112 static void
Process_Destroy(Process * proc)113 Process_Destroy(Process *proc)
114 {
115     Handle_Destroy(proc);
116 
117     Spinlock_Destroy(&proc->lock);
118     Semaphore_Destroy(&proc->zombieSemaphore);
119     CV_Destroy(&proc->zombieProcPCV);
120     CV_Destroy(&proc->zombieProcCV);
121     Mutex_Destroy(&proc->zombieProcLock);
122     PMap_DestroyAS(proc->space);
123 
124     // XXX: We need to promote zombie processes to our parent
125     // XXX: Release the semaphore as well
126 
127     Spinlock_Lock(&procLock);
128     TAILQ_REMOVE(&processList, proc, processList);
129     Spinlock_Unlock(&procLock);
130 
131     Slab_Free(&processSlab, proc);
132 }
133 
134 /**
135  * Process_Lookup --
136  *
137  * Lookup a process by PID and increment its reference count.
138  *
139  * @param [in] pid Process ID to search for.
140  *
141  * @retval NULL No such process.
142  * @return Process that corresponds to the pid.
143  */
144 Process *
Process_Lookup(uint64_t pid)145 Process_Lookup(uint64_t pid)
146 {
147     Process *p;
148     Process *proc = NULL;
149 
150     Spinlock_Lock(&procLock);
151     TAILQ_FOREACH(p, &processList, processList) {
152 	if (p->pid == pid) {
153 	    Process_Retain(p);
154 	    proc = p;
155 	    break;
156 	}
157     }
158     Spinlock_Unlock(&procLock);
159 
160     return proc;
161 }
162 
163 /**
164  * Process_Retain --
165  *
166  * Increment the reference count for a given process.
167  *
168  * @param proc Process to retain a reference to.
169  */
170 void
Process_Retain(Process * proc)171 Process_Retain(Process *proc)
172 {
173     ASSERT(proc->refCount != 0);
174     __sync_fetch_and_add(&proc->refCount, 1);
175 }
176 
177 /**
178  * Process_Release --
179  *
180  * Decrement the reference count for a given process.
181  *
182  * @param proc Process to release a reference of.
183  */
184 void
Process_Release(Process * proc)185 Process_Release(Process *proc)
186 {
187     ASSERT(proc->refCount != 0);
188     if (__sync_fetch_and_sub(&proc->refCount, 1) == 1) {
189 	Process_Destroy(proc);
190     }
191 }
192 
193 /**
194  * Process_Wait --
195  *
196  * Wait for a process to exit and then cleanup it's references.  If the pid ==
197  * 0, we wait for any process, otherwise we wait for a specific process.
198  *
199  * @param [in] proc Parent process.
200  * @param [in] pid Optionally specify the pid of the process to wait on.
201  *
202  * @retval ENOENT Process ID doesn't exist.
203  * @return Exit status of the process that exited or crashed.
204  */
205 uint64_t
Process_Wait(Process * proc,uint64_t pid)206 Process_Wait(Process *proc, uint64_t pid)
207 {
208     Thread *thr;
209     Process *p = NULL;
210     uint64_t status;
211 
212     // XXXFILLMEIN
213     /*
214      * Dummy waitpid implementation that pretends the
215      * process has already exited. Remove and replace
216      * with the actual implementation from the assignment
217      * description.
218      */
219     /* XXXREMOVE START */
220     ASSERT(pid != 0);
221     return SYSCALL_PACK(0, pid << 16);
222     /* XXXREMOVE END */
223 
224     status = (p->pid << 16) | (p->exitCode & 0xff);
225 
226     // Release threads
227     Spinlock_Lock(&proc->lock);
228     while (!TAILQ_EMPTY(&p->zombieQueue)) {
229 	thr = TAILQ_FIRST(&p->zombieQueue);
230 	TAILQ_REMOVE(&p->zombieQueue, thr, schedQueue);
231 	Spinlock_Unlock(&proc->lock);
232 
233 	ASSERT(thr->proc->pid != 1);
234 	Thread_Release(thr);
235 
236 	Spinlock_Lock(&proc->lock);
237     }
238     Spinlock_Unlock(&proc->lock);
239 
240     // Release process
241     Process_Release(p);
242 
243     return SYSCALL_PACK(0, status);
244 }
245 
246 /*
247  * Debugging
248  */
249 
250 void
Process_Dump(Process * proc)251 Process_Dump(Process *proc)
252 {
253     const char *stateStrings[] = {
254 	"NULL",
255 	"READY",
256 	"ZOMBIE"
257     };
258 
259     kprintf("title      %s\n", proc->title);
260     kprintf("pid        %llu\n", proc->pid);
261     kprintf("state	%s\n", stateStrings[proc->procState]);
262     kprintf("space      %016llx\n", proc->space);
263     kprintf("threads    %llu\n", proc->threads);
264     kprintf("refCount   %d\n", proc->refCount);
265     kprintf("nextFD     %llu\n", proc->nextFD);
266 }
267 
268 static void
Debug_Processes(int argc,const char * argv[])269 Debug_Processes(int argc, const char *argv[])
270 {
271     Process *proc;
272 
273     /*
274      * We don't hold locks in case you the kernel debugger is entered while
275      * holding this lock.
276      */
277     //Spinlock_Lock(&threadLock);
278 
279     TAILQ_FOREACH(proc, &processList, processList)
280     {
281 	kprintf("Process: %d(%016llx)\n", proc->pid, proc);
282 	Process_Dump(proc);
283     }
284 
285     //Spinlock_Unlock(&threadLock);
286 }
287 
288 REGISTER_DBGCMD(processes, "Display list of processes", Debug_Processes);
289 
290 static void
Debug_ProcInfo(int argc,const char * argv[])291 Debug_ProcInfo(int argc, const char *argv[])
292 {
293     Thread *thr = curProc[CPU()];
294 
295     kprintf("Current Process State:\n");
296     Process_Dump(thr->proc);
297 }
298 
299 REGISTER_DBGCMD(procinfo, "Display current process state", Debug_ProcInfo);
300 
301