1 using System;
2 using System.Collections;
3 using cmsx.machine;
4
5 namespace cmsx.kernel
6 {
7 public ulong kernelFiberStackSize = 64u * 1024u;
8
9 public delegate void SystemCallFiberDelegate(void* processParam);
10
11 public class ProcessDueTimeLess : Rel<Process*>
12 {
13 public nothrow bool operator()(Process* left, Process* right) const
14 {
15 #assert(left->state == Process.State.asleep);
16 #assert(left->event == wakeupEvent);
17 #assert(right->state == Process.State.asleep);
18 #assert(right->event == wakeupEvent);
19 long leftDueTime = cast<long>(left->eventData1);
20 long rightDueTime = cast<long>(right->eventData1);
21 return leftDueTime < rightDueTime;
22 }
23 }
24
25 public class SleepingProcessQueue
26 {
27 static SleepingProcessQueue() : instance(new SleepingProcessQueue())
28 {
29 }
30 public static SleepingProcessQueue& Instance()
31 {
32 return *instance;
33 }
34 public void AddProcess(Process* process)
35 {
36 sleepingProcesses.Insert(process);
37 }
38 public List<Process*> GetProcessesToWakeUp(long dueTime)
39 {
40 List<Process*> processesToWakeUp;
41 while (!sleepingProcesses.IsEmpty())
42 {
43 Process* process = *sleepingProcesses.Begin();
44 #assert(process->state == Process.State.asleep);
45 #assert(process->event == wakeupEvent);
46 long processDueTime = cast<long>(process->eventData1);
47 if (processDueTime <= dueTime)
48 {
49 sleepingProcesses.Remove(sleepingProcesses.Begin());
50 processesToWakeUp.Add(process);
51 }
52 else
53 {
54 break;
55 }
56 }
57 return processesToWakeUp;
58 }
59 private static UniquePtr<SleepingProcessQueue> instance;
60 private Set<Process*, ProcessDueTimeLess> sleepingProcesses;
61 }
62
63 public nothrow void ClockInterruptHandler(Machine& machine, byte irq)
64 {
65 if (Log())
66 {
67 LogMessage("intr", "clock");
68 }
69 Kernel& kernel = Kernel.Instance();
70 ProcessTable& processTable = kernel.GetProcessTable();
71 List<Process*> processesToWakeUp = SleepingProcessQueue.Instance().GetProcessesToWakeUp(Now().Rep());
72 for (Process* process : processesToWakeUp)
73 {
74 WakeUpProcess(processTable, process);
75 }
76 Schedule(machine, processTable);
77 }
78
79 public void PageFaultHandler(Machine& machine, byte irq)
80 {
81 if (Log())
82 {
83 LogMessage("intr", "pagefault");
84 }
85 Registers& regs = machine.GetRegisters();
86 ulong yy = regs.GetSpecial(Registers.rYY);
87 Protection pageProtection = Protection.notPresent;
88 Protection protection = Protection.notPresent;
89 UnpackProtection(yy, pageProtection, protection);
90 ulong virtualAddress = machine.GetRegisters().GetSpecial(Registers.rZZ);
91 string registers = regs.ToString();
92 Kernel& kernel = Kernel.Instance();
93 ProcessTable& processTable = kernel.GetProcessTable();
94 Process* process = processTable.GetRunning();
95 string runningProcessName = "?";
96 string stackTrace;
97 string error = "page fault";
98 if (process != null)
99 {
100 runningProcessName = ProcessName(process);
101 if ((virtualAddress >= stackSegmentBaseAddress && virtualAddress < kernelBaseAddress))
102 {
103 Console.Error() << "stack overflow." << endl();
104 error = "stack overflow";
105 }
106 else
107 {
108 Console.Error() << "page fault." << endl();
109 stackTrace = GetProcessStackTrace(machine, process, machine.GetRegisters().Get(cmsx.machine.regFP));
110 }
111 }
112 Debugger* debugger = kernel.GetDebugger();
113 string errorlf = error + "\n";
114 debugger->WriteOutput(cast<byte*>(cast<void*>(errorlf.Chars())), errorlf.Length());
115 debugger->Run(process);
116 Panic(error + ", virtual address = " + ToHexString(virtualAddress) + ", page protection = " + GetProtectionStr(pageProtection) + ", " + GetProtectionStr(protection) + " needed (running process=" +
117 runningProcessName + ")\n" + stackTrace + "\nRegisters:\n" + registers);
118 }
119
120 public void SecurityViolationHandler(Machine& machine, byte irq)
121 {
122 if (Log())
123 {
124 LogMessage("intr", "security");
125 }
126 ulong virtualAddress = machine.GetRegisters().GetSpecial(Registers.rZZ);
127 Kernel& kernel = Kernel.Instance();
128 ProcessTable& processTable = kernel.GetProcessTable();
129 Process* process = processTable.GetRunning();
130 string runningProcessName = "?";
131 string stackTrace;
132 Console.Error() << "security violation: virtual address = " << ToHexString(virtualAddress) << endl();
133 if (process != null)
134 {
135 runningProcessName = ProcessName(process);
136 stackTrace = GetProcessStackTrace(machine, process, machine.GetRegisters().Get(cmsx.machine.regFP));
137 }
138 Panic("security violation, virtual address = " + ToHexString(virtualAddress) + ", processor not in kernel mode (running process=" + runningProcessName + ")\n" + stackTrace);
139 }
140
141 public void SoftwareInterruptHandler(Machine& machine, byte irq)
142 {
143 if (Log())
144 {
145 LogMessage("intr", "software");
146 }
147 Registers& regs = machine.GetRegisters();
148 ulong prevRV = regs.GetSpecial(Registers.rV);
149 ulong kernelRV = MakeVirtualTranslationRegisterValue(machine.GetKernelRootPageAddress(), kernelAddressSpaceNumber);
150 regs.SetSpecial(Registers.rV, kernelRV);
151 ulong trapValue = regs.GetSpecial(Registers.rX);
152 byte trapX = cast<byte>((trapValue & trapXMask) >> trapXShift);
153 byte trapY = cast<byte>((trapValue & trapYMask) >> trapYShift);
154 byte trapZ = cast<byte>((trapValue & trapZMask) >> trapZShift);
155 ulong trapAX = regs.Get(regAX);
156 ulong trapBX = regs.Get(regBX);
157 ulong trapCX = regs.Get(regCX);
158 ulong trapDX = regs.Get(regDX);
159 ulong trapEX = regs.Get(regEX);
160 ulong trapOffset = cast<ulong>(trapY) << 3u;
161 ulong systemCallTableBaseAddress = regs.GetSpecial(Registers.rT);
162 ulong systemCallHandlerPtrAddress = systemCallTableBaseAddress + trapOffset;
163 ulong systemCallHandlerAddress = machine.GetMemory().ReadULong(systemCallHandlerPtrAddress, Protection.read);
164 regs.SetSpecial(Registers.rV, prevRV);
165 if (systemCallHandlerAddress == 0u)
166 {
167 Panic("handler for trap " + ToString(trapY) + " is null");
168 }
169 SystemCallHandler handler = cast<SystemCallHandler>(cast<void*>(systemCallHandlerAddress));
170 Kernel& kernel = Kernel.Instance();
171 ProcessTable& processTable = kernel.GetProcessTable();
172 Process* process = processTable.GetRunning();
173 if (process == null)
174 {
175 Panic("system call called for a process that is not running");
176 }
177 if (process->fiber == null)
178 {
179 SystemCallFiberDelegate systemCallFiber = SystemCallFiber;
180 void* systemCallFiberStartAddress = cast<void*>(systemCallFiber);
181 process->fiber = OsCreateFiber(kernelFiberStackSize, systemCallFiberStartAddress, process);
182 }
183 process->mode = Process.Mode.runningKernel;
184 process->systemCall = SystemCall(&machine, &kernel, &processTable, process, handler, trapX, trapY, trapZ, trapAX, trapBX, trapCX, trapDX, trapEX);
185 OsSwitchToFiber(process->fiber);
186 }
187
188 public nothrow void IntervalInterruptHandler(Machine& machine, byte irq)
189 {
190
191 }
192
193 public nothrow void DiskInterruptHandler(Machine& machine, byte irq)
194 {
195 if (Log())
196 {
197 LogMessage("intr", "disk");
198 }
199 DiskDriver& diskDriver = GetDiskDriver();
200 diskDriver.InterruptService();
201 }
202
203 public nothrow void KeyboardInterruptHandler(Machine& machine, byte irq)
204 {
205 if (Log())
206 {
207 LogMessage("intr", "kb");
208 }
209 Kernel& kernel = Kernel.Instance();
210 ConsoleDriver* consoleDriver = kernel.GetConsoleDriver();
211 if (consoleDriver != null)
212 {
213 consoleDriver->InterruptService();
214 }
215 }
216
217 public void KernelInterruptReturn()
218 {
219 if (Log())
220 {
221 LogMessage("intr.return", "begin");
222 }
223 Machine& machine = GetMachine();
224 Kernel& kernel = Kernel.Instance();
225 ProcessTable& processTable = kernel.GetProcessTable();
226 Process* process = processTable.GetRunning();
227 if (process == null)
228 {
229 Panic("no process is running");
230 }
231 while (process->state == Process.State.zombie || process->state == Process.State.asleep || process->mode == Process.Mode.runningKernel && process->state == Process.State.running)
232 {
233 if (process->state == Process.State.zombie)
234 {
235 if (process->fiber != null)
236 {
237 OsDeleteFiber(process->fiber);
238 process->fiber = null;
239 }
240 Schedule(machine, processTable);
241 process = processTable.GetRunning();
242 }
243 else if (process->state == Process.State.asleep)
244 {
245 Schedule(machine, processTable);
246 process = processTable.GetRunning();
247 }
248 else if (process->mode == Process.Mode.runningKernel && process->state == Process.State.running)
249 {
250 if (Log())
251 {
252 LogMessage("intr.return", ProcessName(process) + ".runkernel");
253 }
254 OsSwitchToFiber(process->fiber);
255 }
256 }
257 if (process->systemCallReady && !process->systemCall.IsDebugBreak())
258 {
259 process->systemCallReady = false;
260 long result = process->systemCall.result;
261 ulong returnValue = cast<ulong>(result);
262 if (Log())
263 {
264 LogMessage("intr.return", ProcessName(process) + ".return=" + ToString(returnValue));
265 }
266 Registers& regs = machine.GetRegisters();
267 regs.Set(regAX, returnValue);
268 }
269 if (Log())
270 {
271 LogMessage("intr.return", "end");
272 }
273 }
274 }