diff options
Diffstat (limited to 'linux/kernel')
| -rw-r--r-- | linux/kernel/Makefile | 90 | ||||
| -rw-r--r-- | linux/kernel/asm.s | 157 | ||||
| -rw-r--r-- | linux/kernel/console.c | 550 | ||||
| -rw-r--r-- | linux/kernel/exit.c | 135 | ||||
| -rw-r--r-- | linux/kernel/fork.c | 136 | ||||
| -rw-r--r-- | linux/kernel/hd.c | 413 | ||||
| -rw-r--r-- | linux/kernel/keyboard.s | 409 | ||||
| -rw-r--r-- | linux/kernel/mktime.c | 52 | ||||
| -rw-r--r-- | linux/kernel/panic.c | 11 | ||||
| -rw-r--r-- | linux/kernel/printk.c | 33 | ||||
| -rw-r--r-- | linux/kernel/rs_io.s | 141 | ||||
| -rw-r--r-- | linux/kernel/sched.c | 254 | ||||
| -rw-r--r-- | linux/kernel/serial.c | 53 | ||||
| -rw-r--r-- | linux/kernel/sys.c | 216 | ||||
| -rw-r--r-- | linux/kernel/system_call.s | 219 | ||||
| -rw-r--r-- | linux/kernel/traps.c | 199 | ||||
| -rw-r--r-- | linux/kernel/tty_io.c | 306 | ||||
| -rw-r--r-- | linux/kernel/vsprintf.c | 227 |
18 files changed, 3601 insertions, 0 deletions
diff --git a/linux/kernel/Makefile b/linux/kernel/Makefile new file mode 100644 index 0000000..23fe7dd --- /dev/null +++ b/linux/kernel/Makefile @@ -0,0 +1,90 @@ +# +# 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 diff --git a/linux/kernel/asm.s b/linux/kernel/asm.s new file mode 100644 index 0000000..6fe1981 --- /dev/null +++ b/linux/kernel/asm.s @@ -0,0 +1,157 @@ +/* + * 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 + diff --git a/linux/kernel/console.c b/linux/kernel/console.c new file mode 100644 index 0000000..9e00b31 --- /dev/null +++ b/linux/kernel/console.c @@ -0,0 +1,550 @@ +/* + * 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+LINES*COLUMNS*2; +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); +} diff --git a/linux/kernel/exit.c b/linux/kernel/exit.c new file mode 100644 index 0000000..3402c33 --- /dev/null +++ b/linux/kernel/exit.c @@ -0,0 +1,135 @@ +#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->uid==p->uid || + current->euid==p->uid || + current->uid==p->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)->pid==pid || + (pid==0 && (*p)->pgrp==current->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; +} + + diff --git a/linux/kernel/fork.c b/linux/kernel/fork.c new file mode 100644 index 0000000..70f9ddd --- /dev/null +++ b/linux/kernel/fork.c @@ -0,0 +1,136 @@ +/* + * '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; +} diff --git a/linux/kernel/hd.c b/linux/kernel/hd.c new file mode 100644 index 0000000..d3e6140 --- /dev/null +++ b/linux/kernel/hd.c @@ -0,0 +1,413 @@ +#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 (req==NR_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); +} diff --git a/linux/kernel/keyboard.s b/linux/kernel/keyboard.s new file mode 100644 index 0000000..ba54be5 --- /dev/null +++ b/linux/kernel/keyboard.s @@ -0,0 +1,409 @@ +/* + * 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 diff --git a/linux/kernel/mktime.c b/linux/kernel/mktime.c new file mode 100644 index 0000000..3ba79be --- /dev/null +++ b/linux/kernel/mktime.c @@ -0,0 +1,52 @@ +#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 (24*HOUR) +#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 += HOUR*tm->tm_hour; + res += MINUTE*tm->tm_min; + res += tm->tm_sec; + return res; +} diff --git a/linux/kernel/panic.c b/linux/kernel/panic.c new file mode 100644 index 0000000..feab0cc --- /dev/null +++ b/linux/kernel/panic.c @@ -0,0 +1,11 @@ +/* + * 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(;;); +} diff --git a/linux/kernel/printk.c b/linux/kernel/printk.c new file mode 100644 index 0000000..7a70dc3 --- /dev/null +++ b/linux/kernel/printk.c @@ -0,0 +1,33 @@ +/* + * 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; +} diff --git a/linux/kernel/rs_io.s b/linux/kernel/rs_io.s new file mode 100644 index 0000000..62f075f --- /dev/null +++ b/linux/kernel/rs_io.s @@ -0,0 +1,141 @@ +/* + * 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 diff --git a/linux/kernel/sched.c b/linux/kernel/sched.c new file mode 100644 index 0000000..03399fa --- /dev/null +++ b/linux/kernel/sched.c @@ -0,0 +1,254 @@ +/* + * '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); +} diff --git a/linux/kernel/serial.c b/linux/kernel/serial.c new file mode 100644 index 0000000..f542513 --- /dev/null +++ b/linux/kernel/serial.c @@ -0,0 +1,53 @@ +/* + * 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(); +} diff --git a/linux/kernel/sys.c b/linux/kernel/sys.c new file mode 100644 index 0000000..f18ee7e --- /dev/null +++ b/linux/kernel/sys.c @@ -0,0 +1,216 @@ +#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->gid==gid || current->sgid==gid) + 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 (uid==current->uid || current->suid==current->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); +} diff --git a/linux/kernel/system_call.s b/linux/kernel/system_call.s new file mode 100644 index 0000000..df4f072 --- /dev/null +++ b/linux/kernel/system_call.s @@ -0,0 +1,219 @@ +/* + * 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 + diff --git a/linux/kernel/traps.c b/linux/kernel/traps.c new file mode 100644 index 0000000..b6e8bdb --- /dev/null +++ b/linux/kernel/traps.c @@ -0,0 +1,199 @@ +/* + * '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,÷_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");*/ +} + diff --git a/linux/kernel/tty_io.c b/linux/kernel/tty_io.c new file mode 100644 index 0000000..68a390c --- /dev/null +++ b/linux/kernel/tty_io.c @@ -0,0 +1,306 @@ +/* + * '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); +} diff --git a/linux/kernel/vsprintf.c b/linux/kernel/vsprintf.c new file mode 100644 index 0000000..69c0578 --- /dev/null +++ b/linux/kernel/vsprintf.c @@ -0,0 +1,227 @@ +/* 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(c) ((c) >= '0' && (c) <= '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; +} |
