2 Copyright © 2008-2012, The AROS Development Team. All rights reserved.
5 Desc: CPU-specific add-ons for Windows-hosted scheduler.
6 Context save, restore, and task exception handling.
10 #include <exec/execbase.h>
11 #include <proto/exec.h>
13 #include "kernel_base.h"
14 #include "kernel_debug.h"
15 #include "kernel_mingw32.h"
16 #include "kernel_scheduler.h"
23 * User-mode part of exception handling. Save context, call
24 * exec handler, then resume task.
25 * Note that interrupts are disabled and SysBase->IDNestCnt contains 0
26 * upon entry. Real IDNestCnt count for the task is stored in its tc_IDNestCnt.
28 * We have to do this complex trick with disabling interrupts because
29 * in Windows exception handler operates on thread's stack, this means
30 * we can't modify the stack inside exception handler, i.e. we can't save
31 * the context on task's stack.
33 * In order to overcome this we forcibly disable interrupts in core_Dispatch()
34 * and make the task to jump here. After this et_RegFrame still contains
35 * unmodified saved task context. Since we're running normally on our stack,
36 * we can save the context on the stack here.
37 * Inside Exception() interrupts and task switching will be enabled, so original
38 * IDNestCnt will be lost. In order to prevent it we save it on the stack too.
40 * When we're done we pick up saved IDNestCnt from stack and raise
41 * AROS_EXCEPTION_RESUME in order to jump back to the saved context.
43 static void cpu_Exception()
45 /* Save return context and IDNestCnt on stack */
46 struct Task
*task
= SysBase
->ThisTask
;
47 struct ExceptionContext
*ctx
= task
->tc_UnionETask
.tc_ETask
->et_RegFrame
;
48 char nestCnt
= task
->tc_IDNestCnt
;
49 char ContextSave
[KernelBase
->kb_ContextSize
];
50 struct ExceptionContext
*csPtr
= (struct ExceptionContext
*)ContextSave
;
52 DEXCEPT(bug("[KRN] Entered exception, task 0x%p, IDNestCnt %d\n", task
, SysBase
->IDNestCnt
));
53 /* Save original context */
54 CopyMem(ctx
, ContextSave
, sizeof(struct AROSCPUContext
));
57 /* Call exec exception processing */
60 /* Restore saved task state and resume it. Note that interrupts are
61 disabled again here */
62 task
->tc_IDNestCnt
= nestCnt
;
63 SysBase
->IDNestCnt
= nestCnt
;
65 D(bug("[KRN] Leaving exception, IDNestCnt %d\n", SysBase
->IDNestCnt
));
66 KernelIFace
.core_raise(AROS_EXCEPTION_RESUME
, (IPTR
)ContextSave
);
69 /* CPU-specific Switch() bits. Actually just context save. */
70 void cpu_Switch(CONTEXT
*regs
)
72 struct Task
*t
= SysBase
->ThisTask
;
73 struct AROSCPUContext
*ctx
= t
->tc_UnionETask
.tc_ETask
->et_RegFrame
;
75 /* Actually save the context */
77 ctx
->LastError
= **KernelIFace
.LastError
;
80 t
->tc_SPReg
= GET_SP(ctx
);
86 * CPU-dependent wrapper around core_Dispatch(). Implements
87 * context restore, CPU idle loop, and task exceptions.
89 void cpu_Dispatch(CONTEXT
*regs
)
91 struct Task
*task
= core_Dispatch();
92 struct AROSCPUContext
*ctx
;
97 * There are no ready tasks and we need to go idle.
98 * Because of the way how our emulation works, we do not
99 * have a real loop here, unlike most ports. Instead we
100 * signal idle state and exit. Then there can be two possibilities:
101 * a) we are called by our virtual machine's supervisor thread.
102 * In this case it will just not resume usermode thread, and
103 * will go on with interrupts processing.
104 * b) we are called by usermode thread using core_Rise(). In this
105 * case we will continue execution of the code and hit while(Sleep_Mode);
106 * spinlock in core_Rise(). We will spin until supervisor thread interrupts
107 * us and actually puts asleep.
108 * We can't implement idle loop similar to other ports here because of (b)
109 * case. We would just deadlock then since interrupt processing is actually
110 * disabled during Windows exception processing (which is also an interrupt
113 DSLEEP(if (!Get_Sleep_Mode()) bug("[KRN] TaskReady list empty. Sleeping for a while...\n"));
115 /* This will enable interrupts in core_LeaveInterrupt() */
116 SysBase
->IDNestCnt
= -1;
118 /* We are entering sleep mode */
119 *KernelIFace
.SleepState
= SLEEP_MODE_PENDING
;
124 *KernelIFace
.SleepState
= SLEEP_MODE_OFF
;
126 D(bug("[KRN] Dispatched task 0x%p (%s)\n", task
, task
->tc_Node
.ln_Name
));
127 /* Restore the task's context */
128 ctx
= task
->tc_UnionETask
.tc_ETask
->et_RegFrame
;
129 RESTOREREGS(regs
, ctx
);
130 **KernelIFace
.LastError
= ctx
->LastError
;
132 /* Handle exception if requested */
133 if (task
->tc_Flags
& TF_EXCEPT
)
135 DEXCEPT(bug("[KRN] Exception requested for task 0x%p, return PC = 0x%p\n", task
, PC(regs
)));
137 /* Disable interrupts, otherwise we may lose saved context */
138 SysBase
->IDNestCnt
= 0;
140 /* Make the task to jump to exception handler */
141 PC(regs
) = (IPTR
)cpu_Exception
;