Skip to content

Commit

Permalink
Add PIT implementation for timer interrupt
Browse files Browse the repository at this point in the history
 Details:
  * 8242/8254(PIT) chip programming
  * <NOTE> Although pit is supported, it is unenabled without definition of
            macro: TIMER_INT_SCHED defaultly.
           There are some bugs not solved while enabling pit.
           So it uses auto-yield policy to schedule tasks currently.
  • Loading branch information
chobits committed Dec 30, 2011
1 parent 4f96c7f commit e7f42dc
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 9 deletions.
62 changes: 62 additions & 0 deletions include/pit.h
@@ -0,0 +1,62 @@
/*
* Programmbale Interval Timer(PIT) chip (8253/8254 chip)
*/
#ifndef __PIT_H
#define __PIT_H

#define PIT_CH0 0x40 /* Channle 0 data port(read/write) */
#define PIT_CH1 0x41 /* Channle 1 data port(read/write) */
#define PIT_CH2 0x42 /* Channle 2 data port(read/write) */
#define PIT_MODE 0x43 /* Mode/Command register(write only, a read is ignored) */

/*
* The Mode/Command register at I/O address 0x43 contains the following:
* Bits Usage
* 6, 7 Select channel :
* (7 6)
* 0 0 = Channel 0
* 0 1 = Channel 1
* 1 0 = Channel 2
* 1 1 = Read-back command (8254 only)
* 4, 5 Access mode :
* (5 4)
* 0 0 = Latch count value command
* 0 1 = Access mode: lobyte only
* 1 0 = Access mode: hibyte only
* 1 1 = Access mode: lobyte/hibyte
* 1, 2, 3 Operating mode :
* (3 2 1)
* 0 0 0 = Mode 0 (interrupt on terminal count)
* 0 0 1 = Mode 1 (hardware re-triggerable one-shot)
* 0 1 0 = Mode 2 (rate generator)
* 0 1 1 = Mode 3 (square wave generator)
* 1 0 0 = Mode 4 (software triggered strobe)
* 1 0 1 = Mode 5 (hardware triggered strobe)
* 1 1 0 = Mode 2 (rate generator, same as 010b)
* 1 1 1 = Mode 3 (square wave generator, same as 011b)
* 0 BCD/Binary mode: 0 = 16-bit binary, 1 = four-digit BCD
*/
#define PIT_MODE_CH0 0x00
#define PIT_MODE_CH1 0x40
#define PIT_MODE_CH2 0x80
#define PIT_MODE_RB 0xc0

#define PIT_MODE_LATCH 0x00
#define PIT_MODE_LOW 0x10
#define PIT_MODE_HIGH 0x20
#define PIT_MODE_LOHI 0x30

#define PIT_MODE_0 0x00
#define PIT_MODE_1 0x02
#define PIT_MODE_2 0x04
#define PIT_MODE_3 0x06
#define PIT_MODE_4 0x08
#define PIT_MODE_5 0x09
#define PIT_MODE_6 0x0a
#define PIT_MODE_7 0x0e

#define TIMER_FREQ 1000 /* timer interrupt frequence: 100 times/sec */
#define PIT_FREQ 1193182 /* 8253 chip frequence: 1.193181666MHz */
#define FREQ_DIV (PIT_FREQ / TIMER_FREQ)

#endif /* pit.h */
2 changes: 1 addition & 1 deletion kernel/Makefile
@@ -1,7 +1,7 @@
SUBDIR = kernel

OBJS = entry.o init.o interrupt.o int.o print.o string.o task.o execute.o\
syscall.o fork.o schedule.o exit.o wait_queue.o
syscall.o fork.o schedule.o exit.o wait_queue.o pit.o timer.o

kernel.o:$(OBJS)
$(Q)$(LD) -r $^ -o $@
Expand Down
4 changes: 3 additions & 1 deletion kernel/exit.c
Expand Up @@ -70,7 +70,9 @@ void sys_exit(int status)
task_exit(ctask);
/* No return */
schedule();
panic("task exiting fails");
if (ctask)
printk("%d ", ctask->pid);
panic("task exiting fails: %d");
}

void task_wait_exit(struct task *parent, struct task *task)
Expand Down
2 changes: 2 additions & 0 deletions kernel/fork.c
Expand Up @@ -103,6 +103,8 @@ void clone_task_context(struct regs *preg, struct task *child)
struct regs *creg = (struct regs *)child->kstacktop - 1;
*creg = *preg;
creg->eax = 0; /* Child returns 0 out of fork(). */
if (!(creg->eflags & EFLAGS_IF))
panic("has no IF flag");
child->con.esp = (unsigned int)creg;
child->con.eip = (unsigned int)fork_child_return;
}
Expand Down
15 changes: 12 additions & 3 deletions kernel/init.c
Expand Up @@ -9,6 +9,7 @@ void int_init(void);
void block_init(void);
void task_init(void);
void fs_init(void);
void pit_init(void);
void keyboard_init(void);
void load_first_program(char *, int, char **);

Expand All @@ -17,14 +18,19 @@ void kidle(void)
printk("Kernel is idle now\n\n");
/* Open interrupt */
sti();
while (1)
while (1) {
#ifdef TIMER_INT_SCHED
hlt();
#else
schedule();
#endif
}
}

void load_init(void)
{
char *argv[] = { "hello", "world", NULL };
load_first_program("/init", 2, argv);
char *argv[] = { "/init", NULL };
load_first_program("/init", 1, argv);
}

void init(void)
Expand All @@ -40,6 +46,9 @@ void init(void)
block_init();
fs_init();
keyboard_init();
#ifdef TIMER_INT_SCHED
pit_init();
#endif
load_init();
kidle();
}
18 changes: 15 additions & 3 deletions kernel/int.c
Expand Up @@ -55,6 +55,7 @@ void unmask_8259A(int irq)
extern void do_page_fault(struct regs *);
extern void do_sys_call(struct regs *);
extern void keyboard_interrupt(struct regs *);
extern void timer_interrupt(struct regs *);

/* All traps, faults, fatals and hardware interrupt handler */
void interrupt_handler(int nr, struct regs *reg)
Expand All @@ -64,7 +65,16 @@ void interrupt_handler(int nr, struct regs *reg)
panic("Unknown interrupt %d", nr);
}

/*
* IRQs are mutually exclusive.
* IRQ can preempt other traps and syscalls.
*/
switch (nr) {
#ifdef TIMER_INT_SCHED
case INTNO_IRQ0:
timer_interrupt(reg);
break;
#endif
case INTNO_IRQ1:
keyboard_interrupt(reg);
break;
Expand All @@ -82,10 +92,12 @@ void interrupt_handler(int nr, struct regs *reg)
break;
}

/* send end of interrupt signal to PIC chip (not autoeoi mode) */
/*
* send end of interrupt signal to PIC chip (not autoeoi mode)
* (If no EOI is sent, other hardware irq will not be accepted!)
*/
if (nr >= INTNO_IRQ0 && nr <= INTNO_IRQ15)
EOI_8259A(nr - INTNO_IRQ0);

}

static struct desc idt[IDT_SIZE];
Expand All @@ -111,7 +123,7 @@ void int_init(void)
set_trap_gate(INTNO_NP, np_entry);
set_trap_gate(INTNO_SS, ss_entry);
set_trap_gate(INTNO_GP, gp_entry);
set_trap_gate(INTNO_PF, pf_entry);
set_int_gate(INTNO_PF, pf_entry); /* page fault cannot allow interrupt. */
/* Vector 15 iS reserved */
set_trap_gate(INTNO_MF, mf_entry);
set_trap_gate(INTNO_AC, ac_entry);
Expand Down
16 changes: 16 additions & 0 deletions kernel/pit.c
@@ -0,0 +1,16 @@
#include <print.h>
#include <8259A.h>
#include <pit.h>
#include <int.h>
#include <x86.h>

void pit_init(void)
{
/* channel 0, rate generator, low&high bytes */
outb(PIT_MODE, PIT_MODE_CH0 | PIT_MODE_2 | PIT_MODE_LOHI);
outb(PIT_CH0, FREQ_DIV & 255); /* low byte */
outb(PIT_CH0, FREQ_DIV >> 8); /* high byte */
unmask_8259A(IRQ_TIMER);
printk("pit init\n");
}

16 changes: 15 additions & 1 deletion kernel/schedule.c
Expand Up @@ -38,18 +38,32 @@ void task_switch(struct task *next)
: "memory");
}

void debug_task(void)
{
struct task *task;
for_each_task(task) {
if (task->state == TASK_RUNNABLE)
printk("[%d]", task->pid);
}
}

void schedule(void)
{
struct task *task, *next = NULL;

/* FIXME: add lock to solve race condition for task_list */
for_each_task(task) {
if (task == ctask || task->state != TASK_RUNNABLE)
continue;
if (!next || task->priority > next->priority)
next = task;
}

if (!next) {
if (ctask == &init_task)
if (ctask == &init_task) {
debug_task();
return;
}
next = &init_task;
}
next->priority--;
Expand Down
14 changes: 14 additions & 0 deletions kernel/timer.c
@@ -0,0 +1,14 @@
#include <print.h>
#include <task.h>
#include <pit.h>
#include <int.h>

unsigned int ticks = 0;

extern void task_switch(struct task *);
void timer_interrupt(struct regs *reg)
{
ticks++;
schedule();
}

0 comments on commit e7f42dc

Please sign in to comment.