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