1 using System;
2 using System.Collections;
3 using cmsx.machine;
4
5 namespace cmsx.kernel
6 {
7 public const int numProcessSlots = 1024;
8 public const int noPID = -1;
9 public const int idlePID = 0;
10 public const ulong noEvent = 0u;
11 public const ulong childExitEvent = 1u;
12 public const ulong consoleInputEvent = 2u;
13 public const ulong wakeupEvent = 3u;
14 public const ulong blockUnlockedEvent = 4u;
15 public const ulong anyBlockFreeEvent = 5u;
16 public const ulong blockIOEvent = 6u;
17 public const ulong inodeUnlockedEvent = 7u;
18 public const ulong pipeNotFullEvent = 8u;
19 public const ulong pipeNotEmptyEvent = 9u;
20
21 public nothrow string EventStr(ulong event, ulong eventData1, ulong eventData2)
22 {
23 string eventStr;
24 switch (event)
25 {
26 case childExitEvent: eventStr.Append("child.exit.").Append(ToHexString(eventData1)); break;
27 case consoleInputEvent: eventStr.Append("console.input.").Append(ToHexString(eventData1)).Append('.').Append(ToString(eventData2)); break;
28 case wakeupEvent: eventStr.Append("proc.wakeup.").Append(ToString(eventData1)); break;
29 case blockUnlockedEvent: eventStr.Append("block.unlocked.").Append(cast<Block*>(cast<void*>(eventData1))->ToString()); break;
30 case anyBlockFreeEvent: eventStr.Append("anyblock.free"); break;
31 case blockIOEvent: eventStr.Append("blockio.ready.").Append(cast<Block*>(cast<void*>(eventData1))->ToString()); break;
32 case inodeUnlockedEvent: eventStr.Append("inode.unlocked.").Append(cast<INode*>(cast<void*>(eventData1))->ToString()); break;
33 case pipeNotFullEvent: eventStr.Append("pipe.not.full"); break;
34 case pipeNotEmptyEvent: eventStr.Append("pipe.not.empty"); break;
35 }
36 return eventStr;
37 }
38
39 public nothrow string ProcessName(Process* process)
40 {
41 string s;
42 if (process != null)
43 {
44 s.Append(process->name).Append(".pid=").Append(ToString(process->pid));
45 }
46 else
47 {
48 s.Append("kernel");
49 }
50 return s;
51 }
52
53 public class Process
54 {
55 public enum State : byte
56 {
57 free = 0u, created = 1u, readyToRun = 2u, running = 3u, asleep = 5u, zombie = 6u
58 }
59 public enum Mode : byte
60 {
61 runningUser, runningKernel
62 }
63 public nothrow Process() :
64 pid(noPID), pgid(noPID), state(State.free), mode(Mode.runningUser), parent(null), firstChild(null), lastChild(null), nextSibling(null), nextFree(null), memoryTable(),
65 sp(0u), regAXAddress(0u), regBXAddress(0u), regCXAddress(0u), exitCode(0u), event(noEvent), eventData1(0u), eventData2(0u), systemCallReady(false), umask(0), unsave(false)
66 {
67 }
68 public nothrow void AddChild(Process* child)
69 {
70 #assert(child != null);
71 #assert(child->nextSibling == null);
72 if (lastChild != null)
73 {
74 #assert(lastChild->nextSibling == null);
75 lastChild->nextSibling = child;
76 }
77 lastChild = child;
78 if (firstChild == null)
79 {
80 firstChild = child;
81 }
82 child->parent = this;
83 }
84 public nothrow void RemoveChild(Process* prevChild, Process* child)
85 {
86 #assert(child != null);
87 #assert(prevChild != child);
88 if (prevChild != null)
89 {
90 prevChild->nextSibling = child->nextSibling;
91 }
92 if (lastChild == child)
93 {
94 lastChild = prevChild;
95 }
96 if (firstChild == child)
97 {
98 firstChild = child->nextSibling;
99 }
100 child->nextSibling = null;
101 child->parent = null;
102 }
103 public int pid;
104 public int pgid;
105 public int sid;
106 public int uid;
107 public int gid;
108 public string name;
109 public ulong entryPoint;
110 public State state;
111 public Mode mode;
112 public Process* parent;
113 public Process* firstChild;
114 public Process* lastChild;
115 public Process* nextSibling;
116 public Process* nextFree;
117 public MemoryTable memoryTable;
118 public ProcessFileTable fileTable;
119 public INodeKey rootDirINodeKey;
120 public INodeKey workingDirINodeKey;
121 public INodeKey executableINodeKey;
122 public int umask;
123 public ulong sp;
124 public ulong regAXAddress;
125 public ulong regBXAddress;
126 public ulong regCXAddress;
127 public ushort exitCode;
128 public ulong event;
129 public ulong eventData1;
130 public ulong eventData2;
131 public ulong poolStart;
132 public ulong poolEnd;
133 public ulong functionTableAddress;
134 public ulong functionTableLength;
135 public ulong currentExceptionAddress;
136 public ulong currentExceptionClassId;
137 public ulong handlerAddress;
138 public ulong currentExceptionFrameSize;
139 public SystemCall systemCall;
140 public SystemError lastError;
141 public bool systemCallReady;
142 public void* fiber;
143 public bool unsave;
144 public Duration kernelTime;
145 public Duration userTime;
146 public Duration childKernelTime;
147 public Duration childUserTime;
148 public TimePoint kernelStart;
149 public TimePoint userStart;
150 }
151
152 public class ProcessTable
153 {
154 static ProcessTable() : instance(new ProcessTable())
155 {
156 }
157 public static nothrow ProcessTable& Instance()
158 {
159 return *instance;
160 }
161 private ProcessTable() : machine(GetMachine()), nextPID(1), free(null), init(null), running(null), readyQueue(), nextFreeProcessSlot(0)
162 {
163 }
164 public Process* CreateProcess(const string& name, ulong entryPoint, ulong textSegmentBaseAddress, ulong textSegmentSize, ulong dataSegmentSize, ulong poolSegmentSize,
165 ulong minStackSegmentSize, ulong maxStackSegmentSize, ulong stackSegmentIncrement, int sid)
166 {
167 Process* process = null;
168 if (free != null)
169 {
170 process = free;
171 free = free->nextFree;
172 }
173 else if (nextFreeProcessSlot < numProcessSlots)
174 {
175 process = &processSlots[nextFreeProcessSlot++];
176 }
177 else
178 {
179 throw SystemError(ERLIMITEXCEEDED, "maximum number of simultaneous processes (" + ToString(numProcessSlots) + " exceeeded");
180 }
181 if (nextPID == idlePID)
182 {
183 ++nextPID;
184 }
185 process->pid = nextPID++;
186 process->pgid = process->pid;
187 process->sid = sid;
188 process->uid = 0;
189 process->gid = 0;
190 process->name = name;
191 process->entryPoint = entryPoint;
192 process->state = Process.State.created;
193 process->parent = null;
194 process->firstChild = null;
195 process->lastChild = null;
196 process->nextSibling = null;
197 process->nextFree = null;
198 InitializeProcessMemory(machine, process, textSegmentBaseAddress, textSegmentSize, dataSegmentSize, poolSegmentSize, minStackSegmentSize, maxStackSegmentSize, stackSegmentIncrement);
199 InitializeFileTable(machine, process->fileTable);
200 FileSystem* fs = GetMountTable().GetFileSystem(0);
201 process->rootDirINodeKey = fs->GetRootDirINodeKey();
202 process->workingDirINodeKey = process->rootDirINodeKey;
203 process->umask = 0;
204 AddProcessToProcessMap(process);
205 process->kernelTime = Duration(0);
206 process->userTime = Duration(0);
207 process->childKernelTime = Duration(0);
208 process->childUserTime = Duration(0);
209 process->kernelStart = TimePoint(0);
210 process->userStart = TimePoint(0);
211 return process;
212 }
213 public nothrow Process* CloneProcess(Process* parent)
214 {
215 Process* child = null;
216 if (free != null)
217 {
218 child = free;
219 free = free->nextFree;
220 }
221 else if (nextFreeProcessSlot < numProcessSlots)
222 {
223 child = &processSlots[nextFreeProcessSlot++];
224 }
225 else
226 {
227 return null;
228 }
229 if (nextPID == idlePID)
230 {
231 ++nextPID;
232 }
233 child->pid = nextPID++;
234 child->pgid = parent->pgid;
235 child->sid = parent->sid;
236 child->uid = parent->uid;
237 child->gid = parent->gid;
238 child->name = parent->name + ".child";
239 child->state = Process.State.created;
240 child->mode = Process.Mode.runningUser;
241 child->parent = null;
242 child->firstChild = null;
243 child->lastChild = null;
244 child->nextSibling = null;
245 child->nextFree = null;
246 CloneProcessMemory(machine, child, parent);
247 parent->fileTable.ShareFilesTo(child->fileTable);
248 child->rootDirINodeKey = parent->rootDirINodeKey;
249 child->workingDirINodeKey = parent->workingDirINodeKey;
250 child->executableINodeKey = parent->executableINodeKey;
251 child->umask = parent->umask;
252 child->poolStart = parent->poolStart;
253 child->poolEnd = parent->poolEnd;
254 child->functionTableAddress = parent->functionTableAddress;
255 child->functionTableLength = parent->functionTableLength;
256 AddProcessToProcessMap(child);
257 parent->AddChild(child);
258 child->kernelTime = Duration(0);
259 child->userTime = Duration(0);
260 child->childKernelTime = Duration(0);
261 child->childUserTime = Duration(0);
262 child->kernelStart = TimePoint(0);
263 child->userStart = TimePoint(0);
264 return child;
265 }
266 public nothrow void FreeProcess(Process* process)
267 {
268 RemoveProcessFromExecutableINodeProcessMap(process);
269 processMap.Remove(process->pid);
270 *process = Process();
271 process->nextFree = free;
272 free = process;
273 }
274 public Process* GetProcess(int pid) const
275 {
276 HashMap<int, Process*>.ConstIterator it = processMap.CFind(pid);
277 if (it != processMap.CEnd())
278 {
279 return it->second;
280 }
281 else
282 {
283 return null;
284 }
285 }
286 public inline nothrow Process* GetInit() const
287 {
288 return init;
289 }
290 public inline nothrow void SetInit(Process* init_)
291 {
292 init = init_;
293 }
294 public inline nothrow Process* GetRunning() const
295 {
296 return running;
297 }
298 public inline nothrow void SetRunning(Process* running_)
299 {
300 running = running_;
301 }
302 public inline nothrow Process* GetIdle() const
303 {
304 return &idleProcess;
305 }
306 public nothrow void AddProcessToProcessMap(Process* process)
307 {
308 processMap[process->pid] = process;
309 }
310 public nothrow void AddProcessToExecutableINodeProcessMap(Process* process)
311 {
312 executableINodeProcessMap[process->executableINodeKey] = process;
313 }
314 public nothrow Process* GetProcessFromExecutableINodeProcessMap(const INodeKey& executableINodeKey) const
315 {
316 HashMap<INodeKey, Process*, INodeKeyHash>.ConstIterator it = executableINodeProcessMap.CFind(executableINodeKey);
317 if (it != executableINodeProcessMap.CEnd())
318 {
319 return it->second;
320 }
321 else
322 {
323 return null;
324 }
325 }
326 public nothrow void RemoveProcessFromExecutableINodeProcessMap(Process* process)
327 {
328 executableINodeProcessMap.Remove(process->executableINodeKey);
329 }
330 public inline nothrow void PutToReadyQueue(Process* process)
331 {
332 readyQueue.Put(process);
333 }
334 public inline nothrow bool ReadyQueueEmpty() const
335 {
336 return readyQueue.IsEmpty();
337 }
338 public inline nothrow Process* GetNextReadyProcess()
339 {
340 return readyQueue.Get();
341 }
342 private static UniquePtr<ProcessTable> instance;
343 private Machine& machine;
344 private Process[numProcessSlots] processSlots;
345 private Process idleProcess;
346 private int nextPID;
347 private Process* free;
348 private int nextFreeProcessSlot;
349 private HashMap<int, Process*> processMap;
350 private Process* init;
351 private Process* running;
352 private Queue<Process*> readyQueue;
353 private HashMap<INodeKey, Process*, INodeKeyHash> executableINodeProcessMap;
354 }
355
356 public nothrow ProcessTable& GetProcessTable()
357 {
358 return ProcessTable.Instance();
359 }
360
361 public nothrow Process* CreateProcess(ProcessTable& processTable, const string& name, ulong entryPoint,
362 ulong textSegmentBaseAddress, ulong textSegmentSize, ulong dataSegmentSize, ulong poolSegmentSize, ulong minStackSegmentSize, ulong maxStackSegmentSize, ulong stackSegmentIncrement, int sid)
363 {
364 return processTable.CreateProcess(name, entryPoint, textSegmentBaseAddress, textSegmentSize, dataSegmentSize, poolSegmentSize, minStackSegmentSize, maxStackSegmentSize, stackSegmentIncrement, sid);
365 }
366
367 public nothrow Process* CloneProcess(ProcessTable& processTable, Process* parent)
368 {
369 return processTable.CloneProcess(parent);
370 }
371
372 public nothrow void FreeProcess(ProcessTable& processTable, Process* process)
373 {
374 processTable.FreeProcess(process);
375 }
376
377 public nothrow Process* GetProcess(ProcessTable& processTable, int pid)
378 {
379 return processTable.GetProcess(pid);
380 }
381
382 public nothrow Process* GetRunningProcess(ProcessTable& processTable)
383 {
384 return processTable.GetRunning();
385 }
386
387 public nothrow void SetProcessReadyToRun(ProcessTable& processTable, Process* process)
388 {
389 if (Log())
390 {
391 LogMessage("proc.setReady", ProcessName(process));
392 }
393 process->state = Process.State.readyToRun;
394 processTable.PutToReadyQueue(process);
395 }
396
397 public inline nothrow ushort MakeProcessExitCode(byte signalNumber, byte userExitCode)
398 {
399 return (cast<ushort>(signalNumber) << 8u) | cast<ushort>(userExitCode);
400 }
401
402 public inline nothrow void UnpackProcessExitCode(ushort exitCode, byte& signalNumber, byte& userExitCode)
403 {
404 signalNumber = cast<byte>(exitCode >> 8u);
405 userExitCode = cast<byte>(exitCode);
406 }
407
408 public nothrow void SleepProcess(Process* process, ulong event, ulong eventData1, ulong eventData2)
409 {
410 if (Log())
411 {
412 LogMessage("proc.sleep", ProcessName(process) + ":" + EventStr(event, eventData1, eventData2));
413 }
414 if (process != null)
415 {
416 #assert(process->state == Process.State.running);
417 process->state = Process.State.asleep;
418 process->event = event;
419 process->eventData1 = eventData1;
420 process->eventData2 = eventData2;
421 if (event != wakeupEvent)
422 {
423 process->kernelTime = process->kernelTime + Now() - process->kernelStart;
424 OsSwitchToFiber(mainFiber);
425 process->kernelStart = Now();
426 }
427 }
428 else
429 {
430 Kernel& kernel = GetKernel();
431 kernel.SetWaiting();
432 kernel.WaitKernelEvent();
433 kernel.ResetWaiting();
434 }
435 }
436
437 public nothrow void WakeUpProcess(ProcessTable& processTable, Process* process)
438 {
439 if (Log())
440 {
441 LogMessage("proc.wakeup", ProcessName(process));
442 }
443 if (process != null)
444 {
445 #assert(process->state == Process.State.asleep);
446 process->event = 0u;
447 process->eventData1 = 0u;
448 process->eventData2 = 0u;
449 SetProcessReadyToRun(processTable, process);
450 }
451 else
452 {
453 Kernel& kernel = GetKernel();
454 kernel.SetKernelEvent();
455 }
456 }
457
458 public void ExitProcess(Machine& machine, ProcessTable& processTable, Process* process, ushort exitCode)
459 {
460 if (Log())
461 {
462 LogMessage("proc.exit", ProcessName(process) + ".exitCode=" + ToString(exitCode));
463 }
464 #assert(process->pid != idlePID);
465 process->fileTable.ReleaseFiles();
466 FreeProcessMemory(machine.GetMemory(), process);
467 process->exitCode = exitCode;
468 process->state = Process.State.zombie;
469 process->kernelTime = process->kernelTime + Now() - process->kernelStart;
470 Process* parent = process->parent;
471 Process* init = processTable.GetInit();
472 if (process != init)
473 {
474 Process* child = process->firstChild;
475 while (child != null)
476 {
477 Process* next = child->nextSibling;
478 child->nextSibling = null;
479 init->AddChild(child);
480 child = next;
481 }
482 process->firstChild = null;
483 process->lastChild = null;
484 }
485 if (parent != null)
486 {
487 if (parent->state == Process.State.asleep && parent->event == childExitEvent)
488 {
489 WakeUpProcess(processTable, parent);
490 }
491 }
492 Schedule(machine, processTable);
493 }
494
495 public int WaitProcess(Machine& machine, ProcessTable& processTable, Process* process, ulong exitCodeAddress)
496 {
497 if (Log())
498 {
499 LogMessage("proc.wait", ProcessName(process));
500 }
501 while (true)
502 {
503 Process* child = process->firstChild;
504 if (child == null)
505 {
506 process->lastError = SystemError(EFAIL, "process has no children");
507 return -1;
508 }
509 else
510 {
511 Process* prevChild = null;
512 while (child != null)
513 {
514 if (child->state == Process.State.zombie)
515 {
516 if (exitCodeAddress != 0u)
517 {
518 WriteProcessMemory(machine, process, exitCodeAddress, child->exitCode, 2u, Protection.write);
519 }
520 int childPID = child->pid;
521 process->childKernelTime = process->childKernelTime + child->kernelTime;
522 process->childUserTime = process->childUserTime + child->userTime;
523 process->RemoveChild(prevChild, child);
524 FreeProcess(processTable, child);
525 if (Log())
526 {
527 LogMessage("proc.wait", "return." + ProcessName(process) + ".childPID=" + ToString(childPID));
528 }
529 return childPID;
530 }
531 prevChild = child;
532 child = child->nextSibling;
533 }
534 SleepProcess(process, childExitEvent, exitCodeAddress, 0u);
535 }
536 }
537 }
538
539 public int ForkProcess(Machine& machine, ProcessTable& processTable, Process* parent)
540 {
541 Process* child = CloneProcess(processTable, parent);
542 SetProcessReadyToRun(processTable, child);
543 WriteProcessMemory(machine, child, child->regAXAddress, 0u, 8u, Protection.write);
544 return child->pid;
545 }
546
547 public void SendSignalProcess(Machine& machine, ProcessTable& processTable, Process* process, int pid, int sig)
548 {
549 Process* target = processTable.GetProcess(pid);
550 ExitProcess(machine, processTable, target, 0x100u);
551 }
552
553 public void SetUIDProcess(Process* process, int uid)
554 {
555 if (process->uid == 0)
556 {
557 process->uid = uid;
558 }
559 else
560 {
561 throw SystemError(EPERM, "unauthorized");
562 }
563
564 }
565
566 public void SetGIDProcess(Process* process, int gid)
567 {
568 if (process->gid == 0)
569 {
570 process->gid = gid;
571 }
572 else
573 {
574 throw SystemError(EPERM, "unauthorized");
575 }
576
577 }
578
579 public int SetPoolEndProcess(Machine& machine, Process* process, ulong poolEndAddress)
580 {
581 if (poolEndAddress < poolSegmentBaseAddress || poolEndAddress >= stackSegmentBaseAddress)
582 {
583 throw SystemError(EINVAL, "set_pool_end: invalid virtual address " + ToHexString(poolEndAddress));
584 }
585 if (poolEndAddress < process->poolStart)
586 {
587 throw SystemError(EINVAL, "set_pool_end: invalid virtual address " + ToHexString(poolEndAddress));
588 }
589 if (poolEndAddress > process->poolEnd)
590 {
591 poolEndAddress = pageSize * ((poolEndAddress - 1u) / pageSize + 1u);
592 try
593 {
594 GrowSegment(machine.GetMemory(), process->memoryTable, poolSegmentIndex, poolEndAddress - process->poolEnd);
595 process->poolEnd = poolEndAddress;
596 return 0;
597 }
598 catch (const SystemError& error)
599 {
600 throw;
601 }
602 catch (const Exception& ex)
603 {
604 throw SystemError(EINVAL, "set_pool_end: could not grow pool segment: " + ex.Message());
605 }
606 }
607 else if (poolEndAddress < process->poolEnd)
608 {
609 poolEndAddress = pageSize * (poolEndAddress / pageSize);
610 try
611 {
612 ShrinkSegment(machine.GetMemory(), process->memoryTable, poolSegmentIndex, process->poolEnd - poolEndAddress);
613 process->poolEnd = poolEndAddress;
614 return 0;
615 }
616 catch (const SystemError& error)
617 {
618 throw;
619 }
620 catch (const Exception& ex)
621 {
622 throw SystemError(EINVAL, "set_pool_end: could not shrink pool segment: " + ex.Message());
623 }
624 }
625 return 0;
626 }
627
628 public string GetProcessStackTrace(Machine& machine, Process* process, ulong fp)
629 {
630 List<cmsx.object.FunctionTableEntry> functionTable;
631 ReadFunctionTableFromProcessMemory(machine, process, process->functionTableAddress, process->functionTableLength, functionTable);
632 List<ulong> pcs;
633 string stackTrace = "STACK TRACE:\n";
634 ulong pc = 0u;
635 ReadProcessMemory(machine, process, fp - 8u, pc, 8u, false);
636 while (pc != 0u)
637 {
638 pcs.Add(pc);
639 try
640 {
641 ulong prevFP = 0u;
642 ReadProcessMemory(machine, process, fp, prevFP, 8u, false);
643 fp = prevFP;
644 ReadProcessMemory(machine, process, fp - 8u, pc, 8u, false);
645 }
646 catch (const Exception& ex)
647 {
648 pc = 0u;
649 }
650 }
651 long n = pcs.Count();
652 for (long i = n - 1; i >= 0; --i;)
653 {
654 ulong pc = pcs[i];
655 List<cmsx.object.FunctionTableEntry>.ConstIterator first = functionTable.CBegin();
656 List<cmsx.object.FunctionTableEntry>.ConstIterator last = functionTable.CEnd();
657 cmsx.object.FunctionTableEntry entry(pc, 0u, 0u, 0u, 0u, 0u, 0u, 0u);
658 List<cmsx.object.FunctionTableEntry>.ConstIterator it = LowerBound(first, last, entry);
659 if (it != first && it == last)
660 {
661 --it;
662 }
663 if (it != first && it != last && entry.start < it->start)
664 {
665 --it;
666 }
667 if (it != last && entry.start >= it->start && entry.start < it->start + it->length)
668 {
669 ulong fullNameAddress = it->fullNameAddress;
670 string fullName;
671 ReadStringFromProcessMemory(machine, process, dataSegmentBaseAddress + fullNameAddress, fullName);
672 ulong mangledNameAddress = it->mangledNameAddress;
673 string mangledName;
674 ReadStringFromProcessMemory(machine, process, dataSegmentBaseAddress + mangledNameAddress, mangledName);
675 ulong sourceFileNameAddress = it->sourceFileNameAddress;
676 string sourceFileName;
677 if (sourceFileNameAddress != 0u)
678 {
679 ReadStringFromProcessMemory(machine, process, dataSegmentBaseAddress + sourceFileNameAddress, sourceFileName);
680 }
681 string lineNumberStr;
682 ulong lineNumberTableStartAddress = it->lineNumberTableStartAddress;
683 ulong lineNumberTableEndAddress = it->lineNumberTableEndAddress;
684 List<cmsx.object.LineNumberTableEntry> lineNumberTable;
685 ReadLineNumberTableFromProcessMemory(machine, process, dataSegmentBaseAddress + lineNumberTableStartAddress, dataSegmentBaseAddress + lineNumberTableEndAddress, lineNumberTable);
686 List<cmsx.object.LineNumberTableEntry>.ConstIterator lnFirst = lineNumberTable.CBegin();
687 List<cmsx.object.LineNumberTableEntry>.ConstIterator lnLast = lineNumberTable.CEnd();
688 cmsx.object.LineNumberTableEntry lnEntry(cast<uint>(pc - it->start), 0u);
689 List<cmsx.object.LineNumberTableEntry>.ConstIterator lnIt = LowerBound(lnFirst, lnLast, lnEntry);
690 if (lnIt != lnFirst && lnIt == lnLast)
691 {
692 --lnIt;
693 }
694 if (lnIt != lnFirst && lnIt != lnLast && lnEntry.offset > lnIt->offset)
695 {
696 --lnIt;
697 }
698 if (lnIt != lnLast)
699 {
700 uint lineNumber = lnIt->lineNumber;
701 lineNumberStr.Append(':').Append(ToString(lineNumber));
702 }
703 string sourceFileNameStr;
704 if (!sourceFileName.IsEmpty())
705 {
706 sourceFileNameStr.Append(" : ").Append(sourceFileName);
707 }
708 stackTrace.Append(ToString(i)).Append(": ").Append(fullName).Append(" : ").Append(mangledName).Append(sourceFileNameStr).Append(lineNumberStr).Append('\n');
709 }
710 }
711 stackTrace.Append('\n');
712 return stackTrace;
713 }
714
715 public void GetProcessStackTrace(Machine& machine, Process* process, ulong fp, ulong buffer, long count)
716 {
717 if (buffer < poolSegmentBaseAddress || buffer >= stackSegmentBaseAddress)
718 {
719 throw SystemError(EINVAL, "invalid buffer virtual address " + ToHexString(buffer));
720 }
721 if (count <= 0)
722 {
723 throw SystemError(EINVAL, "invalid count " + ToString(count));
724 }
725 string stackTrace = GetProcessStackTrace(machine, process, fp);
726 if (count < stackTrace.Length())
727 {
728 stackTrace = stackTrace.Substring(stackTrace.Length() - count);
729 }
730 if (count > stackTrace.Length())
731 {
732 count = stackTrace.Length();
733 }
734 long len = Max(cast<long>(0), count - 1);
735 if (len > 0)
736 {
737 WriteProcessMemory(machine, process, buffer, cast<byte*>(cast<void*>(stackTrace.Chars())), cast<ulong>(len), Protection.write);
738 }
739 WriteProcessMemory(machine, process, buffer + cast<ulong>(len), 0u, 1u, Protection.write);
740 }
741
742 public void GetProcessSystemError(Machine& machine, Process* process, ulong errorCodeAddress, ulong bufferAddress, long count)
743 {
744 if (errorCodeAddress != 0u && errorCodeAddress < dataSegmentBaseAddress)
745 {
746 throw SystemError(EINVAL, "invalid error code virtual address " + ToHexString(errorCodeAddress));
747 }
748 if (bufferAddress < poolSegmentBaseAddress || bufferAddress >= stackSegmentBaseAddress)
749 {
750 throw SystemError(EINVAL, "invalid buffer virtual address " + ToHexString(bufferAddress));
751 }
752 if (count <= 0)
753 {
754 throw SystemError(EINVAL, "invalid count " + ToString(count));
755 }
756 if (errorCodeAddress != 0u)
757 {
758 int errorCode = process->lastError.errorCode;
759 WriteProcessMemory(machine, process, errorCodeAddress, cast<ulong>(errorCode), 4u, Protection.write);
760 }
761 string errorMessage = process->lastError.Message();
762 long len = Max(cast<long>(0), count - 1);
763 if (len > 0)
764 {
765 WriteProcessMemory(machine, process, bufferAddress, cast<byte*>(cast<void*>(errorMessage.Chars())), cast<ulong>(len), Protection.write);
766 }
767 WriteProcessMemory(machine, process, bufferAddress + cast<ulong>(len), 0u, 1u, Protection.write);
768 }
769
770 public nothrow void SaveContext(Machine& machine, Process* process)
771 {
772 ulong sp = process->memoryTable.segmentDescriptors[stackSegmentIndex]->baseAddress + process->memoryTable.segmentDescriptors[stackSegmentIndex]->startAddress;
773 Registers& regs = machine.GetRegisters();
774 ulong prevAX = regs.Get(regAX);
775 ulong prevBX = regs.Get(regBX);
776 ulong prevCX = regs.Get(regCX);
777 ulong prevDX = regs.Get(regDX);
778 ulong prevEX = regs.Get(regEX);
779 ulong prevFP = regs.Get(regFP);
780 ulong prevIX = regs.Get(regIX);
781 ulong prevPC = regs.GetPC();
782 ulong prevRL = regs.GetSpecial(Registers.rL);
783 ulong prevRG = regs.GetSpecial(Registers.rG);
784 ulong pc = process->entryPoint;
785 regs.SetPC(pc);
786 regs.Set(regAX, 0u);
787 regs.Set(regBX, 0u);
788 regs.Set(regCX, 0u);
789 regs.Set(regDX, 0u);
790 regs.Set(regEX, 0u);
791 regs.Set(regFP, 0u);
792 regs.Set(regIX, 0u);
793 regs.SetSpecial(Registers.rL, 0u);
794 regs.SetSpecial(Registers.rG, firstGlobalReg);
795 process->sp = machine.GetProcessor().SaveContext(process->memoryTable.virtualTranslationRegisterValue, sp, &process->regAXAddress, &process->regBXAddress, &process->regCXAddress);
796 regs.Set(regAX, prevAX);
797 regs.Set(regBX, prevBX);
798 regs.Set(regCX, prevCX);
799 regs.Set(regDX, prevDX);
800 regs.Set(regEX, prevEX);
801 regs.Set(regFP, prevFP);
802 regs.Set(regIX, prevIX);
803 regs.SetPC(prevPC);
804 regs.SetSpecial(Registers.rL, prevRL);
805 regs.SetSpecial(Registers.rG, prevRG);
806 }
807
808 public nothrow void InitializeProcessMemory(Machine& machine, Process* process,
809 ulong textSegmentStartAddress, ulong textSegmentSize, ulong dataSegmentSize, ulong poolSegmentSize, ulong minStackSegmentSize, ulong maxStackSegmentSize, ulong stackSegmentIncrement)
810 {
811 InitializeMemoryTable(machine, process->memoryTable, textSegmentStartAddress, textSegmentSize, dataSegmentSize, poolSegmentSize, minStackSegmentSize, maxStackSegmentSize, stackSegmentIncrement);
812 AllocateMemory(machine.GetMemory(), process->memoryTable, false);
813 SaveContext(machine, process);
814 }
815
816 public void CloneProcessMemory(Machine& machine, Process* child, Process* parent)
817 {
818 InitializeMemoryTableForCloning(machine, child->memoryTable);
819 CloneMemory(machine.GetMemory(), child->memoryTable, parent->memoryTable);
820 Registers& regs = machine.GetRegisters();
821 child->sp = machine.GetProcessor().SaveContext(child->memoryTable.virtualTranslationRegisterValue, regs.Get(regSP), &child->regAXAddress, &child->regBXAddress, &child->regCXAddress);
822 }
823
824 public nothrow void FreeProcessMemory(Memory& mem, Process* process)
825 {
826 FreeMemory(mem, process->memoryTable);
827 }
828
829 public void ReadProcessMemory(Machine& machine, Process* process, ulong address, ulong& value, byte size)
830 {
831 ReadProcessMemory(machine, process, address, value, size, true);
832 }
833
834 public void ReadProcessMemory(Machine& machine, Process* process, ulong address, ulong& value, byte size, bool pageFault)
835 {
836 Memory& mem = machine.GetMemory();
837 Registers& regs = machine.GetRegisters();
838 ulong prevRV = regs.GetSpecial(Registers.rV);
839 MemoryTable& memoryTable = process->memoryTable;
840 regs.SetSpecial(Registers.rV, memoryTable.virtualTranslationRegisterValue);
841 bool error = false;
842 switch (size)
843 {
844 case 1u: value = cast<ulong>(mem.ReadByte(address, Protection.read, pageFault)); break;
845 case 2u: value = cast<ulong>(mem.ReadUShort(address, Protection.read, pageFault)); break;
846 case 4u: value = cast<ulong>(mem.ReadUInt(address, Protection.read, pageFault)); break;
847 case 8u: value = cast<ulong>(mem.ReadULong(address, Protection.read, pageFault)); break;
848 default: regs.SetSpecial(Registers.rV, prevRV); error = true; break;
849 }
850 regs.SetSpecial(Registers.rV, prevRV);
851 if (error)
852 {
853 throw Exception("internal error: could not read process memory: invalid size " + ToString(size));
854 }
855 }
856
857 public void ReadProcessMemory(Machine& machine, Process* process, ulong address, byte* buffer, ulong size)
858 {
859 ReadProcessMemory(machine, process, address, buffer, size, true);
860 }
861
862 public void ReadProcessMemory(Machine& machine, Process* process, ulong address, byte* buffer, ulong size, bool pageFault)
863 {
864 Memory& mem = machine.GetMemory();
865 Registers& regs = machine.GetRegisters();
866 ulong prevRV = regs.GetSpecial(Registers.rV);
867 MemoryTable& memoryTable = process->memoryTable;
868 regs.SetSpecial(Registers.rV, memoryTable.virtualTranslationRegisterValue);
869 for (ulong i = 0u; i < size; ++i;)
870 {
871 *buffer = mem.ReadByte(address, Protection.read, pageFault);
872 ++buffer;
873 ++address;
874 }
875 regs.SetSpecial(Registers.rV, prevRV);
876 }
877
878 public void ReadStringFromProcessMemory(Machine& machine, Process* process, ulong address, string& s)
879 {
880 Memory& mem = machine.GetMemory();
881 Registers& regs = machine.GetRegisters();
882 ulong prevRV = regs.GetSpecial(Registers.rV);
883 MemoryTable& memoryTable = process->memoryTable;
884 regs.SetSpecial(Registers.rV, memoryTable.virtualTranslationRegisterValue);
885 byte x = mem.ReadByte(address, Protection.read);
886 while (x != 0u)
887 {
888 s.Append(cast<char>(x));
889 ++address;
890 x = mem.ReadByte(address, Protection.read);
891 }
892 regs.SetSpecial(Registers.rV, prevRV);
893 }
894
895 public void ReadFunctionTableFromProcessMemory(Machine& machine, Process* process, ulong functionTableAddress, ulong functionTableLength, List<cmsx.object.FunctionTableEntry>& functionTable)
896 {
897 Memory& mem = machine.GetMemory();
898 Registers& regs = machine.GetRegisters();
899 ulong prevRV = regs.GetSpecial(Registers.rV);
900 MemoryTable& memoryTable = process->memoryTable;
901 regs.SetSpecial(Registers.rV, memoryTable.virtualTranslationRegisterValue);
902 ulong functionTableEndAddress = functionTableAddress + functionTableLength;
903 for (ulong address = functionTableAddress; address < functionTableEndAddress; address = address + cast<ulong>(sizeof(cmsx.object.FunctionTableEntry));)
904 {
905 cmsx.object.FunctionTableEntry entry;
906 entry.start = mem.ReadULong(address, Protection.read);
907 entry.length = mem.ReadULong(address + 8u, Protection.read);
908 entry.mangledNameAddress = mem.ReadULong(address + 16u, Protection.read);
909 entry.fullNameAddress = mem.ReadULong(address + 24u, Protection.read);
910 entry.sourceFileNameAddress = mem.ReadULong(address + 32u, Protection.read);
911 entry.lineNumberTableStartAddress = mem.ReadULong(address + 40u, Protection.read);
912 entry.lineNumberTableEndAddress = mem.ReadULong(address + 48u, Protection.read);
913 entry.exceptionTableAddress = mem.ReadULong(address + 56u, Protection.read);
914 functionTable.Add(entry);
915 }
916 regs.SetSpecial(Registers.rV, prevRV);
917 }
918
919 public void ReadLineNumberTableFromProcessMemory(Machine& machine, Process* process, ulong lineNumberTableStartAddress, ulong lineNumberTableEndAddress,
920 List<cmsx.object.LineNumberTableEntry>& lineNumberTable)
921 {
922 Memory& mem = machine.GetMemory();
923 Registers& regs = machine.GetRegisters();
924 ulong prevRV = regs.GetSpecial(Registers.rV);
925 MemoryTable& memoryTable = process->memoryTable;
926 regs.SetSpecial(Registers.rV, memoryTable.virtualTranslationRegisterValue);
927 for (ulong address = lineNumberTableStartAddress; address < lineNumberTableEndAddress; address = address + cast<ulong>(sizeof(cmsx.object.LineNumberTableEntry));)
928 {
929 cmsx.object.LineNumberTableEntry entry;
930 entry.offset = mem.ReadUInt(address, Protection.read);
931 entry.lineNumber = mem.ReadUInt(address + 4u, Protection.read);
932 lineNumberTable.Add(entry);
933 }
934 regs.SetSpecial(Registers.rV, prevRV);
935 }
936
937 public void WriteProcessMemory(Machine& machine, Process* process, ulong address, ulong value, byte size, Protection access)
938 {
939 if (process->state == Process.State.created || process->state == Process.State.readyToRun || process->state == Process.State.running || process->state == Process.State.asleep)
940 {
941 Memory& mem = machine.GetMemory();
942 Registers& regs = machine.GetRegisters();
943 ulong prevRV = regs.GetSpecial(Registers.rV);
944 MemoryTable& memoryTable = process->memoryTable;
945 regs.SetSpecial(Registers.rV, memoryTable.virtualTranslationRegisterValue);
946 bool error = false;
947 switch (size)
948 {
949 case 1u: mem.WriteByte(address, cast<byte>(value), access); break;
950 case 2u: mem.WriteUShort(address, cast<ushort>(value), access); break;
951 case 4u: mem.WriteUInt(address, cast<uint>(value), access); break;
952 case 8u: mem.WriteULong(address, value, access); break;
953 default: regs.SetSpecial(Registers.rV, prevRV); error = true; break;
954 }
955 regs.SetSpecial(Registers.rV, prevRV);
956 if (error)
957 {
958 throw Exception("internal error: kernel could not write to process memory: invalid size " + ToString(size));
959 }
960 }
961 else
962 {
963 throw Exception("internal error: kernel could not write to process memory because process state not valid");
964 }
965 }
966
967 public void WriteProcessMemory(Machine& machine, Process* process, ulong targetAddress, byte* source, ulong size, Protection access)
968 {
969 if (process->state == Process.State.created || process->state == Process.State.readyToRun || process->state == Process.State.running || process->state == Process.State.asleep)
970 {
971 Memory& mem = machine.GetMemory();
972 Registers& regs = machine.GetRegisters();
973 ulong prevRV = regs.GetSpecial(Registers.rV);
974 MemoryTable& memoryTable = process->memoryTable;
975 regs.SetSpecial(Registers.rV, memoryTable.virtualTranslationRegisterValue);
976 ulong address = targetAddress;
977 for (ulong i = 0u; i < size; ++i;)
978 {
979 mem.WriteByte(address, *source, access);
980 ++address;
981 ++source;
982 }
983 regs.SetSpecial(Registers.rV, prevRV);
984 }
985 else
986 {
987 throw Exception("internal error: kernel could not write to process memory because process state not valid");
988 }
989 }
990
991 public nothrow void InitializeIdleProcess(Machine& machine, ProcessTable& processTable, Process* idleProcess)
992 {
993 idleProcess->pid = idlePID;
994 idleProcess->pgid = idlePID;
995 idleProcess->name = "idle";
996 idleProcess->entryPoint = 4096u;
997 idleProcess->state = Process.State.readyToRun;
998 idleProcess->parent = null;
999 idleProcess->firstChild = null;
1000 idleProcess->lastChild = null;
1001 idleProcess->nextSibling = null;
1002 idleProcess->nextFree = null;
1003 InitializeProcessMemory(machine, idleProcess, 4096u, 4096u, 0u, 0u, 4096u, 4096u, 0u);
1004 Registers& regs = machine.GetRegisters();
1005 Memory& mem = machine.GetMemory();
1006 MemoryTable& memoryTable = idleProcess->memoryTable;
1007 ulong prevRV = regs.GetSpecial(Registers.rV);
1008 regs.SetSpecial(Registers.rV, memoryTable.virtualTranslationRegisterValue);
1009 ulong addr = memoryTable.segmentDescriptors[textSegmentIndex]->startAddress;
1010 mem.WriteByte(addr, SWYM, Protection.execute);
1011 ++addr;
1012 mem.WriteByte(addr, 0u, Protection.execute);
1013 ++addr;
1014 mem.WriteByte(addr, 0u, Protection.execute);
1015 ++addr;
1016 mem.WriteByte(addr, 0u, Protection.execute);
1017 ++addr;
1018 mem.WriteByte(addr, JMPB, Protection.execute);
1019 ++addr;
1020 mem.WriteByte(addr, 0x00u, Protection.execute);
1021 ++addr;
1022 mem.WriteByte(addr, 0x00u, Protection.execute);
1023 ++addr;
1024 mem.WriteByte(addr, 0x1u, Protection.execute);
1025 regs.SetSpecial(Registers.rV, prevRV);
1026 processTable.AddProcessToProcessMap(idleProcess);
1027 }
1028
1029 public Process* CreateSingleProcess(Machine& machine, ProcessTable& processTable, const string& name, ulong entryPoint,
1030 ulong textSegmentBaseAddress, ulong textSegmentSize, ulong dataSegmentSize, ulong poolSegmentSize, ulong minStackSegmentSize, ulong maxStackSegmentSize, ulong stackSegmentIncrement, int sid)
1031 {
1032 Process* idleProcess = processTable.GetIdle();
1033 InitializeIdleProcess(machine, processTable, idleProcess);
1034 Process* process = CreateProcess(processTable, name, entryPoint, textSegmentBaseAddress, textSegmentSize, dataSegmentSize, poolSegmentSize,
1035 minStackSegmentSize, maxStackSegmentSize, stackSegmentIncrement, sid);
1036 SetProcessReadyToRun(processTable, process);
1037 return process;
1038 }
1039
1040 public nothrow void Schedule(Machine& machine, ProcessTable& processTable)
1041 {
1042 Processor& processor = machine.GetProcessor();
1043 Process* running = processTable.GetRunning();
1044 Process* prevRunning = running;
1045 Process* idle = processTable.GetIdle();
1046 if (processTable.ReadyQueueEmpty())
1047 {
1048 if (prevRunning == null || prevRunning->state != Process.State.running)
1049 {
1050 running = idle;
1051 }
1052 }
1053 else
1054 {
1055 running = processTable.GetNextReadyProcess();
1056 }
1057 if (prevRunning != running)
1058 {
1059 if (prevRunning != null)
1060 {
1061 if (prevRunning->state != Process.State.zombie && !prevRunning->unsave)
1062 {
1063 ulong sp = machine.GetRegisters().Get(cmsx.machine.regSP);
1064 prevRunning->sp = processor.SaveContext(prevRunning->memoryTable.virtualTranslationRegisterValue, sp,
1065 &prevRunning->regAXAddress, &prevRunning->regBXAddress, &prevRunning->regCXAddress);
1066 }
1067 if (prevRunning != idle && prevRunning->state == Process.State.running)
1068 {
1069 prevRunning->state = Process.State.readyToRun;
1070 processTable.PutToReadyQueue(prevRunning);
1071 }
1072 }
1073 running->unsave = false;
1074 running->sp = processor.UnsaveContext(running->memoryTable.virtualTranslationRegisterValue, running->sp);
1075 }
1076 else if (running->unsave)
1077 {
1078 running->unsave = false;
1079 running->sp = processor.UnsaveContext(running->memoryTable.virtualTranslationRegisterValue, running->sp);
1080 }
1081 processTable.SetRunning(running);
1082 running->state = Process.State.running;
1083 processor.GetRegisters().SetSpecial(Registers.rV, running->memoryTable.virtualTranslationRegisterValue);
1084 if (Log())
1085 {
1086 if (prevRunning != running)
1087 {
1088 LogMessage("proc.schedule", ProcessName(prevRunning) + "->" + ProcessName(running));
1089 }
1090 }
1091 }
1092
1093 public nothrow void StartUserTime()
1094 {
1095 Kernel& kernel = Kernel.Instance();
1096 ProcessTable& processTable = kernel.GetProcessTable();
1097 Process* running = processTable.GetRunning();
1098 running->userStart = Now();
1099 }
1100
1101 public nothrow void StopUserTime()
1102 {
1103 Kernel& kernel = Kernel.Instance();
1104 ProcessTable& processTable = kernel.GetProcessTable();
1105 Process* running = processTable.GetRunning();
1106 if (running->userStart != TimePoint(0))
1107 {
1108 running->userTime = running->userTime + Now() - running->userStart;
1109 }
1110 }
1111
1112 public void Times(Machine& machine, Process* process, ulong kernelTimeAddress, ulong userTimeAddress, ulong childKernelTimeAddress, ulong childUserTimeAddress)
1113 {
1114 if (kernelTimeAddress == 0u)
1115 {
1116 throw SystemError(EINVAL, "times: kernel time address is null");
1117 }
1118 if (userTimeAddress == 0u)
1119 {
1120 throw SystemError(EINVAL, "times: user time address is null");
1121 }
1122 if (childKernelTimeAddress == 0u)
1123 {
1124 throw SystemError(EINVAL, "times: child kernel time address is null");
1125 }
1126 if (childUserTimeAddress == 0u)
1127 {
1128 throw SystemError(EINVAL, "times: child user time address is null");
1129 }
1130 WriteProcessMemory(machine, process, kernelTimeAddress, cast<ulong>(process->kernelTime.Rep()), 8u, Protection.write);
1131 WriteProcessMemory(machine, process, userTimeAddress, cast<ulong>(process->userTime.Rep()), 8u, Protection.write);
1132 WriteProcessMemory(machine, process, childKernelTimeAddress, cast<ulong>(process->childKernelTime.Rep()), 8u, Protection.write);
1133 WriteProcessMemory(machine, process, childUserTimeAddress, cast<ulong>(process->childUserTime.Rep()), 8u, Protection.write);
1134 }
1135 }