linux源代码剖析之kernel

kernel

asm.s
/*

  • asm.s contains the low-level code for most hardware faults.
  • page_exception is handled by the mm, so that isn’t here. This
  • file also handles (hopefully) fpu-exceptions due to TS-bit, as
  • the fpu must be properly saved/resored. This hasn’t been tested.
    */

.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op
.globl _device_not_available,_double_fault,_coprocessor_segment_overrun
.globl _invalid_TSS,_segment_not_present,_stack_segment
.globl _general_protection,_coprocessor_error,_reserved

_divide_error:
pushl $_do_divide_error
no_error_code:
xchgl %eax,(%esp)
pushl %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
pushl $0 # “error code”
lea 44(%esp),%edx
pushl %edx
movl $0x10,%edx
mov %dx,%ds
mov %dx,%es
mov %dx,%fs
call *%eax
addl $8,%esp
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
iret

_debug:
pushl $_do_int3 # _do_debug
jmp no_error_code

_nmi:
pushl $_do_nmi
jmp no_error_code

_int3:
pushl $_do_int3
jmp no_error_code

_overflow:
pushl $_do_overflow
jmp no_error_code

_bounds:
pushl $_do_bounds
jmp no_error_code

_invalid_op:
pushl $_do_invalid_op
jmp no_error_code

math_emulate:
popl %eax
pushl $_do_device_not_available
jmp no_error_code
_device_not_available:
pushl %eax
movl %cr0,%eax
bt $2,%eax # EM (math emulation bit)
jc math_emulate
clts # clear TS so that we can use math
movl _current,%eax
cmpl _last_task_used_math,%eax
je 1f # shouldn’t happen really …
pushl %ecx
pushl %edx
push %ds
movl $0x10,%eax
mov %ax,%ds
call _math_state_restore
pop %ds
popl %edx
popl %ecx
1: popl %eax
iret

_coprocessor_segment_overrun:
pushl $_do_coprocessor_segment_overrun
jmp no_error_code

_reserved:
pushl $_do_reserved
jmp no_error_code

_coprocessor_error:
pushl $_do_coprocessor_error
jmp no_error_code

_double_fault:
pushl $_do_double_fault
error_code:
xchgl %eax,4(%esp) # error code <-> %eax
xchgl %ebx,(%esp) # &function <-> %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
pushl %eax # error code
lea 44(%esp),%eax # offset
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
call *%ebx
addl $8,%esp
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
iret

_invalid_TSS:
pushl $_do_invalid_TSS
jmp error_code

_segment_not_present:
pushl $_do_segment_not_present
jmp error_code

_stack_segment:
pushl $_do_stack_segment
jmp error_code

_general_protection:
pushl $_do_general_protection
jmp error_code



console.c
/*

  • console.c
  • This module implements the console io functions
  • ‘void con_init(void)’
  • ‘void con_write(struct tty_queue * queue)’
  • Hopefully this will be a rather complete VT102 implementation.

*/

/*

  • NOTE!!! We sometimes disable and enable interrupts for a short while
  • (to put a word in video IO), but this will work even for keyboard
  • interrupts. We know interrupts aren’t enabled when getting a keyboard
  • interrupt, as we use trap-gates. Hopefully all is well.
    */

#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/io.h>
#include <asm/system.h>

#define SCREEN_START 0xb8000
#define SCREEN_END 0xc0000
#define LINES 25
#define COLUMNS 80
#define NPAR 16

extern void keyboard_interrupt(void);

static unsigned long origin=SCREEN_START;
static unsigned long scr_end=SCREEN_START+LINESCOLUMNS2;
static unsigned long pos;
static unsigned long x,y;
static unsigned long top=0,bottom=LINES;
static unsigned long lines=LINES,columns=COLUMNS;
static unsigned long state=0;
static unsigned long npar,par[NPAR];
static unsigned long ques=0;
static unsigned char attr=0x07;

/*

  • this is what the terminal answers to a ESC-Z or csi0c
  • query (= vt100 response).
    */
    #define RESPONSE “\033[?1;2c”

static inline void gotoxy(unsigned int new_x,unsigned int new_y)
{
if (new_x>=columns || new_y>=lines)
return;
x=new_x;
y=new_y;
pos=origin+((y*columns+x)<<1);
}

static inline void set_origin(void)
{
cli();
outb_p(12,0x3d4);
outb_p(0xff&((origin-SCREEN_START)>>9),0x3d5);
outb_p(13,0x3d4);
outb_p(0xff&((origin-SCREEN_START)>>1),0x3d5);
sti();
}

static void scrup(void)
{
if (!top && bottom==lines) {
origin += columns<<1;
pos += columns<<1;
scr_end += columns<<1;
if (scr_end>SCREEN_END) {
asm(“cld\n\t”
“rep\n\t”
“movsl\n\t”
“movl _columns,%1\n\t”
“rep\n\t”
“stosw”
::“a” (0x0720),
“c” ((lines-1)*columns>>1),
“D” (SCREEN_START),
“S” (origin)
:“cx”,“di”,“si”);
scr_end -= origin-SCREEN_START;
pos -= origin-SCREEN_START;
origin = SCREEN_START;
} else {
asm(“cld\n\t”
“rep\n\t”
“stosl”
::“a” (0x07200720),
“c” (columns>>1),
“D” (scr_end-(columns<<1))
:“cx”,“di”);
}
set_origin();
} else {
asm(“cld\n\t”
“rep\n\t”
“movsl\n\t”
“movl _columns,%%ecx\n\t”
“rep\n\t”
“stosw”
::“a” (0x0720),
“c” ((bottom-top-1)*columns>>1),
“D” (origin+(columns<<1)top),
“S” (origin+(columns<<1)
(top+1))
:“cx”,“di”,“si”);
}
}

static void scrdown(void)
{
asm(“std\n\t”
“rep\n\t”
“movsl\n\t”
“addl $2,%%edi\n\t” /* %edi has been decremented by 4 */
“movl _columns,%%ecx\n\t”
“rep\n\t”
“stosw”
::“a” (0x0720),
“c” ((bottom-top-1)*columns>>1),
“D” (origin+(columns<<1)bottom-4),
“S” (origin+(columns<<1)
(bottom-1)-4)
:“ax”,“cx”,“di”,“si”);
}

static void lf(void)
{
if (y+1<bottom) {
y++;
pos += columns<<1;
return;
}
scrup();
}

static void ri(void)
{
if (y>top) {
y–;
pos -= columns<<1;
return;
}
scrdown();
}

static void cr(void)
{
pos -= x<<1;
x=0;
}

static void del(void)
{
if (x) {
pos -= 2;
x–;
*(unsigned short *)pos = 0x0720;
}
}

static void csi_J(int par)
{
long count asm(“cx”);
long start asm(“di”);

switch (par) {
	case 0:	/* erase from cursor to end of display */
		count = (scr_end-pos)>>1;
		start = pos;
		break;
	case 1:	/* erase from start to cursor */
		count = (pos-origin)>>1;
		start = origin;
		break;
	case 2: /* erase whole display */
		count = columns*lines;
		start = origin;
		break;
	default:
		return;
}
__asm__("cld\n\t"
	"rep\n\t"
	"stosw\n\t"
	::"c" (count),
	"D" (start),"a" (0x0720)
	:"cx","di");

}

static void csi_K(int par)
{
long count asm(“cx”);
long start asm(“di”);

switch (par) {
	case 0:	/* erase from cursor to end of line */
		if (x>=columns)
			return;
		count = columns-x;
		start = pos;
		break;
	case 1:	/* erase from start of line to cursor */
		start = pos - (x<<1);
		count = (x<columns)?x:columns;
		break;
	case 2: /* erase whole line */
		start = pos - (x<<1);
		count = columns;
		break;
	default:
		return;
}
__asm__("cld\n\t"
	"rep\n\t"
	"stosw\n\t"
	::"c" (count),
	"D" (start),"a" (0x0720)
	:"cx","di");

}

void csi_m(void)
{
int i;

for (i=0;i<=npar;i++)
	switch (par[i]) {
		case 0:attr=0x07;break;
		case 1:attr=0x0f;break;
		case 4:attr=0x0f;break;
		case 7:attr=0x70;break;
		case 27:attr=0x07;break;
	}

}

static inline void set_cursor(void)
{
cli();
outb_p(14,0x3d4);
outb_p(0xff&((pos-SCREEN_START)>>9),0x3d5);
outb_p(15,0x3d4);
outb_p(0xff&((pos-SCREEN_START)>>1),0x3d5);
sti();
}

static void respond(struct tty_struct * tty)
{
char * p = RESPONSE;

cli();
while (*p) {
	PUTCH(*p,tty->read_q);
	p++;
}
sti();
copy_to_cooked(tty);

}

static void insert_char(void)
{
int i=x;
unsigned short tmp,old=0x0720;
unsigned short * p = (unsigned short *) pos;

while (i++<columns) {
	tmp=*p;
	*p=old;
	old=tmp;
	p++;
}

}

static void insert_line(void)
{
int oldtop,oldbottom;

oldtop=top;
oldbottom=bottom;
top=y;
bottom=lines;
scrdown();
top=oldtop;
bottom=oldbottom;

}

static void delete_char(void)
{
int i;
unsigned short * p = (unsigned short *) pos;

if (x>=columns)
	return;
i = x;
while (++i < columns) {
	*p = *(p+1);
	p++;
}
*p=0x0720;

}

static void delete_line(void)
{
int oldtop,oldbottom;

oldtop=top;
oldbottom=bottom;
top=y;
bottom=lines;
scrup();
top=oldtop;
bottom=oldbottom;

}

static void csi_at(int nr)
{
if (nr>columns)
nr=columns;
else if (!nr)
nr=1;
while (nr–)
insert_char();
}

static void csi_L(int nr)
{
if (nr>lines)
nr=lines;
else if (!nr)
nr=1;
while (nr–)
insert_line();
}

static void csi_P(int nr)
{
if (nr>columns)
nr=columns;
else if (!nr)
nr=1;
while (nr–)
delete_char();
}

static void csi_M(int nr)
{
if (nr>lines)
nr=lines;
else if (!nr)
nr=1;
while (nr–)
delete_line();
}

static int saved_x=0;
static int saved_y=0;

static void save_cur(void)
{
saved_x=x;
saved_y=y;
}

static void restore_cur(void)
{
x=saved_x;
y=saved_y;
pos=origin+((y*columns+x)<<1);
}

void con_write(struct tty_struct * tty)
{
int nr;
char c;

nr = CHARS(tty->write_q);
while (nr--) {
	GETCH(tty->write_q,c);
	switch(state) {
		case 0:
			if (c>31 && c<127) {
				if (x>=columns) {
					x -= columns;
					pos -= columns<<1;
					lf();
				}
				__asm__("movb _attr,%%ah\n\t"
					"movw %%ax,%1\n\t"
					::"a" (c),"m" (*(short *)pos)
					:"ax");
				pos += 2;
				x++;
			} else if (c==27)
				state=1;
			else if (c==10 || c==11 || c==12)
				lf();
			else if (c==13)
				cr();
			else if (c==ERASE_CHAR(tty))
				del();
			else if (c==8) {
				if (x) {
					x--;
					pos -= 2;
				}
			} else if (c==9) {
				c=8-(x&7);
				x += c;
				pos += c<<1;
				if (x>columns) {
					x -= columns;
					pos -= columns<<1;
					lf();
				}
				c=9;
			}
			break;
		case 1:
			state=0;
			if (c=='[')
				state=2;
			else if (c=='E')
				gotoxy(0,y+1);
			else if (c=='M')
				ri();
			else if (c=='D')
				lf();
			else if (c=='Z')
				respond(tty);
			else if (x=='7')
				save_cur();
			else if (x=='8')
				restore_cur();
			break;
		case 2:
			for(npar=0;npar<NPAR;npar++)
				par[npar]=0;
			npar=0;
			state=3;
			if (ques=(c=='?'))
				break;
		case 3:
			if (c==';' && npar<NPAR-1) {
				npar++;
				break;
			} else if (c>='0' && c<='9') {
				par[npar]=10*par[npar]+c-'0';
				break;
			} else state=4;
		case 4:
			state=0;
			switch(c) {
				case 'G': case '`':
					if (par[0]) par[0]--;
					gotoxy(par[0],y);
					break;
				case 'A':
					if (!par[0]) par[0]++;
					gotoxy(x,y-par[0]);
					break;
				case 'B': case 'e':
					if (!par[0]) par[0]++;
					gotoxy(x,y+par[0]);
					break;
				case 'C': case 'a':
					if (!par[0]) par[0]++;
					gotoxy(x+par[0],y);
					break;
				case 'D':
					if (!par[0]) par[0]++;
					gotoxy(x-par[0],y);
					break;
				case 'E':
					if (!par[0]) par[0]++;
					gotoxy(0,y+par[0]);
					break;
				case 'F':
					if (!par[0]) par[0]++;
					gotoxy(0,y-par[0]);
					break;
				case 'd':
					if (par[0]) par[0]--;
					gotoxy(x,par[0]);
					break;
				case 'H': case 'f':
					if (par[0]) par[0]--;
					if (par[1]) par[1]--;
					gotoxy(par[1],par[0]);
					break;
				case 'J':
					csi_J(par[0]);
					break;
				case 'K':
					csi_K(par[0]);
					break;
				case 'L':
					csi_L(par[0]);
					break;
				case 'M':
					csi_M(par[0]);
					break;
				case 'P':
					csi_P(par[0]);
					break;
				case '@':
					csi_at(par[0]);
					break;
				case 'm':
					csi_m();
					break;
				case 'r':
					if (par[0]) par[0]--;
					if (!par[1]) par[1]=lines;
					if (par[0] < par[1] &&
					    par[1] <= lines) {
						top=par[0];
						bottom=par[1];
					}
					break;
				case 's':
					save_cur();
					break;
				case 'u':
					restore_cur();
					break;
			}
	}
}
set_cursor();

}

/*

  • void con_init(void);

  • This routine initalizes console interrupts, and does nothing

  • else. If you want the screen to clear, call tty_write with

  • the appropriate escape-sequece.
    */
    void con_init(void)
    {
    register unsigned char a;

    gotoxy(*(unsigned char )(0x90000+510),(unsigned char *)(0x90000+511));
    set_trap_gate(0x21,&keyboard_interrupt);
    outb_p(inb_p(0x21)&0xfd,0x21);
    a=inb_p(0x61);
    outb_p(a|0x80,0x61);
    outb(a,0x61);
    }



exit.c
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <asm/segment.h>

int sys_pause(void);
int sys_close(int fd);

void release(struct task_struct * p)
{
int i;

if (!p)
	return;
for (i=1 ; i<NR_TASKS ; i++)
	if (task[i]==p) {
		task[i]=NULL;
		free_page((long)p);
		schedule();
		return;
	}
panic("trying to release non-existent task");

}

static inline void send_sig(long sig,struct task_struct * p,int priv)
{
if (!p || sig<1 || sig>32)
return;
if (priv ||
current->uidp->uid ||
current->euid
p->uid ||
current->uidp->euid ||
current->euid
p->euid)
p->signal |= (1<<(sig-1));
}

void do_kill(long pid,long sig,int priv)
{
struct task_struct **p = NR_TASKS + task;

if (!pid) while (--p > &FIRST_TASK) {
	if (*p && (*p)->pgrp == current->pid)
		send_sig(sig,*p,priv);
} else if (pid>0) while (--p > &FIRST_TASK) {
	if (*p && (*p)->pid == pid)
		send_sig(sig,*p,priv);
} else if (pid == -1) while (--p > &FIRST_TASK)
	send_sig(sig,*p,priv);
else while (--p > &FIRST_TASK)
	if (*p && (*p)->pgrp == -pid)
		send_sig(sig,*p,priv);

}

int sys_kill(int pid,int sig)
{
do_kill(pid,sig,!(current->uid || current->euid));
return 0;
}

int do_exit(long code)
{
int i;

free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
for (i=0 ; i<NR_TASKS ; i++)
	if (task[i] && task[i]->father == current->pid)
		task[i]->father = 0;
for (i=0 ; i<NR_OPEN ; i++)
	if (current->filp[i])
		sys_close(i);
iput(current->pwd);
current->pwd=NULL;
iput(current->root);
current->root=NULL;
if (current->leader && current->tty >= 0)
	tty_table[current->tty].pgrp = 0;
if (last_task_used_math == current)
	last_task_used_math = NULL;
if (current->father) {
	current->state = TASK_ZOMBIE;
	do_kill(current->father,SIGCHLD,1);
	current->exit_code = code;
} else
	release(current);
schedule();
return (-1);	/* just to suppress warnings */

}

int sys_exit(int error_code)
{
return do_exit((error_code&0xff)<<8);
}

int sys_waitpid(pid_t pid,int * stat_addr, int options)
{
int flag=0;
struct task_struct ** p;

verify_area(stat_addr,4);

repeat:
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p && *p != current &&
(pid==-1 || (*p)->pidpid ||
(pid
0 && (*p)->pgrpcurrent->pgrp) ||
(pid<0 && (*p)->pgrp
-pid)))
if ((*p)->father == current->pid) {
flag=1;
if ((*p)->state==TASK_ZOMBIE) {
put_fs_long((*p)->exit_code,
(unsigned long *) stat_addr);
current->cutime += (*p)->utime;
current->cstime += (*p)->stime;
flag = (*p)->pid;
release(*p);
return flag;
}
}
if (flag) {
if (options & WNOHANG)
return 0;
sys_pause();
if (!(current->signal &= ~(1<<(SIGCHLD-1))))
goto repeat;
else
return -EINTR;
}
return -ECHILD;
}



fork.c
/*

  • ‘fork.c’ contains the help-routines for the ‘fork’ system call
  • (see also system_call.s), and some misc functions (‘verify_area’).
  • Fork is rather simple, once you get the hang of it, but the memory
  • management can be a bitch. See ‘mm/mm.c’: ‘copy_page_tables()’
    */
    #include <errno.h>

#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <asm/system.h>

extern void write_verify(unsigned long address);

long last_pid=0;

void verify_area(void * addr,int size)
{
unsigned long start;

start = (unsigned long) addr;
size += start & 0xfff;
start &= 0xfffff000;
start += get_base(current->ldt[2]);
while (size>0) {
	size -= 4096;
	write_verify(start);
	start += 4096;
}

}

int copy_mem(int nr,struct task_struct * p)
{
unsigned long old_data_base,new_data_base,data_limit;
unsigned long old_code_base,new_code_base,code_limit;

code_limit=get_limit(0x0f);
data_limit=get_limit(0x17);
old_code_base = get_base(current->ldt[1]);
old_data_base = get_base(current->ldt[2]);
if (old_data_base != old_code_base)
	panic("We don't support separate I&D");
if (data_limit < code_limit)
	panic("Bad data_limit");
new_data_base = new_code_base = nr * 0x4000000;
set_base(p->ldt[1],new_code_base);
set_base(p->ldt[2],new_data_base);
if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
	free_page_tables(new_data_base,data_limit);
	return -ENOMEM;
}
return 0;

}

/*

  • Ok, this is the main fork-routine. It copies the system process

  • information (task[nr]) and sets up the necessary registers. It

  • also copies the data segment in it’s entirety.
    */
    int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
    long ebx,long ecx,long edx,
    long fs,long es,long ds,
    long eip,long cs,long eflags,long esp,long ss)
    {
    struct task_struct *p;
    int i;
    struct file *f;

    p = (struct task_struct *) get_free_page();
    if (!p)
    return -EAGAIN;
    *p = current; / NOTE! this doesn’t copy the supervisor stack /
    p->state = TASK_RUNNING;
    p->pid = last_pid;
    p->father = current->pid;
    p->counter = p->priority;
    p->signal = 0;
    p->alarm = 0;
    p->leader = 0; /
    process leadership doesn’t inherit /
    p->utime = p->stime = 0;
    p->cutime = p->cstime = 0;
    p->start_time = jiffies;
    p->tss.back_link = 0;
    p->tss.esp0 = PAGE_SIZE + (long) p;
    p->tss.ss0 = 0x10;
    p->tss.eip = eip;
    p->tss.eflags = eflags;
    p->tss.eax = 0;
    p->tss.ecx = ecx;
    p->tss.edx = edx;
    p->tss.ebx = ebx;
    p->tss.esp = esp;
    p->tss.ebp = ebp;
    p->tss.esi = esi;
    p->tss.edi = edi;
    p->tss.es = es & 0xffff;
    p->tss.cs = cs & 0xffff;
    p->tss.ss = ss & 0xffff;
    p->tss.ds = ds & 0xffff;
    p->tss.fs = fs & 0xffff;
    p->tss.gs = gs & 0xffff;
    p->tss.ldt = _LDT(nr);
    p->tss.trace_bitmap = 0x80000000;
    if (last_task_used_math == current)
    asm(“fnsave %0”::“m” (p->tss.i387));
    if (copy_mem(nr,p)) {
    free_page((long) p);
    return -EAGAIN;
    }
    for (i=0; i<NR_OPEN;i++)
    if (f=p->filp[i])
    f->f_count++;
    if (current->pwd)
    current->pwd->i_count++;
    if (current->root)
    current->root->i_count++;
    set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
    set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
    task[nr] = p; /
    do this last, just in case */
    return last_pid;
    }

int find_empty_process(void)
{
int i;

repeat:
	if ((++last_pid)<0) last_pid=1;
	for(i=0 ; i<NR_TASKS ; i++)
		if (task[i] && task[i]->pid == last_pid) goto repeat;
for(i=1 ; i<NR_TASKS ; i++)
	if (!task[i])
		return i;
return -EAGAIN;

}



hd.c
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>

/*

  • This code handles all hd-interrupts, and read/write requests to
  • the hard-disk. It is relatively straigthforward (not obvious maybe,
  • but interrupts never are), while still being efficient, and never
  • disabling interrupts (except to overcome possible race-condition).
  • The elevator block-seek algorithm doesn’t need to disable interrupts
  • due to clever programming.
    */

/* Max read/write errors/sector */
#define MAX_ERRORS 5
#define MAX_HD 2
#define NR_REQUEST 32

/*

  • This struct defines the HD’s and their types.
  • Currently defined for CP3044’s, ie a modified
  • type 17.
    */
    static struct hd_i_struct{
    int head,sect,cyl,wpcom,lzone,ctl;
    } hd_info[]= { HD_TYPE };

#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))

static struct hd_struct {
long start_sect;
long nr_sects;
} hd[5*MAX_HD]={{0,0},};

static struct hd_request {
int hd; /* -1 if no request */
int nsector;
int sector;
int head;
int cyl;
int cmd;
int errors;
struct buffer_head * bh;
struct hd_request * next;
} request[NR_REQUEST];

#define IN_ORDER(s1,s2)
((s1)->hd<(s2)->hd || (s1)->hd==(s2)->hd &&
((s1)->cyl<(s2)->cyl || (s1)->cyl==(s2)->cyl &&
((s1)->head<(s2)->head || (s1)->head==(s2)->head &&
((s1)->sector<(s2)->sector))))

static struct hd_request * this_request = NULL;

static int sorting=0;

static void do_request(void);
static void reset_controller(void);
static void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head,
unsigned int cyl,struct buffer_head * bh);
void hd_init(void);

#define port_read(port,buf,nr)
asm(“cld;rep;insw”::“d” (port),“D” (buf),“c” (nr):“cx”,“di”)

#define port_write(port,buf,nr)
asm(“cld;rep;outsw”::“d” (port),“S” (buf),“c” (nr):“cx”,“si”)

extern void hd_interrupt(void);

static struct task_struct * wait_for_request=NULL;

static inline void lock_buffer(struct buffer_head * bh)
{
if (bh->b_lock)
printk(“hd.c: buffer multiply locked\n”);
bh->b_lock=1;
}

static inline void unlock_buffer(struct buffer_head * bh)
{
if (!bh->b_lock)
printk(“hd.c: free buffer being unlocked\n”);
bh->b_lock=0;
wake_up(&bh->b_wait);
}

static inline void wait_on_buffer(struct buffer_head * bh)
{
cli();
while (bh->b_lock)
sleep_on(&bh->b_wait);
sti();
}

void rw_hd(int rw, struct buffer_head * bh)
{
unsigned int block,dev;
unsigned int sec,head,cyl;

block = bh->b_blocknr << 1;
dev = MINOR(bh->b_dev);
if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects)
	return;
block += hd[dev].start_sect;
dev /= 5;
__asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),
	"r" (hd_info[dev].sect));
__asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
	"r" (hd_info[dev].head));
rw_abs_hd(rw,dev,sec+1,head,cyl,bh);

}

/* This may be used only once, enforced by ‘static int callable’ */
int sys_setup(void)
{
static int callable = 1;
int i,drive;
struct partition *p;

if (!callable)
	return -1;
callable = 0;
for (drive=0 ; drive<NR_HD ; drive++) {
	rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer);
	if (!start_buffer->b_uptodate) {
		printk("Unable to read partition table of drive %d\n\r",
			drive);
		panic("");
	}
	if (start_buffer->b_data[510] != 0x55 || (unsigned char)
	    start_buffer->b_data[511] != 0xAA) {
		printk("Bad partition table on drive %d\n\r",drive);
		panic("");
	}
	p = 0x1BE + (void *)start_buffer->b_data;
	for (i=1;i<5;i++,p++) {
		hd[i+5*drive].start_sect = p->start_sect;
		hd[i+5*drive].nr_sects = p->nr_sects;
	}
}
printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
mount_root();
return (0);

}

/*

  • This is the pointer to a routine to be executed at every hd-interrupt.
  • Interesting way of doing things, but should be rather practical.
    */
    void (*do_hd)(void) = NULL;

static int controller_ready(void)
{
int retries=1000;

while (--retries && (inb(HD_STATUS)&0xc0)!=0x40);
return (retries);

}

static int win_result(void)
{
int i=inb(HD_STATUS);

if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
	== (READY_STAT | SEEK_STAT))
	return(0); /* ok */
if (i&1) i=inb(HD_ERROR);
return (1);

}

static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
unsigned int head,unsigned int cyl,unsigned int cmd,
void (*intr_addr)(void))
{
register int port asm(“dx”);

if (drive>1 || head>15)
	panic("Trying to write bad sector");
if (!controller_ready())
	panic("HD controller not ready");
do_hd = intr_addr;
outb(_CTL,HD_CMD);
port=HD_DATA;
outb_p(_WPCOM,++port);
outb_p(nsect,++port);
outb_p(sect,++port);
outb_p(cyl,++port);
outb_p(cyl>>8,++port);
outb_p(0xA0|(drive<<4)|head,++port);
outb(cmd,++port);

}

static int drive_busy(void)
{
unsigned int i;

for (i = 0; i < 100000; i++)
	if (READY_STAT == (inb(HD_STATUS) & (BUSY_STAT | READY_STAT)))
		break;
i = inb(HD_STATUS);
i &= BUSY_STAT | READY_STAT | SEEK_STAT;
if (i == READY_STAT | SEEK_STAT)
	return(0);
printk("HD controller times out\n\r");
return(1);

}

static void reset_controller(void)
{
int i;

outb(4,HD_CMD);
for(i = 0; i < 1000; i++) nop();
outb(0,HD_CMD);
for(i = 0; i < 10000 && drive_busy(); i++) /* nothing */;
if (drive_busy())
	printk("HD-controller still busy\n\r");
if((i = inb(ERR_STAT)) != 1)
	printk("HD-controller reset failed: %02x\n\r",i);

}

static void reset_hd(int nr)
{
reset_controller();
hd_out(nr,_SECT,_SECT,_HEAD-1,_CYL,WIN_SPECIFY,&do_request);
}

void unexpected_hd_interrupt(void)
{
panic(“Unexpected HD interrupt\n\r”);
}

static void bad_rw_intr(void)
{
int i = this_request->hd;

if (this_request->errors++ >= MAX_ERRORS) {
	this_request->bh->b_uptodate = 0;
	unlock_buffer(this_request->bh);
	wake_up(&wait_for_request);
	this_request->hd = -1;
	this_request=this_request->next;
}
reset_hd(i);

}

static void read_intr(void)
{
if (win_result()) {
bad_rw_intr();
return;
}
port_read(HD_DATA,this_request->bh->b_data+
512*(this_request->nsector&1),256);
this_request->errors = 0;
if (–this_request->nsector)
return;
this_request->bh->b_uptodate = 1;
this_request->bh->b_dirt = 0;
wake_up(&wait_for_request);
unlock_buffer(this_request->bh);
this_request->hd = -1;
this_request=this_request->next;
do_request();
}

static void write_intr(void)
{
if (win_result()) {
bad_rw_intr();
return;
}
if (–this_request->nsector) {
port_write(HD_DATA,this_request->bh->b_data+512,256);
return;
}
this_request->bh->b_uptodate = 1;
this_request->bh->b_dirt = 0;
wake_up(&wait_for_request);
unlock_buffer(this_request->bh);
this_request->hd = -1;
this_request=this_request->next;
do_request();
}

static void do_request(void)
{
int i,r;

if (sorting)
	return;
if (!this_request) {
	do_hd=NULL;
	return;
}
if (this_request->cmd == WIN_WRITE) {
	hd_out(this_request->hd,this_request->nsector,this_request->
		sector,this_request->head,this_request->cyl,
		this_request->cmd,&write_intr);
	for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
		/* nothing */ ;
	if (!r) {
		reset_hd(this_request->hd);
		return;
	}
	port_write(HD_DATA,this_request->bh->b_data+
		512*(this_request->nsector&1),256);
} else if (this_request->cmd == WIN_READ) {
	hd_out(this_request->hd,this_request->nsector,this_request->
		sector,this_request->head,this_request->cyl,
		this_request->cmd,&read_intr);
} else
	panic("unknown hd-command");

}

/*

  • add-request adds a request to the linked list.

  • It sets the ‘sorting’-variable when doing something

  • that interrupts shouldn’t touch.
    */
    static void add_request(struct hd_request * req)
    {
    struct hd_request * tmp;

    if (req->nsector != 2)
    panic(“nsector!=2 not implemented”);
    /*

  • Not to mess up the linked lists, we never touch the two first

  • entries (not this_request, as it is used by current interrups,

  • and not this_request->next, as it can be assigned to this_request).

  • This is not too high a price to pay for the ability of not

  • disabling interrupts.
    /
    sorting=1;
    if (!(tmp=this_request))
    this_request=req;
    else {
    if (!(tmp->next))
    tmp->next=req;
    else {
    tmp=tmp->next;
    for ( ; tmp->next ; tmp=tmp->next)
    if ((IN_ORDER(tmp,req) ||
    !IN_ORDER(tmp,tmp->next)) &&
    IN_ORDER(req,tmp->next))
    break;
    req->next=tmp->next;
    tmp->next=req;
    }
    }
    sorting=0;
    /

  • NOTE! As a result of sorting, the interrupts may have died down,

  • as they aren’t redone due to locking with sorting=1. They might

  • also never have started, if this is the first request in the queue,

  • so we restart them if necessary.
    */
    if (!do_hd)
    do_request();
    }

void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head,
unsigned int cyl,struct buffer_head * bh)
{
struct hd_request * req;

if (rw!=READ && rw!=WRITE)
	panic("Bad hd command, must be R/W");
lock_buffer(bh);

repeat:
for (req=0+request ; req<NR_REQUEST+request ; req++)
if (req->hd<0)
break;
if (reqNR_REQUEST+request) {
sleep_on(&wait_for_request);
goto repeat;
}
req->hd=nr;
req->nsector=2;
req->sector=sec;
req->head=head;
req->cyl=cyl;
req->cmd = ((rw
READ)?WIN_READ:WIN_WRITE);
req->bh=bh;
req->errors=0;
req->next=NULL;
add_request(req);
wait_on_buffer(bh);
}

void hd_init(void)
{
int i;

for (i=0 ; i<NR_REQUEST ; i++) {
	request[i].hd = -1;
	request[i].next = NULL;
}
for (i=0 ; i<NR_HD ; i++) {
	hd[i*5].start_sect = 0;
	hd[i*5].nr_sects = hd_info[i].head*
			hd_info[i].sect*hd_info[i].cyl;
}
set_trap_gate(0x2E,&hd_interrupt);
outb_p(inb_p(0x21)&0xfb,0x21);
outb(inb_p(0xA1)&0xbf,0xA1);

}



keyboard.s
/*

  • keyboard.s
    */

.text
.globl _keyboard_interrupt

/*

  • these are for the keyboard read functions
    /
    size = 1024 /
    must be a power of two ! And MUST be the same
    as in tty_io.c !!! */
    head = 4
    tail = 8
    proc_list = 12
    buf = 16

mode: .byte 0 /* caps, alt, ctrl and shift mode /
leds: .byte 2 /
num-lock, caps, scroll-lock mode (nom-lock on) */
e0: .byte 0

/*

  • con_int is the real interrupt routine that reads the
  • keyboard scan-code and converts it into the appropriate
  • ascii character(s).
    /
    _keyboard_interrupt:
    pushl %eax
    pushl %ebx
    pushl %ecx
    pushl %edx
    push %ds
    push %es
    movl $0x10,%eax
    mov %ax,%ds
    mov %ax,%es
    xorl %al,%al /
    %eax is scan code */
    inb $0x60,%al
    cmpb $0xe0,%al
    je set_e0
    cmpb $0xe1,%al
    je set_e1
    call key_table(,%eax,4)
    movb $0,e0
    e0_e1: inb $0x61,%al
    jmp 1f
    1: jmp 1f
    1: orb $0x80,%al
    jmp 1f
    1: jmp 1f
    1: outb %al,$0x61
    jmp 1f
    1: jmp 1f
    1: andb $0x7F,%al
    outb %al,$0x61
    movb $0x20,%al
    outb %al,$0x20
    pushl $0
    call _do_tty_interrupt
    addl $4,%esp
    pop %es
    pop %ds
    popl %edx
    popl %ecx
    popl %ebx
    popl %eax
    iret
    set_e0: movb $1,e0
    jmp e0_e1
    set_e1: movb $2,e0
    jmp e0_e1

/*

  • This routine fills the buffer with max 8 bytes, taken from
  • %ebx:%eax. (%edx is high). The bytes are written in the
  • order %al,%ah,%eal,%eah,%bl,%bh … until %eax is zero.
    */
    put_queue:
    pushl %ecx
    pushl %edx
    movl _table_list,%edx # read-queue for console
    movl head(%edx),%ecx
    1: movb %al,buf(%edx,%ecx)
    incl %ecx
    andl $size-1,%ecx
    cmpl tail(%edx),%ecx # buffer full - discard everything
    je 3f
    shrdl $8,%ebx,%eax
    je 2f
    shrl $8,%ebx
    jmp 1b
    2: movl %ecx,head(%edx)
    movl proc_list(%edx),%ecx
    testl %ecx,%ecx
    je 3f
    movl $0,(%ecx)
    3: popl %edx
    popl %ecx
    ret

ctrl: movb $0x04,%al
jmp 1f
alt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: orb %al,mode
ret
unctrl: movb $0x04,%al
jmp 1f
unalt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: notb %al
andb %al,mode
ret

lshift:
orb $0x01,mode
ret
unlshift:
andb $0xfe,mode
ret
rshift:
orb $0x02,mode
ret
unrshift:
andb $0xfd,mode
ret

caps: testb $0x80,mode
jne 1f
xorb $4,leds
xorb $0x40,mode
orb $0x80,mode
set_leds:
call kb_wait
movb $0xed,%al /* set leds command */
outb %al,$0x60
call kb_wait
movb leds,%al
outb %al,$0x60
ret
uncaps: andb $0x7f,mode
ret
scroll:
xorb $1,leds
jmp set_leds
num: xorb $2,leds
jmp set_leds

/*

  • curosr-key/numeric keypad cursor keys are handled here.
  • checking for numeric keypad etc.
    /
    cursor:
    subb $0x47,%al
    jb 1f
    cmpb $12,%al
    ja 1f
    jne cur2 /
    check for ctrl-alt-del /
    testb $0x0c,mode
    je cur2
    testb $0x30,mode
    jne reboot
    cur2: cmpb $0x01,e0 /
    e0 forces cursor movement /
    je cur
    testb $0x02,leds /
    not num-lock forces cursor /
    je cur
    testb $0x03,mode /
    shift forces cursor */
    jne cur
    xorl %ebx,%ebx
    movb num_table(%eax),%al
    jmp put_queue
    1: ret

cur: movb cur_table(%eax),%al
cmpb $‘9,%al
ja ok_cur
movb $’~,%ah
ok_cur: shll $16,%eax
movw $0x5b1b,%ax
xorl %ebx,%ebx
jmp put_queue

num_table:
.ascii “789 456 1230,”
cur_table:
.ascii “HA5 DGC YB623”

/*

  • this routine handles function keys
    /
    func:
    subb $0x3B,%al
    jb end_func
    cmpb $9,%al
    jbe ok_func
    subb $18,%al
    cmpb $10,%al
    jb end_func
    cmpb $11,%al
    ja end_func
    ok_func:
    cmpl $4,%ecx /
    check that there is enough room */
    jl end_func
    movl func_table(,%eax,4),%eax
    xorl %ebx,%ebx
    jmp put_queue
    end_func:
    ret

/*

  • function keys send F1:‘esc [ [ A’ F2:‘esc [ [ B’ etc.
    */
    func_table:
    .long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
    .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
    .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b

key_map:
.byte 0,27
.ascii “1234567890+’”
.byte 127,9
.ascii “qwertyuiop}”
.byte 0,10,0
.ascii “asdfghjkl|{”
.byte 0,0
.ascii “'zxcvbnm,.-”
.byte 0,’,0,32 / 36-39 /
.fill 16,1,0 /
3A-49 /
.byte ‘-,0,0,0,’+ /
4A-4E /
.byte 0,0,0,0,0,0,0 /
4F-55 */
.byte '<
.fill 10,1,0

shift_map:
.byte 0,27
.ascii “!”#$%&/()=?`"
.byte 127,9
.ascii “QWERTYUIOP]^”
.byte 10,0
.ascii “ASDFGHJKL\[”
.byte 0,0
.ascii "ZXCVBNM;:_"
.byte 0,’
,0,32 /* 36-39 /
.fill 16,1,0 /
3A-49 /
.byte ‘-,0,0,0,’+ /
4A-4E /
.byte 0,0,0,0,0,0,0 /
4F-55 */
.byte '>
.fill 10,1,0

alt_map:
.byte 0,0
.ascii “\0@\0$\0\0{[]}\\0”
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,10,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 /
.fill 16,1,0 /
3A-49 /
.byte 0,0,0,0,0 /
4A-4E /
.byte 0,0,0,0,0,0,0 /
4F-55 */
.byte '|
.fill 10,1,0

/*

  • do_self handles “normal” keys, ie keys that don’t change meaning
  • and which have just one character returns.
    /
    do_self:
    lea alt_map,%ebx
    testb $0x20,mode /
    alt-gr /
    jne 1f
    lea shift_map,%ebx
    testb $0x03,mode
    jne 1f
    lea key_map,%ebx
    1: movb (%ebx,%eax),%al
    orb %al,%al
    je none
    testb $0x4c,mode /
    ctrl or caps /
    je 2f
    cmpb $'a,%al
    jb 2f
    cmpb $'z,%al
    ja 2f
    subb $32,%al
    2: testb $0x0c,mode /
    ctrl /
    je 3f
    cmpb $64,%al
    jb 3f
    cmpb $64+32,%al
    jae 3f
    subb $64,%al
    3: testb $0x10,mode /
    left alt */
    je 4f
    orb $0x80,%al
    4: andl $0xff,%eax
    xorl %ebx,%ebx
    call put_queue
    none: ret

/*

  • minus has a routine of it’s own, as a ‘E0h’ before
  • the scan code for minus means that the numeric keypad
  • slash was pushed.
    */
    minus: cmpb $1,e0
    jne do_self
    movl $’/,%eax
    xorl %ebx,%ebx
    jmp put_queue

/*

  • This table decides which routine to call when a scan-code has been
  • gotten. Most routines just call do_self, or none, depending if
  • they are make or break.
    /
    key_table:
    .long none,do_self,do_self,do_self /
    00-03 s0 esc 1 2 /
    .long do_self,do_self,do_self,do_self /
    04-07 3 4 5 6 /
    .long do_self,do_self,do_self,do_self /
    08-0B 7 8 9 0 /
    .long do_self,do_self,do_self,do_self /
    0C-0F + ’ bs tab /
    .long do_self,do_self,do_self,do_self /
    10-13 q w e r /
    .long do_self,do_self,do_self,do_self /
    14-17 t y u i /
    .long do_self,do_self,do_self,do_self /
    18-1B o p } ^ /
    .long do_self,ctrl,do_self,do_self /
    1C-1F enter ctrl a s /
    .long do_self,do_self,do_self,do_self /
    20-23 d f g h /
    .long do_self,do_self,do_self,do_self /
    24-27 j k l | /
    .long do_self,do_self,lshift,do_self /
    28-2B { para lshift , /
    .long do_self,do_self,do_self,do_self /
    2C-2F z x c v /
    .long do_self,do_self,do_self,do_self /
    30-33 b n m , /
    .long do_self,minus,rshift,do_self /
    34-37 . - rshift * /
    .long alt,do_self,caps,func /
    38-3B alt sp caps f1 /
    .long func,func,func,func /
    3C-3F f2 f3 f4 f5 /
    .long func,func,func,func /
    40-43 f6 f7 f8 f9 /
    .long func,num,scroll,cursor /
    44-47 f10 num scr home /
    .long cursor,cursor,do_self,cursor /
    48-4B up pgup - left /
    .long cursor,cursor,do_self,cursor /
    4C-4F n5 right + end /
    .long cursor,cursor,cursor,cursor /
    50-53 dn pgdn ins del /
    .long none,none,do_self,func /
    54-57 sysreq ? < f11 /
    .long func,none,none,none /
    58-5B f12 ? ? ? /
    .long none,none,none,none /
    5C-5F ? ? ? ? /
    .long none,none,none,none /
    60-63 ? ? ? ? /
    .long none,none,none,none /
    64-67 ? ? ? ? /
    .long none,none,none,none /
    68-6B ? ? ? ? /
    .long none,none,none,none /
    6C-6F ? ? ? ? /
    .long none,none,none,none /
    70-73 ? ? ? ? /
    .long none,none,none,none /
    74-77 ? ? ? ? /
    .long none,none,none,none /
    78-7B ? ? ? ? /
    .long none,none,none,none /
    7C-7F ? ? ? ? /
    .long none,none,none,none /
    80-83 ? br br br /
    .long none,none,none,none /
    84-87 br br br br /
    .long none,none,none,none /
    88-8B br br br br /
    .long none,none,none,none /
    8C-8F br br br br /
    .long none,none,none,none /
    90-93 br br br br /
    .long none,none,none,none /
    94-97 br br br br /
    .long none,none,none,none /
    98-9B br br br br /
    .long none,unctrl,none,none /
    9C-9F br unctrl br br /
    .long none,none,none,none /
    A0-A3 br br br br /
    .long none,none,none,none /
    A4-A7 br br br br /
    .long none,none,unlshift,none /
    A8-AB br br unlshift br /
    .long none,none,none,none /
    AC-AF br br br br /
    .long none,none,none,none /
    B0-B3 br br br br /
    .long none,none,unrshift,none /
    B4-B7 br br unrshift br /
    .long unalt,none,uncaps,none /
    B8-BB unalt br uncaps br /
    .long none,none,none,none /
    BC-BF br br br br /
    .long none,none,none,none /
    C0-C3 br br br br /
    .long none,none,none,none /
    C4-C7 br br br br /
    .long none,none,none,none /
    C8-CB br br br br /
    .long none,none,none,none /
    CC-CF br br br br /
    .long none,none,none,none /
    D0-D3 br br br br /
    .long none,none,none,none /
    D4-D7 br br br br /
    .long none,none,none,none /
    D8-DB br ? ? ? /
    .long none,none,none,none /
    DC-DF ? ? ? ? /
    .long none,none,none,none /
    E0-E3 e0 e1 ? ? /
    .long none,none,none,none /
    E4-E7 ? ? ? ? /
    .long none,none,none,none /
    E8-EB ? ? ? ? /
    .long none,none,none,none /
    EC-EF ? ? ? ? /
    .long none,none,none,none /
    F0-F3 ? ? ? ? /
    .long none,none,none,none /
    F4-F7 ? ? ? ? /
    .long none,none,none,none /
    F8-FB ? ? ? ? /
    .long none,none,none,none /
    FC-FF ? ? ? ? */

/*

  • kb_wait waits for the keyboard controller buffer to empty.
  • there is no timeout - if the buffer doesn’t empty, we hang.
    /
    kb_wait:
    pushl %eax
    1: inb $0x64,%al
    testb $0x02,%al
    jne 1b
    popl %eax
    ret
    /
  • This routine reboots the machine by asking the keyboard
  • controller to pulse the reset-line low.
    /
    reboot:
    call kb_wait
    movw $0x1234,0x472 /
    don’t do memory check /
    movb $0xfc,%al /
    pulse reset and A20 low */
    outb %al,$0x64
    die: jmp die


Makefile

Makefile for the FREAX-kernel.

Note! Dependencies are done automagically by ‘make dep’, which also

removes any old dependencies. DON’T put your own dependencies here

unless it’s something special (ie not a .c file).

AR =gar
AS =gas
LD =gld
LDFLAGS =-s -x
CC =gcc
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs
-finline-functions -mstring-insns -nostdinc -I…/include
CPP =gcc -E -nostdinc -I…/include

.c.s:
$(CC) $(CFLAGS)
-S -o $.s $<
.s.o:
$(AS) -c -o $
.o $<
.c.o:
$(CC) $(CFLAGS)
-c -o $*.o $<

OBJS = sched.o system_call.o traps.o asm.o fork.o
panic.o printk.o vsprintf.o tty_io.o console.o
keyboard.o rs_io.o hd.o sys.o exit.o serial.o
mktime.o

kernel.o: $(OBJS)
$(LD) -r -o kernel.o $(OBJS)
sync

clean:
rm -f core *.o *.a tmp_make
for i in *.c;do rm -f basename $$i .c.s;done

dep:
sed ‘/### Dependencies/q’ < Makefile > tmp_make
(for i in *.c;do echo -n echo $$i | sed 's,\.c,\.s,'" ";
$(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile

Dependencies:

console.s console.o : console.c …/include/linux/sched.h …/include/linux/head.h
…/include/linux/fs.h …/include/sys/types.h …/include/linux/mm.h
…/include/linux/tty.h …/include/termios.h …/include/asm/io.h
…/include/asm/system.h
exit.s exit.o : exit.c …/include/errno.h …/include/signal.h
…/include/sys/types.h …/include/sys/wait.h …/include/linux/sched.h
…/include/linux/head.h …/include/linux/fs.h …/include/linux/mm.h
…/include/linux/kernel.h …/include/linux/tty.h …/include/termios.h
…/include/asm/segment.h
fork.s fork.o : fork.c …/include/errno.h …/include/linux/sched.h
…/include/linux/head.h …/include/linux/fs.h …/include/sys/types.h
…/include/linux/mm.h …/include/linux/kernel.h …/include/asm/segment.h
…/include/asm/system.h
hd.s hd.o : hd.c …/include/linux/config.h …/include/linux/sched.h
…/include/linux/head.h …/include/linux/fs.h …/include/sys/types.h
…/include/linux/mm.h …/include/linux/kernel.h …/include/linux/hdreg.h
…/include/asm/system.h …/include/asm/io.h …/include/asm/segment.h
mktime.s mktime.o : mktime.c …/include/time.h
panic.s panic.o : panic.c …/include/linux/kernel.h
printk.s printk.o : printk.c …/include/stdarg.h …/include/stddef.h
…/include/linux/kernel.h
sched.s sched.o : sched.c …/include/linux/sched.h …/include/linux/head.h
…/include/linux/fs.h …/include/sys/types.h …/include/linux/mm.h
…/include/linux/kernel.h …/include/signal.h …/include/linux/sys.h
…/include/asm/system.h …/include/asm/io.h …/include/asm/segment.h
serial.s serial.o : serial.c …/include/linux/tty.h …/include/termios.h
…/include/linux/sched.h …/include/linux/head.h …/include/linux/fs.h
…/include/sys/types.h …/include/linux/mm.h …/include/asm/system.h
…/include/asm/io.h
sys.s sys.o : sys.c …/include/errno.h …/include/linux/sched.h
…/include/linux/head.h …/include/linux/fs.h …/include/sys/types.h
…/include/linux/mm.h …/include/linux/tty.h …/include/termios.h
…/include/linux/kernel.h …/include/asm/segment.h …/include/sys/times.h
…/include/sys/utsname.h
traps.s traps.o : traps.c …/include/string.h …/include/linux/head.h
…/include/linux/sched.h …/include/linux/fs.h …/include/sys/types.h
…/include/linux/mm.h …/include/linux/kernel.h …/include/asm/system.h
…/include/asm/segment.h
tty_io.s tty_io.o : tty_io.c …/include/ctype.h …/include/errno.h
…/include/signal.h …/include/sys/types.h …/include/linux/sched.h
…/include/linux/head.h …/include/linux/fs.h …/include/linux/mm.h
…/include/linux/tty.h …/include/termios.h …/include/asm/segment.h
…/include/asm/system.h
vsprintf.s vsprintf.o : vsprintf.c …/include/stdarg.h …/include/string.h



mktime.c
#include <time.h>

/*

  • This isn’t the library routine, it is only used in the kernel.
  • as such, we don’t care about years<1970 etc, but assume everything
  • is ok. Similarly, TZ etc is happily ignored. We just do everything
  • as easily as possible. Let’s find something public for the library
  • routines (although I think minix times is public).
    /
    /
  • PS. I hate whoever though up the year 1970 - couldn’t they have gotten
  • a leap-year instead? I also hate Gregorius, pope or no. I’m grumpy.
    /
    #define MINUTE 60
    #define HOUR (60
    MINUTE)
    #define DAY (24HOUR)
    #define YEAR (365
    DAY)

/* interestingly, we assume leap-years /
static int month[12] = {
0,
DAY
(31),
DAY*(31+29),
DAY*(31+29+31),
DAY*(31+29+31+30),
DAY*(31+29+31+30+31),
DAY*(31+29+31+30+31+30),
DAY*(31+29+31+30+31+30+31),
DAY*(31+29+31+30+31+30+31+31),
DAY*(31+29+31+30+31+30+31+31+30),
DAY*(31+29+31+30+31+30+31+31+30+31),
DAY*(31+29+31+30+31+30+31+31+30+31+30)
};

long kernel_mktime(struct tm * tm)
{
long res;
int year;

year = tm->tm_year - 70;

/* magic offsets (y+1) needed to get leapyears right./
res = YEAR
year + DAY*((year+1)/4);
res += month[tm->tm_mon];
/* and (y+2) here. If it wasn’t a leap-year, we have to adjust /
if (tm->tm_mon>1 && ((year+2)%4))
res -= DAY;
res += DAY
(tm->tm_mday-1);
res += HOURtm->tm_hour;
res += MINUTE
tm->tm_min;
res += tm->tm_sec;
return res;
}



panic.c
/*

  • This function is used through-out the kernel (includeinh mm and fs)
  • to indicate a major problem.
    */
    #include <linux/kernel.h>

volatile void panic(const char * s)
{
printk(“Kernel panic: %s\n\r”,s);
for(;😉;
}



printk.c
/*

  • When in kernel-mode, we cannot use printf, as fs is liable to
  • point to ‘interesting’ things. Make a printf with fs-saving, and
  • all is well.
    */
    #include <stdarg.h>
    #include <stddef.h>

#include <linux/kernel.h>

static char buf[1024];

int printk(const char *fmt, …)
{
va_list args;
int i;

va_start(args, fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
__asm__("push %%fs\n\t"
	"push %%ds\n\t"
	"pop %%fs\n\t"
	"pushl %0\n\t"
	"pushl $_buf\n\t"
	"pushl $0\n\t"
	"call _tty_write\n\t"
	"addl $8,%%esp\n\t"
	"popl %0\n\t"
	"pop %%fs"
	::"r" (i):"ax","cx","dx");
return i;

}



rs_io.s
/*

  • rs_io.s
  • This module implements the rs232 io interrupts.
    */

.text
.globl _rs1_interrupt,_rs2_interrupt

size = 1024 /* must be power of two !
and must match the value
in tty_io.c!!! */

/* these are the offsets into the read/write buffer structures */
rs_addr = 0
head = 4
tail = 8
proc_list = 12
buf = 16

startup = 256 /* chars left in write queue when we restart it */

/*

  • These are the actual interrupt routines. They look where
  • the interrupt is coming from, and take appropriate action.
    /
    .align 2
    _rs1_interrupt:
    pushl $_table_list+8
    jmp rs_int
    .align 2
    _rs2_interrupt:
    pushl $_table_list+16
    rs_int:
    pushl %edx
    pushl %ecx
    pushl %ebx
    pushl %eax
    push %es
    push %ds /
    as this is an interrupt, we cannot /
    pushl $0x10 /
    know that bs is ok. Load it /
    pop %ds
    pushl $0x10
    pop %es
    movl 24(%esp),%edx
    movl (%edx),%edx
    movl rs_addr(%edx),%edx
    addl $2,%edx /
    interrupt ident. reg /
    rep_int:
    xorl %eax,%eax
    inb %dx,%al
    testb $1,%al
    jne end
    cmpb $6,%al /
    this shouldn’t happen, but … /
    ja end
    movl 24(%esp),%ecx
    pushl %edx
    subl $2,%edx
    call jmp_table(,%eax,2) /
    NOTE! not *4, bit0 is 0 already /
    popl %edx
    jmp rep_int
    end: movb $0x20,%al
    outb %al,$0x20 /
    EOI */
    pop %ds
    pop %es
    popl %eax
    popl %ebx
    popl %ecx
    popl %edx
    addl $4,%esp # jump over _table_list entry
    iret

jmp_table:
.long modem_status,write_char,read_char,line_status

.align 2
modem_status:
addl $6,%edx /* clear intr by reading modem status reg */
inb %dx,%al
ret

.align 2
line_status:
addl $5,%edx /* clear intr by reading line status reg. */
inb %dx,%al
ret

.align 2
read_char:
inb %dx,%al
movl %ecx,%edx
subl $_table_list,%edx
shrl $3,%edx
movl (%ecx),%ecx # read-queue
movl head(%ecx),%ebx
movb %al,buf(%ecx,%ebx)
incl %ebx
andl $size-1,%ebx
cmpl tail(%ecx),%ebx
je 1f
movl %ebx,head(%ecx)
pushl %edx
call _do_tty_interrupt
addl $4,%esp
1: ret

.align 2
write_char:
movl 4(%ecx),%ecx # write-queue
movl head(%ecx),%ebx
subl tail(%ecx),%ebx
andl $size-1,%ebx # nr chars in queue
je write_buffer_empty
cmpl $startup,%ebx
ja 1f
movl proc_list(%ecx),%ebx # wake up sleeping process
testl %ebx,%ebx # is there any?
je 1f
movl $0,(%ebx)
1: movl tail(%ecx),%ebx
movb buf(%ecx,%ebx),%al
outb %al,%dx
incl %ebx
andl $size-1,%ebx
movl %ebx,tail(%ecx)
cmpl head(%ecx),%ebx
je write_buffer_empty
ret
.align 2
write_buffer_empty:
movl proc_list(%ecx),%ebx # wake up sleeping process
testl %ebx,%ebx # is there any?
je 1f
movl $0,(%ebx)
1: incl %edx
inb %dx,%al
jmp 1f
1: jmp 1f
1: andb $0xd,%al /* disable transmit interrupt */
outb %al,%dx
ret



sched.c
/*

  • ‘sched.c’ is the main kernel file. It contains scheduling primitives
  • (sleep_on, wakeup, schedule etc) as well as a number of simple system
  • call functions (type getpid(), which just extracts a field from
  • current-task
    */
    #include <linux/sched.h>
    #include <linux/kernel.h>
    #include <signal.h>
    #include <linux/sys.h>
    #include <asm/system.h>
    #include <asm/io.h>
    #include <asm/segment.h>

#define LATCH (1193180/HZ)

extern void mem_use(void);

extern int timer_interrupt(void);
extern int system_call(void);

union task_union {
struct task_struct task;
char stack[PAGE_SIZE];
};

static union task_union init_task = {INIT_TASK,};

long volatile jiffies=0;
long startup_time=0;
struct task_struct *current = &(init_task.task), *last_task_used_math = NULL;

struct task_struct * task[NR_TASKS] = {&(init_task.task), };

long user_stack [ PAGE_SIZE>>2 ] ;

struct {
long * a;
short b;
} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
/*

  • ‘math_state_restore()’ saves the current math information in the
  • old math state array, and gets the new ones from the current task
    */
    void math_state_restore()
    {
    if (last_task_used_math)
    asm(“fnsave %0”::“m” (last_task_used_math->tss.i387));
    if (current->used_math)
    asm(“frstor %0”::“m” (current->tss.i387));
    else {
    asm(“fninit”:😃;
    current->used_math=1;
    }
    last_task_used_math=current;
    }

/*

  • ‘schedule()’ is the scheduler function. This is GOOD CODE! There
  • probably won’t be any reason to change this, as it should work well
  • in all circumstances (ie gives IO-bound processes good response etc).
  • The one thing you might take a look at is the signal-handler code here.
  • NOTE!! Task 0 is the ‘idle’ task, which gets called when no other
  • tasks can run. It can not be killed, and it cannot sleep. The ‘state’
  • information in task[0] is never used.
    */
    void schedule(void)
    {
    int i,next,c;
    struct task_struct ** p;

/* check alarm, wake up any interruptible tasks that have got a signal */

for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
	if (*p) {
		if ((*p)->alarm && (*p)->alarm < jiffies) {
				(*p)->signal |= (1<<(SIGALRM-1));
				(*p)->alarm = 0;
			}
		if ((*p)->signal && (*p)->state==TASK_INTERRUPTIBLE)
			(*p)->state=TASK_RUNNING;
	}

/* this is the scheduler proper: */

while (1) {
	c = -1;
	next = 0;
	i = NR_TASKS;
	p = &task[NR_TASKS];
	while (--i) {
		if (!*--p)
			continue;
		if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
			c = (*p)->counter, next = i;
	}
	if (c) break;
	for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
		if (*p)
			(*p)->counter = ((*p)->counter >> 1) +
					(*p)->priority;
}
switch_to(next);

}

int sys_pause(void)
{
current->state = TASK_INTERRUPTIBLE;
schedule();
return 0;
}

void sleep_on(struct task_struct **p)
{
struct task_struct *tmp;

if (!p)
	return;
if (current == &(init_task.task))
	panic("task[0] trying to sleep");
tmp = *p;
*p = current;
current->state = TASK_UNINTERRUPTIBLE;
schedule();
if (tmp)
	tmp->state=0;

}

void interruptible_sleep_on(struct task_struct **p)
{
struct task_struct *tmp;

if (!p)
	return;
if (current == &(init_task.task))
	panic("task[0] trying to sleep");
tmp=*p;
*p=current;

repeat: current->state = TASK_INTERRUPTIBLE;
schedule();
if (*p && *p != current) {
(**p).state=0;
goto repeat;
}
*p=NULL;
if (tmp)
tmp->state=0;
}

void wake_up(struct task_struct **p)
{
if (p && *p) {
(**p).state=0;
*p=NULL;
}
}

void do_timer(long cpl)
{
if (cpl)
current->utime++;
else
current->stime++;
if ((–current->counter)>0) return;
current->counter=0;
if (!cpl) return;
schedule();
}

int sys_alarm(long seconds)
{
current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
return seconds;
}

int sys_getpid(void)
{
return current->pid;
}

int sys_getppid(void)
{
return current->father;
}

int sys_getuid(void)
{
return current->uid;
}

int sys_geteuid(void)
{
return current->euid;
}

int sys_getgid(void)
{
return current->gid;
}

int sys_getegid(void)
{
return current->egid;
}

int sys_nice(long increment)
{
if (current->priority-increment>0)
current->priority -= increment;
return 0;
}

int sys_signal(long signal,long addr,long restorer)
{
long i;

switch (signal) {
	case SIGHUP: case SIGINT: case SIGQUIT: case SIGILL:
	case SIGTRAP: case SIGABRT: case SIGFPE: case SIGUSR1:
	case SIGSEGV: case SIGUSR2: case SIGPIPE: case SIGALRM:
	case SIGCHLD:
		i=(long) current->sig_fn[signal-1];
		current->sig_fn[signal-1] = (fn_ptr) addr;
		current->sig_restorer = (fn_ptr) restorer;
		return i;
	default: return -1;
}

}

void sched_init(void)
{
int i;
struct desc_struct * p;

set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
p = gdt+2+FIRST_TSS_ENTRY;
for(i=1;i<NR_TASKS;i++) {
	task[i] = NULL;
	p->a=p->b=0;
	p++;
	p->a=p->b=0;
	p++;
}
ltr(0);
lldt(0);
outb_p(0x36,0x43);		/* binary, mode 3, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40);	/* LSB */
outb(LATCH >> 8 , 0x40);	/* MSB */
set_intr_gate(0x20,&timer_interrupt);
outb(inb_p(0x21)&~0x01,0x21);
set_system_gate(0x80,&system_call);

}



serial.c
/*

  • serial.c
  • This module implements the rs232 io functions
  • void rs_write(struct tty_struct * queue);
  • void rs_init(void);
  • and all interrupts pertaining to serial IO.
    */

#include <linux/tty.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/io.h>

#define WAKEUP_CHARS (TTY_BUF_SIZE/4)

extern void rs1_interrupt(void);
extern void rs2_interrupt(void);

static void init(int port)
{
outb_p(0x80,port+3); /* set DLAB of line control reg /
outb_p(0x30,port); /
LS of divisor (48 -> 2400 bps /
outb_p(0x00,port+1); /
MS of divisor /
outb_p(0x03,port+3); /
reset DLAB /
outb_p(0x0b,port+4); /
set DTR,RTS, OUT_2 /
outb_p(0x0d,port+1); /
enable all intrs but writes /
(void)inb(port); /
read data port to reset things (?) */
}

void rs_init(void)
{
set_intr_gate(0x24,rs1_interrupt);
set_intr_gate(0x23,rs2_interrupt);
init(tty_table[1].read_q.data);
init(tty_table[2].read_q.data);
outb(inb_p(0x21)&0xE7,0x21);
}

/*

  • This routine gets called when tty_write has put something into
  • the write_queue. It must check wheter the queue is empty, and
  • set the interrupt register accordingly
  • void _rs_write(struct tty_struct * tty);
    */
    void rs_write(struct tty_struct * tty)
    {
    cli();
    if (!EMPTY(tty->write_q))
    outb(inb_p(tty->write_q.data+1)|0x02,tty->write_q.data+1);
    sti();
    }


sys.c
#include <errno.h>

#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <sys/times.h>
#include <sys/utsname.h>

int sys_ftime()
{
return -ENOSYS;
}

int sys_mknod()
{
return -ENOSYS;
}

int sys_break()
{
return -ENOSYS;
}

int sys_mount()
{
return -ENOSYS;
}

int sys_umount()
{
return -ENOSYS;
}

int sys_ustat(int dev,struct ustat * ubuf)
{
return -1;
}

int sys_ptrace()
{
return -ENOSYS;
}

int sys_stty()
{
return -ENOSYS;
}

int sys_gtty()
{
return -ENOSYS;
}

int sys_rename()
{
return -ENOSYS;
}

int sys_prof()
{
return -ENOSYS;
}

int sys_setgid(int gid)
{
if (current->euid && current->uid)
if (current->gidgid || current->sgidgid)
current->egid=gid;
else
return -EPERM;
else
current->gid=current->egid=gid;
return 0;
}

int sys_acct()
{
return -ENOSYS;
}

int sys_phys()
{
return -ENOSYS;
}

int sys_lock()
{
return -ENOSYS;
}

int sys_mpx()
{
return -ENOSYS;
}

int sys_ulimit()
{
return -ENOSYS;
}

int sys_time(long * tloc)
{
int i;

i = CURRENT_TIME;
if (tloc) {
	verify_area(tloc,4);
	put_fs_long(i,(unsigned long *)tloc);
}
return i;

}

int sys_setuid(int uid)
{
if (current->euid && current->uid)
if (uidcurrent->uid || current->suidcurrent->uid)
current->euid=uid;
else
return -EPERM;
else
current->euid=current->uid=uid;
return 0;
}

int sys_stime(long * tptr)
{
if (current->euid && current->uid)
return -1;
startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ;
return 0;
}

int sys_times(struct tms * tbuf)
{
if (!tbuf)
return jiffies;
verify_area(tbuf,sizeof *tbuf);
put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime);
put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime);
put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime);
put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime);
return jiffies;
}

int sys_brk(unsigned long end_data_seg)
{
if (end_data_seg >= current->end_code &&
end_data_seg < current->start_stack - 16384)
current->brk = end_data_seg;
return current->brk;
}

/*

  • This needs some heave checking …

  • I just haven’t get the stomach for it. I also don’t fully

  • understand sessions/pgrp etc. Let somebody who does explain it.
    */
    int sys_setpgid(int pid, int pgid)
    {
    int i;

    if (!pid)
    pid = current->pid;
    if (!pgid)
    pgid = pid;
    for (i=0 ; i<NR_TASKS ; i++)
    if (task[i] && task[i]->pid==pid) {
    if (task[i]->leader)
    return -EPERM;
    if (task[i]->session != current->session)
    return -EPERM;
    task[i]->pgrp = pgid;
    return 0;
    }
    return -ESRCH;
    }

int sys_getpgrp(void)
{
return current->pgrp;
}

int sys_setsid(void)
{
if (current->uid && current->euid)
return -EPERM;
if (current->leader)
return -EPERM;
current->leader = 1;
current->session = current->pgrp = current->pid;
current->tty = -1;
return current->pgrp;
}

int sys_uname(struct utsname * name)
{
static struct utsname thisname = {
“linux .0”,“nodename”,"release ","version ","machine "
};
int i;

if (!name) return -1;
verify_area(name,sizeof *name);
for(i=0;i<sizeof *name;i++)
	put_fs_byte(((char *) &thisname)[i],i+(char *) name);
return (0);

}

int sys_umask(int mask)
{
int old = current->umask;

current->umask = mask & 0777;
return (old);

}



system_call.s
/*

  • system_call.s contains the system-call low-level handling routines.
  • This also contains the timer-interrupt handler, as some of the code is
  • the same. The hd-interrupt is also here.
  • NOTE: This code handles signal-recognition, which happens every time
  • after a timer-interrupt and after each system call. Ordinary interrupts
  • don’t handle signal-recognition, as that would clutter them up totally
  • unnecessarily.
  • Stack layout in ‘ret_from_system_call’:
  • 0(%esp) - %eax
  • 4(%esp) - %ebx
  • 8(%esp) - %ecx
  • C(%esp) - %edx
  • 10(%esp) - %fs
  • 14(%esp) - %es
  • 18(%esp) - %ds
  • 1C(%esp) - %eip
  • 20(%esp) - %cs
  • 24(%esp) - %eflags
  • 28(%esp) - %oldesp
  • 2C(%esp) - %oldss
    */

SIG_CHLD = 17
EAX = 0x00
EBX = 0x04
ECX = 0x08
EDX = 0x0C
FS = 0x10
ES = 0x14
DS = 0x18
EIP = 0x1C
CS = 0x20
EFLAGS = 0x24
OLDESP = 0x28
OLDSS = 0x2C

state = 0 # these are offsets into the task-struct.
counter = 4
priority = 8
signal = 12
restorer = 16 # address of info-restorer
sig_fn = 20 # table of 32 signal addresses

nr_system_calls = 67

.globl _system_call,_sys_fork,_timer_interrupt,_hd_interrupt,_sys_execve

.align 2
bad_sys_call:
movl $-1,%eax
iret
.align 2
reschedule:
pushl $ret_from_sys_call
jmp _schedule
.align 2
_system_call:
cmpl $nr_system_calls-1,%eax
ja bad_sys_call
push %ds
push %es
push %fs
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
movl $0x10,%edx # set up ds,es to kernel space
mov %dx,%ds
mov %dx,%es
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
call _sys_call_table(,%eax,4)
pushl %eax
movl _current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
ret_from_sys_call:
movl _current,%eax # task[0] cannot have signals
cmpl _task,%eax
je 3f
movl CS(%esp),%ebx # was old code segment supervisor
testl $3,%ebx # mode? If so - don’t check signals
je 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
jne 3f
2: movl signal(%eax),%ebx # signals (bitmap, 32 signals)
bsfl %ebx,%ecx # %ecx is signal nr, return if none
je 3f
btrl %ecx,%ebx # clear it
movl %ebx,signal(%eax)
movl sig_fn(%eax,%ecx,4),%ebx # %ebx is signal handler address
cmpl $1,%ebx
jb default_signal # 0 is default signal handler - exit
je 2b # 1 is ignore - find next signal
movl $0,sig_fn(%eax,%ecx,4) # reset signal handler address
incl %ecx
xchgl %ebx,EIP(%esp) # put new return address on stack
subl $28,OLDESP(%esp)
movl OLDESP(%esp),%edx # push old return address on stack
pushl %eax # but first check that it’s ok.
pushl %ecx
pushl $28
pushl %edx
call _verify_area
popl %edx
addl $4,%esp
popl %ecx
popl %eax
movl restorer(%eax),%eax
movl %eax,%fs:(%edx) # flag/reg restorer
movl %ecx,%fs:4(%edx) # signal nr
movl EAX(%esp),%eax
movl %eax,%fs:8(%edx) # old eax
movl ECX(%esp),%eax
movl %eax,%fs:12(%edx) # old ecx
movl EDX(%esp),%eax
movl %eax,%fs:16(%edx) # old edx
movl EFLAGS(%esp),%eax
movl %eax,%fs:20(%edx) # old eflags
movl %ebx,%fs:24(%edx) # old return addr
3: popl %eax
popl %ebx
popl %ecx
popl %edx
pop %fs
pop %es
pop %ds
iret

default_signal:
incl %ecx
cmpl $SIG_CHLD,%ecx
je 2b
pushl %ecx
call _do_exit # remember to set bit 7 when dumping core
addl $4,%esp
jmp 3b

.align 2
_timer_interrupt:
push %ds # save ds,es and put kernel data space
push %es # into them. %fs is used by _system_call
push %fs
pushl %edx # we save %eax,%ecx,%edx as gcc doesn’t
pushl %ecx # save those across function calls. %ebx
pushl %ebx # is saved as we use that in ret_sys_call
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
incl _jiffies
movb $0x20,%al # EOI to interrupt controller #1
outb %al,$0x20
movl CS(%esp),%eax
andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor)
pushl %eax
call _do_timer # ‘do_timer(long CPL)’ does everything from
addl $4,%esp # task switching to accounting …
jmp ret_from_sys_call

.align 2
_sys_execve:
lea EIP(%esp),%eax
pushl %eax
call _do_execve
addl $4,%esp
ret

.align 2
_sys_fork:
call _find_empty_process
testl %eax,%eax
js 1f
push %gs
pushl %esi
pushl %edi
pushl %ebp
pushl %eax
call _copy_process
addl $20,%esp
1: ret

_hd_interrupt:
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
movb $0x20,%al
outb %al,$0x20 # EOI to interrupt controller #1
jmp 1f # give port chance to breathe
1: jmp 1f
1: outb %al,$0xA0 # same to controller #2
movl _do_hd,%eax
testl %eax,%eax
jne 1f
movl $_unexpected_hd_interrupt,%eax
1: call *%eax # “interesting” way of handling intr.
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret



traps.c
/*

  • ‘Traps.c’ handles hardware traps and faults after we have saved some
  • state in ‘asm.s’. Currently mostly a debugging-aid, will be extended
  • to mainly kill the offending process (probably by giving it a signal,
  • but possibly by killing it outright if necessary).
    */
    #include <string.h>

#include <linux/head.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>
#include <asm/segment.h>

#define get_seg_byte(seg,addr) ({
register char __res;
asm(“push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs”
:"=a" (__res):“0” (seg),“m” (*(addr)));
__res;})

#define get_seg_long(seg,addr) ({
register unsigned long __res;
asm(“push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs”
:"=a" (__res):“0” (seg),“m” (*(addr)));
__res;})

#define _fs() ({
register unsigned short __res;
asm(“mov %%fs,%%ax”:"=a" (__res)😃;
__res;})

int do_exit(long code);

void page_exception(void);

void divide_error(void);
void debug(void);
void nmi(void);
void int3(void);
void overflow(void);
void bounds(void);
void invalid_op(void);
void device_not_available(void);
void double_fault(void);
void coprocessor_segment_overrun(void);
void invalid_TSS(void);
void segment_not_present(void);
void stack_segment(void);
void general_protection(void);
void page_fault(void);
void coprocessor_error(void);
void reserved(void);

static void die(char * str,long esp_ptr,long nr)
{
long * esp = (long *) esp_ptr;
int i;

printk("%s: %04x\n\r",str,nr&0xffff);
printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n",
	esp[1],esp[0],esp[2],esp[4],esp[3]);
printk("fs: %04x\n",_fs());
printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17));
if (esp[4] == 0x17) {
	printk("Stack: ");
	for (i=0;i<4;i++)
		printk("%p ",get_seg_long(0x17,i+(long *)esp[3]));
	printk("\n");
}
str(i);
printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i);
for(i=0;i<10;i++)
	printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0])));
printk("\n\r");
do_exit(11);		/* play segment exception */

}

void do_double_fault(long esp, long error_code)
{
die(“double fault”,esp,error_code);
}

void do_general_protection(long esp, long error_code)
{
die(“general protection”,esp,error_code);
}

void do_divide_error(long esp, long error_code)
{
die(“divide error”,esp,error_code);
}

void do_int3(long * esp, long error_code,
long fs,long es,long ds,
long ebp,long esi,long edi,
long edx,long ecx,long ebx,long eax)
{
int tr;

__asm__("str %%ax":"=a" (tr):"0" (0));
printk("eax\t\tebx\t\tecx\t\tedx\n\r%8x\t%8x\t%8x\t%8x\n\r",
	eax,ebx,ecx,edx);
printk("esi\t\tedi\t\tebp\t\tesp\n\r%8x\t%8x\t%8x\t%8x\n\r",
	esi,edi,ebp,(long) esp);
printk("\n\rds\tes\tfs\ttr\n\r%4x\t%4x\t%4x\t%4x\n\r",
	ds,es,fs,tr);
printk("EIP: %8x   CS: %4x  EFLAGS: %8x\n\r",esp[0],esp[1],esp[2]);

}

void do_nmi(long esp, long error_code)
{
die(“nmi”,esp,error_code);
}

void do_debug(long esp, long error_code)
{
die(“debug”,esp,error_code);
}

void do_overflow(long esp, long error_code)
{
die(“overflow”,esp,error_code);
}

void do_bounds(long esp, long error_code)
{
die(“bounds”,esp,error_code);
}

void do_invalid_op(long esp, long error_code)
{
die(“invalid operand”,esp,error_code);
}

void do_device_not_available(long esp, long error_code)
{
die(“device not available”,esp,error_code);
}

void do_coprocessor_segment_overrun(long esp, long error_code)
{
die(“coprocessor segment overrun”,esp,error_code);
}

void do_invalid_TSS(long esp,long error_code)
{
die(“invalid TSS”,esp,error_code);
}

void do_segment_not_present(long esp,long error_code)
{
die(“segment not present”,esp,error_code);
}

void do_stack_segment(long esp,long error_code)
{
die(“stack segment”,esp,error_code);
}

void do_coprocessor_error(long esp, long error_code)
{
die(“coprocessor error”,esp,error_code);
}

void do_reserved(long esp, long error_code)
{
die(“reserved (15,17-31) error”,esp,error_code);
}

void trap_init(void)
{
int i;

set_trap_gate(0,&divide_error);
set_trap_gate(1,&debug);
set_trap_gate(2,&nmi);
set_system_gate(3,&int3);	/* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_trap_gate(8,&double_fault);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_trap_gate(14,&page_fault);
set_trap_gate(15,&reserved);
set_trap_gate(16,&coprocessor_error);
for (i=17;i<32;i++)
	set_trap_gate(i,&reserved);

/* asm(“movl $0x3ff000,%%eax\n\t”
“movl %%eax,%%db0\n\t”
“movl $0x000d0303,%%eax\n\t”
“movl %%eax,%%db7”
:::“ax”);*/
}



tty_io.c
/*

  • ‘tty_io.c’ gives an orthogonal feeling to tty’s, be they consoles
  • or rs-channels. It also implements echoing, cooked mode etc (well,
  • not currently, but …)
    */
    #include <ctype.h>
    #include <errno.h>
    #include <signal.h>

#define ALRMMASK (1<<(SIGALRM-1))

#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/segment.h>
#include <asm/system.h>

#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f)
#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f)
#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f)

#define L_CANON(tty) _L_FLAG((tty),ICANON)
#define L_ISIG(tty) _L_FLAG((tty),ISIG)
#define L_ECHO(tty) _L_FLAG((tty),ECHO)
#define L_ECHOE(tty) _L_FLAG((tty),ECHOE)
#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)
#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)

#define I_UCLC(tty) _I_FLAG((tty),IUCLC)
#define I_NLCR(tty) _I_FLAG((tty),INLCR)
#define I_CRNL(tty) _I_FLAG((tty),ICRNL)
#define I_NOCR(tty) _I_FLAG((tty),IGNCR)

#define O_POST(tty) _O_FLAG((tty),OPOST)
#define O_NLCR(tty) _O_FLAG((tty),ONLCR)
#define O_CRNL(tty) _O_FLAG((tty),OCRNL)
#define O_NLRET(tty) _O_FLAG((tty),ONLRET)
#define O_LCUC(tty) _O_FLAG((tty),OLCUC)

struct tty_struct tty_table[] = {
{
{0,
OPOST|ONLCR, /* change outgoing NL to CRNL /
0,
ICANON | ECHO | ECHOCTL | ECHOKE,
0, /
console termio /
INIT_C_CC},
0, /
initial pgrp /
0, /
initial stopped /
con_write,
{0,0,0,0,""}, /
console read-queue /
{0,0,0,0,""}, /
console write-queue /
{0,0,0,0,""} /
console secondary queue /
},{
{0, /IGNCR/
OPOST | ONLRET, /
change outgoing NL to CR /
B2400 | CS8,
0,
0,
INIT_C_CC},
0,
0,
rs_write,
{0x3f8,0,0,0,""}, /
rs 1 /
{0x3f8,0,0,0,""},
{0,0,0,0,""}
},{
{0, /IGNCR/
OPOST | ONLRET, /
change outgoing NL to CR /
B2400 | CS8,
0,
0,
INIT_C_CC},
0,
0,
rs_write,
{0x2f8,0,0,0,""}, /
rs 2 */
{0x2f8,0,0,0,""},
{0,0,0,0,""}
}
};

/*

  • these are the tables used by the machine code handlers.
  • you can implement pseudo-tty’s or something by changing
  • them. Currently not done.
    */
    struct tty_queue * table_list[]={
    &tty_table[0].read_q, &tty_table[0].write_q,
    &tty_table[1].read_q, &tty_table[1].write_q,
    &tty_table[2].read_q, &tty_table[2].write_q
    };

void tty_init(void)
{
rs_init();
con_init();
}

void tty_intr(struct tty_struct * tty, int signal)
{
int i;

if (tty->pgrp <= 0)
	return;
for (i=0;i<NR_TASKS;i++)
	if (task[i] && task[i]->pgrp==tty->pgrp)
		task[i]->signal |= 1<<(signal-1);

}

static void sleep_if_empty(struct tty_queue * queue)
{
cli();
while (!current->signal && EMPTY(*queue))
interruptible_sleep_on(&queue->proc_list);
sti();
}

static void sleep_if_full(struct tty_queue * queue)
{
if (!FULL(*queue))
return;
cli();
while (!current->signal && LEFT(*queue)<128)
interruptible_sleep_on(&queue->proc_list);
sti();
}

void copy_to_cooked(struct tty_struct * tty)
{
signed char c;

while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) {
	GETCH(tty->read_q,c);
	if (c==13)
		if (I_CRNL(tty))
			c=10;
		else if (I_NOCR(tty))
			continue;
		else ;
	else if (c==10 && I_NLCR(tty))
		c=13;
	if (I_UCLC(tty))
		c=tolower(c);
	if (L_CANON(tty)) {
		if (c==ERASE_CHAR(tty)) {
			if (EMPTY(tty->secondary) ||
			   (c=LAST(tty->secondary))==10 ||
			   c==EOF_CHAR(tty))
				continue;
			if (L_ECHO(tty)) {
				if (c<32)
					PUTCH(127,tty->write_q);
				PUTCH(127,tty->write_q);
				tty->write(tty);
			}
			DEC(tty->secondary.head);
			continue;
		}
		if (c==STOP_CHAR(tty)) {
			tty->stopped=1;
			continue;
		}
		if (c==START_CHAR(tty)) {
			tty->stopped=0;
			continue;
		}
	}
	if (!L_ISIG(tty)) {
		if (c==INTR_CHAR(tty)) {
			tty_intr(tty,SIGINT);
			continue;
		}
	}
	if (c==10 || c==EOF_CHAR(tty))
		tty->secondary.data++;
	if (L_ECHO(tty)) {
		if (c==10) {
			PUTCH(10,tty->write_q);
			PUTCH(13,tty->write_q);
		} else if (c<32) {
			if (L_ECHOCTL(tty)) {
				PUTCH('^',tty->write_q);
				PUTCH(c+64,tty->write_q);
			}
		} else
			PUTCH(c,tty->write_q);
		tty->write(tty);
	}
	PUTCH(c,tty->secondary);
}
wake_up(&tty->secondary.proc_list);

}

int tty_read(unsigned channel, char * buf, int nr)
{
struct tty_struct * tty;
char c, * b=buf;
int minimum,time,flag=0;
long oldalarm;

if (channel>2 || nr<0) return -1;
tty = &tty_table[channel];
oldalarm = current->alarm;
time = (unsigned) 10*tty->termios.c_cc[VTIME];
minimum = (unsigned) tty->termios.c_cc[VMIN];
if (time && !minimum) {
	minimum=1;
	if (flag=(!oldalarm || time+jiffies<oldalarm))
		current->alarm = time+jiffies;
}
if (minimum>nr)
	minimum=nr;
while (nr>0) {
	if (flag && (current->signal & ALRMMASK)) {
		current->signal &= ~ALRMMASK;
		break;
	}
	if (current->signal)
		break;
	if (EMPTY(tty->secondary) || (L_CANON(tty) &&
	!tty->secondary.data && LEFT(tty->secondary)>20)) {
		sleep_if_empty(&tty->secondary);
		continue;
	}
	do {
		GETCH(tty->secondary,c);
		if (c==EOF_CHAR(tty) || c==10)
			tty->secondary.data--;
		if (c==EOF_CHAR(tty) && L_CANON(tty))
			return (b-buf);
		else {
			put_fs_byte(c,b++);
			if (!--nr)
				break;
		}
	} while (nr>0 && !EMPTY(tty->secondary));
	if (time && !L_CANON(tty))
		if (flag=(!oldalarm || time+jiffies<oldalarm))
			current->alarm = time+jiffies;
		else
			current->alarm = oldalarm;
	if (L_CANON(tty)) {
		if (b-buf)
			break;
	} else if (b-buf >= minimum)
		break;
}
current->alarm = oldalarm;
if (current->signal && !(b-buf))
	return -EINTR;
return (b-buf);

}

int tty_write(unsigned channel, char * buf, int nr)
{
static cr_flag=0;
struct tty_struct * tty;
char c, *b=buf;

if (channel>2 || nr<0) return -1;
tty = channel + tty_table;
while (nr>0) {
	sleep_if_full(&tty->write_q);
	if (current->signal)
		break;
	while (nr>0 && !FULL(tty->write_q)) {
		c=get_fs_byte(b);
		if (O_POST(tty)) {
			if (c=='\r' && O_CRNL(tty))
				c='\n';
			else if (c=='\n' && O_NLRET(tty))
				c='\r';
			if (c=='\n' && !cr_flag && O_NLCR(tty)) {
				cr_flag = 1;
				PUTCH(13,tty->write_q);
				continue;
			}
			if (O_LCUC(tty))
				c=toupper(c);
		}
		b++; nr--;
		cr_flag = 0;
		PUTCH(c,tty->write_q);
	}
	tty->write(tty);
	if (nr>0)
		schedule();
}
return (b-buf);

}

/*

  • Jeh, sometimes I really like the 386.
  • This routine is called from an interrupt,
  • and there should be absolutely no problem
  • with sleeping even in an interrupt (I hope).
  • Of course, if somebody proves me wrong, I’ll
  • hate intel for all time 😃. We’ll have to
  • be careful and see to reinstating the interrupt
  • chips before calling this, though.
    */
    void do_tty_interrupt(int tty)
    {
    copy_to_cooked(tty_table+tty);
    }


vsprintf.c
/* vsprintf.c – Lars Wirzenius & Linus Torvalds. /
/

  • Wirzenius wrote this portably, Torvalds fucked it up 😃
    */

#include <stdarg.h>
#include <string.h>

/* we use this so that we can do without the ctype library */
#define is_digit© (© >= ‘0’ && © <= ‘9’)

static int skip_atoi(const char **s)
{
int i=0;

while (is_digit(**s))
	i = i*10 + *((*s)++) - '0';
return i;

}

#define ZEROPAD 1 /* pad with zero /
#define SIGN 2 /
unsigned/signed long /
#define PLUS 4 /
show plus /
#define SPACE 8 /
space if plus /
#define LEFT 16 /
left justified /
#define SPECIAL 32 /
0x /
#define SMALL 64 /
use ‘abcdef’ instead of ‘ABCDEF’ */

#define do_div(n,base) ({
int __res;
asm(“divl %4”:"=a" (n),"=d" (__res):“0” (n),“1” (0),“r” (base));
__res; })

static char * number(char * str, int num, int base, int size, int precision
,int type)
{
char c,sign,tmp[36];
const char *digits=“0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ”;
int i;

if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz";
if (type&LEFT) type &= ~ZEROPAD;
if (base<2 || base>36)
	return 0;
c = (type & ZEROPAD) ? '0' : ' ' ;
if (type&SIGN && num<0) {
	sign='-';
	num = -num;
} else
	sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);
if (sign) size--;
if (type&SPECIAL)
	if (base==16) size -= 2;
	else if (base==8) size--;
i=0;
if (num==0)
	tmp[i++]='0';
else while (num!=0)
	tmp[i++]=digits[do_div(num,base)];
if (i>precision) precision=i;
size -= precision;
if (!(type&(ZEROPAD+LEFT)))
	while(size-->0)
		*str++ = ' ';
if (sign)
	*str++ = sign;
if (type&SPECIAL)
	if (base==8)
		*str++ = '0';
	else if (base==16) {
		*str++ = '0';
		*str++ = digits[33];
	}
if (!(type&LEFT))
	while(size-->0)
		*str++ = c;
while(i<precision--)
	*str++ = '0';
while(i-->0)
	*str++ = tmp[i];
while(size-->0)
	*str++ = ' ';
return str;

}

int vsprintf(char *buf, const char *fmt, va_list args)
{
int len;
int i;
char * str;
char *s;
int *ip;

int flags;		/* flags to number() */

int field_width;	/* width of output field */
int precision;		/* min. # of digits for integers; max
			   number of chars for from string */
int qualifier;		/* 'h', 'l', or 'L' for integer fields */

for (str=buf ; *fmt ; ++fmt) {
	if (*fmt != '%') {
		*str++ = *fmt;
		continue;
	}
		
	/* process flags */
	flags = 0;
	repeat:
		++fmt;		/* this also skips first '%' */
		switch (*fmt) {
			case '-': flags |= LEFT; goto repeat;
			case '+': flags |= PLUS; goto repeat;
			case ' ': flags |= SPACE; goto repeat;
			case '#': flags |= SPECIAL; goto repeat;
			case '0': flags |= ZEROPAD; goto repeat;
			}
	
	/* get field width */
	field_width = -1;
	if (is_digit(*fmt))
		field_width = skip_atoi(&fmt);
	else if (*fmt == '*') {
		/* it's the next argument */
		field_width = va_arg(args, int);
		if (field_width < 0) {
			field_width = -field_width;
			flags |= LEFT;
		}
	}

	/* get the precision */
	precision = -1;
	if (*fmt == '.') {
		++fmt;	
		if (is_digit(*fmt))
			precision = skip_atoi(&fmt);
		else if (*fmt == '*') {
			/* it's the next argument */
			precision = va_arg(args, int);
		}
		if (precision < 0)
			precision = 0;
	}

	/* get the conversion qualifier */
	qualifier = -1;
	if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
		qualifier = *fmt;
		++fmt;
	}

	switch (*fmt) {
	case 'c':
		if (!(flags & LEFT))
			while (--field_width > 0)
				*str++ = ' ';
		*str++ = (unsigned char) va_arg(args, int);
		while (--field_width > 0)
			*str++ = ' ';
		break;

	case 's':
		s = va_arg(args, char *);
		len = strlen(s);
		if (precision < 0)
			precision = len;
		else if (len > precision)
			len = precision;

		if (!(flags & LEFT))
			while (len < field_width--)
				*str++ = ' ';
		for (i = 0; i < len; ++i)
			*str++ = *s++;
		while (len < field_width--)
			*str++ = ' ';
		break;

	case 'o':
		str = number(str, va_arg(args, unsigned long), 8,
			field_width, precision, flags);
		break;

	case 'p':
		if (field_width == -1) {
			field_width = 8;
			flags |= ZEROPAD;
		}
		str = number(str,
			(unsigned long) va_arg(args, void *), 16,
			field_width, precision, flags);
		break;

	case 'x':
		flags |= SMALL;
	case 'X':
		str = number(str, va_arg(args, unsigned long), 16,
			field_width, precision, flags);
		break;

	case 'd':
	case 'i':
		flags |= SIGN;
	case 'u':
		str = number(str, va_arg(args, unsigned long), 10,
			field_width, precision, flags);
		break;

	case 'n':
		ip = va_arg(args, int *);
		*ip = (str - buf);
		break;

	default:
		if (*fmt != '%')
			*str++ = '%';
		if (*fmt)
			*str++ = *fmt;
		else
			--fmt;
		break;
	}
}
*str = '\0';
return str-buf;

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

低调的小哥哥

你的关注就是我为你服务的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值