// Page Fault
if(Regs->int_num == 14)
{
- __asm__ __volatile__ ("sti"); // Should be OK, TODO: Test
__asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr));
MM_PageFault( cr, Regs->err_code, Regs );
return ;
void KernelPanic_SetMode(void)
{
int i;
+
+ __asm__ __volatile__ ("cli"); // Stop the processor!
// This function is called by Panic(), but MM_PageFault and the
// CPU exception handers also call it, so let's not clear the screen
&& gaPageTable[Addr>>12] & PF_COW )
{
tPAddr paddr;
+ __asm__ __volatile__ ("sti");
if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1)
{
gaPageTable[Addr>>12] &= ~PF_COW;
// If it was a user, tell the thread handler
if(ErrorCode & 4) {
+ __asm__ __volatile__ ("sti");
Log_Warning("MMVirt", "User %s %s memory%s",
(ErrorCode&2?"write to":"read from"),
(ErrorCode&1?"bad/locked":"non-present"),
Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active
Uint16 Resvd;
tThread *Current;
+ tThread *LastTimerThread; // Used to do preeemption
} tCPU;
// === IMPORTS ===
// Update CPU state
gaCPUs[cpu].Current = nextthread;
+ gaCPUs[cpu].LastTimerThread = NULL;
gTSSs[cpu].ESP0 = nextthread->KernelStack-4;
__asm__ __volatile__("mov %0, %%db0\n\t" : : "r"(nextthread) );
*/
void Proc_Scheduler(int CPU)
{
-#if 0
- tThread *thread;
-
- // If the spinlock is set, let it complete
- if(IS_LOCKED(&glThreadListLock)) return;
-
- // Get current thread
- thread = gaCPUs[CPU].Current;
-
- if( thread )
+ // If two ticks happen within the same task, and it's not an idle task, swap
+ if( gaCPUs[CPU].Current->TID > giNumCPUs && gaCPUs[CPU].Current == gaCPUs[CPU].LastTimerThread )
{
- tRegs *regs;
- Uint ebp;
- // Reduce remaining quantum and continue timeslice if non-zero
- if( thread->Remaining-- )
- return;
- // Reset quantum for next call
- thread->Remaining = thread->Quantum;
-
- // TODO: Make this more stable somehow
- __asm__ __volatile__("mov %%ebp, %0" : "=r" (ebp));
- regs = (tRegs*)(ebp+(2+2)*4); // EBP,Ret + CPU,CurThread
- thread->SavedState.UserCS = regs->cs;
- thread->SavedState.UserEIP = regs->eip;
-
- if(thread->bInstrTrace) {
- regs->eflags |= 0x100; // Set TF
- Log("%p De-scheduled", thread);
- }
- else
- regs->eflags &= ~0x100; // Clear TF
+ Proc_Reschedule();
}
-
- // TODO: Ack timer?
- #if USE_MP
- if( GetCPUNum() )
- gpMP_LocalAPIC->EOI.Val = 0;
- else
- #endif
- outb(0x20, 0x20);
- __asm__ __volatile__ ("sti");
- Proc_Reschedule();
-#endif
+ gaCPUs[CPU].LastTimerThread = gaCPUs[CPU].Current;
+ // Call the timer update code
+ Timer_CallTimers();
}
// === EXPORTS ===
#define DEBUG_MAX_LINE_LEN 256
-#define LOCK_DEBUG_OUTPUT 1
+#define LOCK_DEBUG_OUTPUT 0
#define TRACE_TO_KTERM 0
#define PACKED __attribute__((packed))
//! Mark a function as not returning
#define NORETURN __attribute__((noreturn))
+//! Mark a function (or variable) as deprecated
+#define DEPRECATED __attribute__((deprecated))
//! Mark a parameter as unused
#define UNUSED(x) UNUSED_##x __attribute__((unused))
//! Get the offset of a member in a structure
# define LEAVE_RET0() return
#endif
#if SANITY
-# define ASSERT(expr) do{if(!(expr))Panic("%s: Assertion '"#expr"' failed",(char*)__func__);}while(0)
+# define ASSERT(expr) do{if(!(expr))Panic("%s:%i - %s: Assertion '"#expr"' failed",__FILE__,__LINE__,(char*)__func__);}while(0)
#else
# define ASSERT(expr)
#endif
* \param Callback Function to call each time
* \param Argument Argument to pass to the callback
*/
-extern tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument);
+extern tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument) DEPRECATED;
+
+/**
+ * \brief Allocate (but don't schedule) a timer object
+ * \param Callback Function to call (if NULL, EVENT_TIMER is delivered to the current thread)
+ * \param Argument Argument passed to \a Callback (ignored if \a Callback is NULL)
+ * \return New timer pointer
+ */
+extern tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument);
+
+/**
+ * \brief Free an allocated timer object
+ * \param Timer Object pointer returned by Time_AllocateTimer
+ */
+extern void Time_FreeTimer(tTimer *Timer);
+
+/**
+ * \brief Schedules a timer to fire in \a Delta ms
+ * \param Timer Timer object returned by Time_AllocateTimer
+ * \param Delta Time until timer fires (in milliseconds)
+ */
+extern void Time_ScheduleTimer(tTimer *Timer, int Delta);
+
/**
* \brief Removed an active timer
*/
extern void Time_RemoveTimer(tTimer *Timer);
+
/**
* \brief Wait for a period of milliseconds
*/
extern int Modules_LoadBuiltins(void);
extern void Modules_SetBuiltinParams(char *Name, char *ArgString);
extern void Debug_SetKTerminal(const char *File);
+extern void Timer_CallbackThread(void *);
// === PROTOTYPES ===
void System_Init(char *Commandline);
// === CODE ===
void System_Init(char *CommandLine)
{
+ Proc_SpawnWorker(Timer_CallbackThread, NULL);
+
// Parse Kernel's Command Line
System_ParseCommandLine(CommandLine);
ret->Next = NULL;
ret->Remaining = 0;
- giNumActiveThreads --;
// no need to decrement tickets, scheduler did it for us
#if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
return ret;
#else
+ giNumActiveThreads --;
return Proc_GetCurThread();
#endif
}
*
* Timer Code
*/
+#define SANITY 1 // Enable ASSERTs
+#define DEBUG 0
#include <acess.h>
#include <timers.h>
#include <events.h>
#include <hal_proc.h> // Proc_GetCurThread
-
-// === CONSTANTS ===
-#define NUM_TIMERS 8
+#include <workqueue.h>
// === TYPEDEFS ===
struct sTimer {
Sint64 FiresAfter;
void (*Callback)(void*);
void *Argument;
+// tMutex Lock;
+ BOOL bActive;
};
// === PROTOTYPES ===
+void Timer_CallbackThread(void *Unused);
void Timer_CallTimers(void);
+#if 0
+tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument);
+void Time_ScheduleTimer(tTimer *Timer, int Delta);
+void Time_RemoveTimer(tTimer *Timer);
+tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument);
+#endif
+void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument);
+#if 0
+void Time_FreeTimer(tTimer *Timer);
+void Time_Delay(int Time);
+#endif
// === GLOBALS ===
volatile Uint64 giTicks = 0;
volatile Sint64 giTimestamp = 0;
volatile Uint64 giPartMiliseconds = 0;
-tTimer *gTimers; // TODO: Replace by a ring-list timer
+tTimer *gTimers;
+tWorkqueue gTimers_CallbackQueue;
+tShortSpinlock gTimers_ListLock;
// === CODE ===
+void Timer_CallbackThread(void *Unused)
+{
+ Threads_SetName("Timer Callback Thread");
+
+ for(;;)
+ {
+ tTimer *timer = Workqueue_GetWork(&gTimers_CallbackQueue);
+
+ if( !timer->Callback ) {
+ LOG("Timer %p doesn't have a callback", timer);
+ ASSERT( timer->Callback );
+ }
+
+ // Save callback and argument (because once the mutex is released
+ // the timer may no longer be valid)
+ tTimerCallback *cb = timer->Callback;
+ void *arg = timer->Argument;
+
+ LOG("Callback fire %p", timer);
+
+ // Allow Time_RemoveTimer to be called safely
+ timer->bActive = 0;
+
+ // Fire callback
+ cb(arg);
+
+ // Mark timer as no longer needed
+ }
+}
+
/**
* \fn void Timer_CallTimers()
*/
void Timer_CallTimers()
{
+ SHORTLOCK(&gTimers_ListLock);
while( gTimers && gTimers->FiresAfter < now() )
{
- tTimer *next;
+ // Get timer from list
+ tTimer *timer = gTimers;
- if( gTimers->Callback )
- gTimers->Callback(gTimers->Argument);
- else
- Threads_PostEvent(gTimers->Argument, THREAD_EVENT_TIMER);
-
- next = gTimers->Next;
- free(gTimers);
- gTimers = next;
+ ASSERT( gTimers != gTimers->Next );
+ gTimers = gTimers->Next;
+
+ // Perform event
+ if( timer->Callback ) {
+ LOG("Callback schedule %p", timer);
+ // PROBLEM! Possibly causes rescheudle during interrupt
+// Mutex_Acquire( &timer->Lock ); // Released once the callback fires
+ Workqueue_AddWork(&gTimers_CallbackQueue, timer);
+ }
+ else {
+ LOG("Event fire %p", timer);
+ ASSERT( timer->Argument );
+ Threads_PostEvent(timer->Argument, THREAD_EVENT_TIMER);
+ timer->bActive = 0;
+ }
}
+ SHORTREL(&gTimers_ListLock);
}
/**
- * \brief Schedule an action
+ * \brief Schedule an action (Legacy)
*/
tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
{
- tTimer *ret;
+ tTimer *ret = malloc(sizeof(tTimer));
+ if( !ret ) return NULL;
+ Time_InitTimer(ret, Callback, Argument);
+ Time_ScheduleTimer(ret, Delta);
+ return ret;
+}
+
+/**
+ * \brief Schedule a timer to fire
+ */
+void Time_ScheduleTimer(tTimer *Timer, int Delta)
+{
tTimer *t, *p;
-
- if(Callback == NULL)
- Argument = Proc_GetCurThread();
- // TODO: Use a pool instead?
- ret = malloc(sizeof(tTimer));
+ // Sanity checks
+ if( !Timer ) return ;
+
+ if( Timer->bActive ) return;
+
+ // Set time
+ Timer->FiresAfter = now() + Delta;
- ret->Callback = Callback;
- ret->FiresAfter = now() + Delta;
- ret->Argument = Argument;
+ // Debug
+ LOG("%p added timer %p - %i ms (ts=%lli)",
+ __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
// Add into list (sorted)
+ SHORTLOCK(&gTimers_ListLock);
+// Mutex_Release( &Timer->Lock ); // Prevent deadlocks
for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
{
- if( t->FiresAfter > ret->FiresAfter ) break;
+ ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
+ if( t == Timer )
+ {
+ LOG("Double schedule - increasing delta");
+ SHORTREL(&gTimers_ListLock);
+ return ;
+ }
+ LOG(" t = %p ts:%lli", t, t->FiresAfter);
+ if( t->FiresAfter > Timer->FiresAfter ) break;
}
- ret->Next = t;
- p->Next = ret;
-
- return ret;
+ Timer->Next = t;
+ p->Next = Timer;
+ Timer->bActive = 1;
+ LOG(" %p %p %p", p, Timer, t);
+ SHORTREL(&gTimers_ListLock);
}
/**
- * \brief Delete a timer
+ * \brief Delete a timer from the running list
*/
void Time_RemoveTimer(tTimer *Timer)
{
tTimer *t, *p;
+
+ if( !Timer ) return ;
+
+ SHORTLOCK(&gTimers_ListLock);
for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
{
+ ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
if( t == Timer )
{
p->Next = t->Next;
- free(Timer);
- return ;
+ break ;
}
}
+ SHORTREL(&gTimers_ListLock);
+
+ if( t ) {
+ Timer->bActive = 0;
+ LOG("%p removed %p", __builtin_return_address(0), Timer);
+ }
+ else
+ LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
+}
+
+/**
+ * \brief Allocate a timer object, but don't schedule it
+ */
+tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
+{
+ tTimer *ret = malloc(sizeof(tTimer));
+ if( !ret ) return NULL;
+ Time_InitTimer(ret, Callback, Argument);
+ return ret;
+}
+
+/**
+ * \brief Initialise a timer
+ * \note Mostly an internal function
+ */
+void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
+{
+ if(Callback == NULL)
+ Argument = Proc_GetCurThread();
+ Timer->FiresAfter = 0;
+ Timer->Callback = Callback;
+ Timer->Argument = Argument;
+// memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
+ Timer->bActive = 0;
+}
+
+/**
+ * \brief Free an allocated timer
+ */
+void Time_FreeTimer(tTimer *Timer)
+{
+ if( !Timer ) return ;
+ Time_RemoveTimer( Timer ); // Just in case
+
+ // Ensures that we don't free until the timer callback has started
+ while( Timer->bActive ) Threads_Yield();
+ // Release won't be needed, as nothing should be waiting on it
+// Mutex_Acquire( &Timer->Lock );
+
+ // Free timer
+ free(Timer);
+ LOG("%p deallocated %p", __builtin_return_address(0), Timer);
}
/**
*/
void Time_Delay(int Delay)
{
-// tTime dest = now() + Delay;
-// while(dest > now()) Threads_Yield();
- Time_CreateTimer(Delay, NULL, NULL);
+ tTimer *t;
+ t = Time_AllocateTimer(NULL, NULL);
+ Time_ScheduleTimer(t, Delay);
Threads_WaitEvents(THREAD_EVENT_TIMER);
+ Time_FreeTimer(t);
}
// === EXPORTS ===
-EXPORT(Time_CreateTimer);
+//EXPORT(Time_CreateTimer);
+EXPORT(Time_ScheduleTimer);
EXPORT(Time_RemoveTimer);
+EXPORT(Time_AllocateTimer);
+//EXPORT(Time_InitTimer);
+EXPORT(Time_FreeTimer);
EXPORT(Time_Delay);
#include <workqueue.h>
#include <threads_int.h>
+#define QUEUENEXT(ptr) (*( (void**)(ptr) + Queue->NextOffset/sizeof(void*) ))
+
// === CODE ===
void Workqueue_Init(tWorkqueue *Queue, const char *Name, size_t NextOfset)
{
if(Queue->Head)
{
void *ret = Queue->Head;
- Queue->Head = *( (void**)ret + Queue->NextOffset/sizeof(void*) );
+ Queue->Head = QUEUENEXT( ret );
if(Queue->Tail == ret)
Queue->Tail = NULL;
SHORTREL(&Queue->Protector);
SHORTLOCK(&Queue->Protector);
if( Queue->Tail )
- *( (void**)Queue->Tail + Queue->NextOffset/sizeof(void*) ) = Ptr;
+ QUEUENEXT(Queue->Tail) = Ptr;
else
Queue->Head = Ptr;
Queue->Tail = Ptr;
-
+ QUEUENEXT(Ptr) = NULL;
+
if( Queue->Sleeper )
{
if( Queue->Sleeper->Status != THREAD_STAT_ACTIVE )
#include <modules.h>\r
#include <vm8086.h>\r
#include "common.h"\r
+#include <timers.h>\r
\r
// === CONSTANTS ===\r
#define FLAG_LFB 0x1\r
// --- Cursor Control ---\r
int giVesaCursorX = -1;\r
int giVesaCursorY = -1;\r
- int giVesaCursorTimer = -1; // Invalid timer\r
+#if BLINKING_CURSOR\r
+tTimer *gpVesaCursorTimer;\r
+#endif\r
int gbVesa_CursorVisible = 0;\r
// --- 2D Video Stream Handlers ---\r
tDrvUtil_Video_BufInfo gVesa_BufInfo;\r
\r
// VM8086_Deallocate( info );\r
\r
+ #if BLINKING_CURSOR\r
+ // Create blink timer\r
+ gpVesaCursorTimer = Time_AllocateTimer( Vesa_FlipCursor, NULL );\r
+ #endif\r
+\r
// Install Device\r
giVesaDriverId = DevFS_AddDevice( &gVesa_DriverStruct );\r
if(giVesaDriverId == -1) return MODULE_ERR_MISC;\r
- \r
+\r
return MODULE_ERR_OK;\r
}\r
\r
\r
Vesa_int_FillModeList();\r
\r
- Time_RemoveTimer(giVesaCursorTimer);\r
- giVesaCursorTimer = -1;\r
+ Time_RemoveTimer(gpVesaCursorTimer);\r
\r
Mutex_Acquire( &glVesa_Lock );\r
\r
{\r
DrvUtil_Video_RemoveCursor( &gVesa_BufInfo );\r
#if BLINKING_CURSOR\r
- if(giVesaCursorTimer != -1) {\r
- Time_RemoveTimer(giVesaCursorTimer);\r
- giVesaCursorTimer = -1;\r
+ if(gpVesaCursorTimer) {\r
+ Time_RemoveTimer(gpVesaCursorTimer);\r
}\r
#endif\r
}\r
giVesaCursorY*giVT_CharHeight\r
);\r
#if BLINKING_CURSOR\r
- giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, NULL);\r
+ Time_ScheduleTimer( gpVesaCursorTimer, VESA_CURSOR_PERIOD );\r
#endif\r
}\r
else\r
gbVesa_CursorVisible = !gbVesa_CursorVisible;\r
\r
#if BLINKING_CURSOR\r
- giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, Arg);\r
+ Time_ScheduleTimer( gpVesaCursorTimer, VESA_CURSOR_PERIOD );\r
#endif\r
}\r
\r
// Install IRQ6 Handler
IRQ_AddHandler(6, FDD_int_IRQHandler, NULL);
+ for( int i = 0; i < 2; i ++ )
+ {
+ if( !gaFDD_Disks[i].bValid ) continue ;
+
+ gaFDD_Disks[i].Timer = Time_AllocateTimer(FDD_int_StopMotorCallback, (void*)(tVAddr)i);
+ }
+
// Reset controller
FDD_int_Reset(0);
// TODO: All controllers
-
+
return 0;
}
// Clear the motor off timer
Time_RemoveTimer(gaFDD_Disks[Disk].Timer);
- gaFDD_Disks[Disk].Timer = NULL;
// Check if the motor is already on
if( gaFDD_Disks[Disk].MotorState == MOTOR_ATSPEED )
{
if( gaFDD_Disks[Disk].MotorState != MOTOR_ATSPEED )
return 0;
- if( gaFDD_Disks[Disk].Timer != NULL )
- return 0;
- gaFDD_Disks[Disk].Timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(tVAddr)Disk);
+ Time_ScheduleTimer(gaFDD_Disks[Disk].Timer, MOTOR_OFF_DELAY);
return 0;
}
int _disk;
Uint16 base = FDD_int_GetBase(Disk, &_disk);
- gaFDD_Disks[Disk].Timer = NULL;
gaFDD_Disks[Disk].MotorState = MOTOR_OFF;
outb(base + FDC_DOR, inb(base+FDC_DOR) & ~(1 << (_disk + 4)));
*/
#define DEBUG 1
#include <usb_hub.h>
+#include <timers.h>
#define MAX_PORTS 32 // Not actually a max, but used for DeviceRemovable
#include <modules.h>
#include <usb_host.h>
#include "uhci.h"
+#include <timers.h>
// === CONSTANTS ===
#define MAX_CONTROLLERS 4