diff options
Diffstat (limited to 'src/kernel')
28 files changed, 5712 insertions, 0 deletions
diff --git a/src/kernel/Makefile b/src/kernel/Makefile new file mode 100644 index 0000000..aa6b3d7 --- /dev/null +++ b/src/kernel/Makefile | |||
@@ -0,0 +1,87 @@ | |||
1 | # | ||
2 | # Makefile for the FREAX-kernel. | ||
3 | # | ||
4 | # Note! Dependencies are done automagically by 'make dep', which also | ||
5 | # removes any old dependencies. DON'T put your own dependencies here | ||
6 | # unless it's something special (ie not a .c file). | ||
7 | # | ||
8 | |||
9 | include ../Makefile.header | ||
10 | |||
11 | LDFLAGS += -r | ||
12 | |||
13 | # NOTE!: do not try to add -On to CFLAGS with gcc4.x, which will optimize the | ||
14 | # memcpy function a lot to let the kernel not work, for fixing this problem, | ||
15 | # please refer to glibc and rewrite the memcpy in include/string.h, or just not | ||
16 | # use any -On options with gcc 4.x when compiling it. in fact, we only can not | ||
17 | # use -On with gcc4.x to compile fork.c, but after that you need to isolate the | ||
18 | # compling procedure of it, it's not good too. for playing with linux-0.11, we | ||
19 | # just have fun, have no much concerning about the performance. | ||
20 | |||
21 | CFLAGS += -I../include | ||
22 | |||
23 | CPP += -I../include | ||
24 | |||
25 | .c.s: | ||
26 | @$(CC) $(CFLAGS) \ | ||
27 | -S -o $*.s $< | ||
28 | .s.o: | ||
29 | @$(AS) -o $*.o $< | ||
30 | .c.o: | ||
31 | @$(CC) $(CFLAGS) \ | ||
32 | -c -o $*.o $< | ||
33 | |||
34 | OBJS = sched.o system_call.o traps.o asm.o fork.o \ | ||
35 | panic.o printk.o vsprintf.o sys.o exit.o \ | ||
36 | signal.o mktime.o | ||
37 | |||
38 | kernel.o: $(OBJS) | ||
39 | @$(LD) $(LDFLAGS) -o kernel.o $(OBJS) | ||
40 | @sync | ||
41 | |||
42 | clean: | ||
43 | @rm -f core *.o *.a tmp_make keyboard.s | ||
44 | @for i in *.c;do rm -f `basename $$i .c`.s;done | ||
45 | @for i in chr_drv blk_drv math; do make clean -C $$i; done | ||
46 | |||
47 | dep: | ||
48 | @sed '/\#\#\# Dependencies/q' < Makefile > tmp_make | ||
49 | @(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ | ||
50 | $(CPP) -M $$i;done) >> tmp_make | ||
51 | @cp tmp_make Makefile | ||
52 | @for i in chr_drv blk_drv; do make dep -C $$i; done | ||
53 | |||
54 | ### Dependencies: | ||
55 | exit.s exit.o: exit.c ../include/errno.h ../include/signal.h \ | ||
56 | ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \ | ||
57 | ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ | ||
58 | ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \ | ||
59 | ../include/asm/segment.h | ||
60 | fork.s fork.o: fork.c ../include/errno.h ../include/linux/sched.h \ | ||
61 | ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ | ||
62 | ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ | ||
63 | ../include/asm/segment.h ../include/asm/system.h | ||
64 | mktime.s mktime.o: mktime.c ../include/time.h | ||
65 | panic.s panic.o: panic.c ../include/linux/kernel.h ../include/linux/sched.h \ | ||
66 | ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ | ||
67 | ../include/linux/mm.h ../include/signal.h | ||
68 | printk.s printk.o: printk.c ../include/stdarg.h ../include/stddef.h \ | ||
69 | ../include/linux/kernel.h | ||
70 | sched.s sched.o: sched.c ../include/linux/sched.h ../include/linux/head.h \ | ||
71 | ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ | ||
72 | ../include/signal.h ../include/linux/kernel.h ../include/linux/sys.h \ | ||
73 | ../include/linux/fdreg.h ../include/asm/system.h ../include/asm/io.h \ | ||
74 | ../include/asm/segment.h | ||
75 | signal.s signal.o: signal.c ../include/linux/sched.h ../include/linux/head.h \ | ||
76 | ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ | ||
77 | ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h | ||
78 | sys.s sys.o: sys.c ../include/errno.h ../include/linux/sched.h \ | ||
79 | ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ | ||
80 | ../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \ | ||
81 | ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h \ | ||
82 | ../include/sys/times.h ../include/sys/utsname.h | ||
83 | traps.s traps.o: traps.c ../include/string.h ../include/linux/head.h \ | ||
84 | ../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \ | ||
85 | ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ | ||
86 | ../include/asm/system.h ../include/asm/segment.h ../include/asm/io.h | ||
87 | vsprintf.s vsprintf.o: vsprintf.c ../include/stdarg.h ../include/string.h | ||
diff --git a/src/kernel/asm.s b/src/kernel/asm.s new file mode 100644 index 0000000..1022817 --- /dev/null +++ b/src/kernel/asm.s | |||
@@ -0,0 +1,146 @@ | |||
1 | /* | ||
2 | * linux/kernel/asm.s | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * asm.s contains the low-level code for most hardware faults. | ||
9 | * page_exception is handled by the mm, so that isn't here. This | ||
10 | * file also handles (hopefully) fpu-exceptions due to TS-bit, as | ||
11 | * the fpu must be properly saved/resored. This hasn't been tested. | ||
12 | */ | ||
13 | |||
14 | .globl divide_error,debug,nmi,int3,overflow,bounds,invalid_op | ||
15 | .globl double_fault,coprocessor_segment_overrun | ||
16 | .globl invalid_TSS,segment_not_present,stack_segment | ||
17 | .globl general_protection,coprocessor_error,irq13,reserved | ||
18 | |||
19 | divide_error: | ||
20 | pushl $do_divide_error | ||
21 | no_error_code: | ||
22 | xchgl %eax,(%esp) | ||
23 | pushl %ebx | ||
24 | pushl %ecx | ||
25 | pushl %edx | ||
26 | pushl %edi | ||
27 | pushl %esi | ||
28 | pushl %ebp | ||
29 | push %ds | ||
30 | push %es | ||
31 | push %fs | ||
32 | pushl $0 # "error code" | ||
33 | lea 44(%esp),%edx | ||
34 | pushl %edx | ||
35 | movl $0x10,%edx | ||
36 | mov %dx,%ds | ||
37 | mov %dx,%es | ||
38 | mov %dx,%fs | ||
39 | call *%eax | ||
40 | addl $8,%esp | ||
41 | pop %fs | ||
42 | pop %es | ||
43 | pop %ds | ||
44 | popl %ebp | ||
45 | popl %esi | ||
46 | popl %edi | ||
47 | popl %edx | ||
48 | popl %ecx | ||
49 | popl %ebx | ||
50 | popl %eax | ||
51 | iret | ||
52 | |||
53 | debug: | ||
54 | pushl $do_int3 # _do_debug | ||
55 | jmp no_error_code | ||
56 | |||
57 | nmi: | ||
58 | pushl $do_nmi | ||
59 | jmp no_error_code | ||
60 | |||
61 | int3: | ||
62 | pushl $do_int3 | ||
63 | jmp no_error_code | ||
64 | |||
65 | overflow: | ||
66 | pushl $do_overflow | ||
67 | jmp no_error_code | ||
68 | |||
69 | bounds: | ||
70 | pushl $do_bounds | ||
71 | jmp no_error_code | ||
72 | |||
73 | invalid_op: | ||
74 | pushl $do_invalid_op | ||
75 | jmp no_error_code | ||
76 | |||
77 | coprocessor_segment_overrun: | ||
78 | pushl $do_coprocessor_segment_overrun | ||
79 | jmp no_error_code | ||
80 | |||
81 | reserved: | ||
82 | pushl $do_reserved | ||
83 | jmp no_error_code | ||
84 | |||
85 | irq13: | ||
86 | pushl %eax | ||
87 | xorb %al,%al | ||
88 | outb %al,$0xF0 | ||
89 | movb $0x20,%al | ||
90 | outb %al,$0x20 | ||
91 | jmp 1f | ||
92 | 1: jmp 1f | ||
93 | 1: outb %al,$0xA0 | ||
94 | popl %eax | ||
95 | jmp coprocessor_error | ||
96 | |||
97 | double_fault: | ||
98 | pushl $do_double_fault | ||
99 | error_code: | ||
100 | xchgl %eax,4(%esp) # error code <-> %eax | ||
101 | xchgl %ebx,(%esp) # &function <-> %ebx | ||
102 | pushl %ecx | ||
103 | pushl %edx | ||
104 | pushl %edi | ||
105 | pushl %esi | ||
106 | pushl %ebp | ||
107 | push %ds | ||
108 | push %es | ||
109 | push %fs | ||
110 | pushl %eax # error code | ||
111 | lea 44(%esp),%eax # offset | ||
112 | pushl %eax | ||
113 | movl $0x10,%eax | ||
114 | mov %ax,%ds | ||
115 | mov %ax,%es | ||
116 | mov %ax,%fs | ||
117 | call *%ebx | ||
118 | addl $8,%esp | ||
119 | pop %fs | ||
120 | pop %es | ||
121 | pop %ds | ||
122 | popl %ebp | ||
123 | popl %esi | ||
124 | popl %edi | ||
125 | popl %edx | ||
126 | popl %ecx | ||
127 | popl %ebx | ||
128 | popl %eax | ||
129 | iret | ||
130 | |||
131 | invalid_TSS: | ||
132 | pushl $do_invalid_TSS | ||
133 | jmp error_code | ||
134 | |||
135 | segment_not_present: | ||
136 | pushl $do_segment_not_present | ||
137 | jmp error_code | ||
138 | |||
139 | stack_segment: | ||
140 | pushl $do_stack_segment | ||
141 | jmp error_code | ||
142 | |||
143 | general_protection: | ||
144 | pushl $do_general_protection | ||
145 | jmp error_code | ||
146 | |||
diff --git a/src/kernel/blk_drv/Makefile b/src/kernel/blk_drv/Makefile new file mode 100644 index 0000000..766d2fd --- /dev/null +++ b/src/kernel/blk_drv/Makefile | |||
@@ -0,0 +1,62 @@ | |||
1 | # | ||
2 | # Makefile for the FREAX-kernel block device drivers. | ||
3 | # | ||
4 | # Note! Dependencies are done automagically by 'make dep', which also | ||
5 | # removes any old dependencies. DON'T put your own dependencies here | ||
6 | # unless it's something special (ie not a .c file). | ||
7 | # | ||
8 | |||
9 | include ../../Makefile.header | ||
10 | |||
11 | CFLAGS += -I../../include | ||
12 | CPP += -I../../include | ||
13 | |||
14 | .c.s: | ||
15 | @$(CC) $(CFLAGS) \ | ||
16 | -S -o $*.s $< | ||
17 | .s.o: | ||
18 | @$(AS) -o $*.o $< | ||
19 | .c.o: | ||
20 | @$(CC) $(CFLAGS) \ | ||
21 | -c -o $*.o $< | ||
22 | |||
23 | OBJS = ll_rw_blk.o floppy.o hd.o ramdisk.o | ||
24 | |||
25 | blk_drv.a: $(OBJS) | ||
26 | @$(AR) rcs blk_drv.a $(OBJS) | ||
27 | @sync | ||
28 | |||
29 | clean: | ||
30 | @rm -f core *.o *.a tmp_make | ||
31 | @for i in *.c;do rm -f `basename $$i .c`.s;done | ||
32 | |||
33 | dep: | ||
34 | @sed '/\#\#\# Dependencies/q' < Makefile > tmp_make | ||
35 | @(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ | ||
36 | $(CPP) -M $$i;done) >> tmp_make | ||
37 | @cp tmp_make Makefile | ||
38 | |||
39 | ### Dependencies: | ||
40 | floppy.s floppy.o: floppy.c ../../include/linux/sched.h ../../include/linux/head.h \ | ||
41 | ../../include/linux/fs.h ../../include/sys/types.h \ | ||
42 | ../../include/linux/mm.h ../../include/signal.h \ | ||
43 | ../../include/linux/kernel.h ../../include/linux/fdreg.h \ | ||
44 | ../../include/asm/system.h ../../include/asm/io.h \ | ||
45 | ../../include/asm/segment.h blk.h | ||
46 | hd.s hd.o: hd.c ../../include/linux/config.h ../../include/linux/sched.h \ | ||
47 | ../../include/linux/head.h ../../include/linux/fs.h \ | ||
48 | ../../include/sys/types.h ../../include/linux/mm.h \ | ||
49 | ../../include/signal.h ../../include/linux/kernel.h \ | ||
50 | ../../include/linux/hdreg.h ../../include/asm/system.h \ | ||
51 | ../../include/asm/io.h ../../include/asm/segment.h blk.h | ||
52 | ll_rw_blk.s ll_rw_blk.o: ll_rw_blk.c ../../include/errno.h \ | ||
53 | ../../include/linux/sched.h ../../include/linux/head.h \ | ||
54 | ../../include/linux/fs.h ../../include/sys/types.h \ | ||
55 | ../../include/linux/mm.h ../../include/signal.h \ | ||
56 | ../../include/linux/kernel.h ../../include/asm/system.h blk.h | ||
57 | ramdisk.s ramdisk.o: ramdisk.c ../../include/string.h ../../include/linux/config.h \ | ||
58 | ../../include/linux/sched.h ../../include/linux/head.h \ | ||
59 | ../../include/linux/fs.h ../../include/sys/types.h \ | ||
60 | ../../include/linux/mm.h ../../include/signal.h \ | ||
61 | ../../include/linux/kernel.h ../../include/asm/system.h \ | ||
62 | ../../include/asm/segment.h ../../include/asm/memory.h blk.h | ||
diff --git a/src/kernel/blk_drv/blk.h b/src/kernel/blk_drv/blk.h new file mode 100644 index 0000000..b272173 --- /dev/null +++ b/src/kernel/blk_drv/blk.h | |||
@@ -0,0 +1,140 @@ | |||
1 | #ifndef _BLK_H | ||
2 | #define _BLK_H | ||
3 | |||
4 | #define NR_BLK_DEV 7 | ||
5 | /* | ||
6 | * NR_REQUEST is the number of entries in the request-queue. | ||
7 | * NOTE that writes may use only the low 2/3 of these: reads | ||
8 | * take precedence. | ||
9 | * | ||
10 | * 32 seems to be a reasonable number: enough to get some benefit | ||
11 | * from the elevator-mechanism, but not so much as to lock a lot of | ||
12 | * buffers when they are in the queue. 64 seems to be too many (easily | ||
13 | * long pauses in reading when heavy writing/syncing is going on) | ||
14 | */ | ||
15 | #define NR_REQUEST 32 | ||
16 | |||
17 | /* | ||
18 | * Ok, this is an expanded form so that we can use the same | ||
19 | * request for paging requests when that is implemented. In | ||
20 | * paging, 'bh' is NULL, and 'waiting' is used to wait for | ||
21 | * read/write completion. | ||
22 | */ | ||
23 | struct request { | ||
24 | int dev; /* -1 if no request */ | ||
25 | int cmd; /* READ or WRITE */ | ||
26 | int errors; | ||
27 | unsigned long sector; | ||
28 | unsigned long nr_sectors; | ||
29 | char * buffer; | ||
30 | struct task_struct * waiting; | ||
31 | struct buffer_head * bh; | ||
32 | struct request * next; | ||
33 | }; | ||
34 | |||
35 | /* | ||
36 | * This is used in the elevator algorithm: Note that | ||
37 | * reads always go before writes. This is natural: reads | ||
38 | * are much more time-critical than writes. | ||
39 | */ | ||
40 | #define IN_ORDER(s1,s2) \ | ||
41 | ((s1)->cmd<(s2)->cmd || ((s1)->cmd==(s2)->cmd && \ | ||
42 | ((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \ | ||
43 | (s1)->sector < (s2)->sector)))) | ||
44 | |||
45 | struct blk_dev_struct { | ||
46 | void (*request_fn)(void); | ||
47 | struct request * current_request; | ||
48 | }; | ||
49 | |||
50 | extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; | ||
51 | extern struct request request[NR_REQUEST]; | ||
52 | extern struct task_struct * wait_for_request; | ||
53 | |||
54 | #ifdef MAJOR_NR | ||
55 | |||
56 | /* | ||
57 | * Add entries as needed. Currently the only block devices | ||
58 | * supported are hard-disks and floppies. | ||
59 | */ | ||
60 | |||
61 | #if (MAJOR_NR == 1) | ||
62 | /* ram disk */ | ||
63 | #define DEVICE_NAME "ramdisk" | ||
64 | #define DEVICE_REQUEST do_rd_request | ||
65 | #define DEVICE_NR(device) ((device) & 7) | ||
66 | #define DEVICE_ON(device) | ||
67 | #define DEVICE_OFF(device) | ||
68 | |||
69 | #elif (MAJOR_NR == 2) | ||
70 | /* floppy */ | ||
71 | #define DEVICE_NAME "floppy" | ||
72 | #define DEVICE_INTR do_floppy | ||
73 | #define DEVICE_REQUEST do_fd_request | ||
74 | #define DEVICE_NR(device) ((device) & 3) | ||
75 | #define DEVICE_ON(device) floppy_on(DEVICE_NR(device)) | ||
76 | #define DEVICE_OFF(device) floppy_off(DEVICE_NR(device)) | ||
77 | |||
78 | #elif (MAJOR_NR == 3) | ||
79 | /* harddisk */ | ||
80 | #define DEVICE_NAME "harddisk" | ||
81 | #define DEVICE_INTR do_hd | ||
82 | #define DEVICE_REQUEST do_hd_request | ||
83 | #define DEVICE_NR(device) (MINOR(device)/5) | ||
84 | #define DEVICE_ON(device) | ||
85 | #define DEVICE_OFF(device) | ||
86 | |||
87 | #else | ||
88 | /* unknown blk device */ | ||
89 | #error "unknown blk device" | ||
90 | |||
91 | #endif | ||
92 | |||
93 | #define CURRENT (blk_dev[MAJOR_NR].current_request) | ||
94 | #define CURRENT_DEV DEVICE_NR(CURRENT->dev) | ||
95 | |||
96 | #ifdef DEVICE_INTR | ||
97 | void (*DEVICE_INTR)(void) = NULL; | ||
98 | #endif | ||
99 | static void (DEVICE_REQUEST)(void); | ||
100 | |||
101 | static inline void unlock_buffer(struct buffer_head * bh) | ||
102 | { | ||
103 | if (!bh->b_lock) | ||
104 | printk(DEVICE_NAME ": free buffer being unlocked\n"); | ||
105 | bh->b_lock=0; | ||
106 | wake_up(&bh->b_wait); | ||
107 | } | ||
108 | |||
109 | static inline void end_request(int uptodate) | ||
110 | { | ||
111 | DEVICE_OFF(CURRENT->dev); | ||
112 | if (CURRENT->bh) { | ||
113 | CURRENT->bh->b_uptodate = uptodate; | ||
114 | unlock_buffer(CURRENT->bh); | ||
115 | } | ||
116 | if (!uptodate) { | ||
117 | printk(DEVICE_NAME " I/O error\n\r"); | ||
118 | printk("dev %04x, block %d\n\r",CURRENT->dev, | ||
119 | CURRENT->bh->b_blocknr); | ||
120 | } | ||
121 | wake_up(&CURRENT->waiting); | ||
122 | wake_up(&wait_for_request); | ||
123 | CURRENT->dev = -1; | ||
124 | CURRENT = CURRENT->next; | ||
125 | } | ||
126 | |||
127 | #define INIT_REQUEST \ | ||
128 | repeat: \ | ||
129 | if (!CURRENT) \ | ||
130 | return; \ | ||
131 | if (MAJOR(CURRENT->dev) != MAJOR_NR) \ | ||
132 | panic(DEVICE_NAME ": request list destroyed"); \ | ||
133 | if (CURRENT->bh) { \ | ||
134 | if (!CURRENT->bh->b_lock) \ | ||
135 | panic(DEVICE_NAME ": block not locked"); \ | ||
136 | } | ||
137 | |||
138 | #endif | ||
139 | |||
140 | #endif | ||
diff --git a/src/kernel/blk_drv/floppy.c b/src/kernel/blk_drv/floppy.c new file mode 100644 index 0000000..f497906 --- /dev/null +++ b/src/kernel/blk_drv/floppy.c | |||
@@ -0,0 +1,462 @@ | |||
1 | /* | ||
2 | * linux/kernel/floppy.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * 02.12.91 - Changed to static variables to indicate need for reset | ||
9 | * and recalibrate. This makes some things easier (output_byte reset | ||
10 | * checking etc), and means less interrupt jumping in case of errors, | ||
11 | * so the code is hopefully easier to understand. | ||
12 | */ | ||
13 | |||
14 | /* | ||
15 | * This file is certainly a mess. I've tried my best to get it working, | ||
16 | * but I don't like programming floppies, and I have only one anyway. | ||
17 | * Urgel. I should check for more errors, and do more graceful error | ||
18 | * recovery. Seems there are problems with several drives. I've tried to | ||
19 | * correct them. No promises. | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * As with hd.c, all routines within this file can (and will) be called | ||
24 | * by interrupts, so extreme caution is needed. A hardware interrupt | ||
25 | * handler may not sleep, or a kernel panic will happen. Thus I cannot | ||
26 | * call "floppy-on" directly, but have to set a special timer interrupt | ||
27 | * etc. | ||
28 | * | ||
29 | * Also, I'm not certain this works on more than 1 floppy. Bugs may | ||
30 | * abund. | ||
31 | */ | ||
32 | |||
33 | #include <linux/sched.h> | ||
34 | #include <linux/fs.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/fdreg.h> | ||
37 | #include <asm/system.h> | ||
38 | #include <asm/io.h> | ||
39 | #include <asm/segment.h> | ||
40 | |||
41 | #define MAJOR_NR 2 | ||
42 | #include "blk.h" | ||
43 | |||
44 | static int recalibrate = 0; | ||
45 | static int reset = 0; | ||
46 | static int seek = 0; | ||
47 | |||
48 | extern unsigned char current_DOR; | ||
49 | |||
50 | #define immoutb_p(val,port) \ | ||
51 | __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port)) | ||
52 | |||
53 | #define TYPE(x) ((x)>>2) | ||
54 | #define DRIVE(x) ((x)&0x03) | ||
55 | /* | ||
56 | * Note that MAX_ERRORS=8 doesn't imply that we retry every bad read | ||
57 | * max 8 times - some types of errors increase the errorcount by 2, | ||
58 | * so we might actually retry only 5-6 times before giving up. | ||
59 | */ | ||
60 | #define MAX_ERRORS 8 | ||
61 | |||
62 | /* | ||
63 | * globals used by 'result()' | ||
64 | */ | ||
65 | #define MAX_REPLIES 7 | ||
66 | static unsigned char reply_buffer[MAX_REPLIES]; | ||
67 | #define ST0 (reply_buffer[0]) | ||
68 | #define ST1 (reply_buffer[1]) | ||
69 | #define ST2 (reply_buffer[2]) | ||
70 | #define ST3 (reply_buffer[3]) | ||
71 | |||
72 | /* | ||
73 | * This struct defines the different floppy types. Unlike minix | ||
74 | * linux doesn't have a "search for right type"-type, as the code | ||
75 | * for that is convoluted and weird. I've got enough problems with | ||
76 | * this driver as it is. | ||
77 | * | ||
78 | * The 'stretch' tells if the tracks need to be boubled for some | ||
79 | * types (ie 360kB diskette in 1.2MB drive etc). Others should | ||
80 | * be self-explanatory. | ||
81 | */ | ||
82 | static struct floppy_struct { | ||
83 | unsigned int size, sect, head, track, stretch; | ||
84 | unsigned char gap,rate,spec1; | ||
85 | } floppy_type[] = { | ||
86 | { 0, 0,0, 0,0,0x00,0x00,0x00 }, /* no testing */ | ||
87 | { 720, 9,2,40,0,0x2A,0x02,0xDF }, /* 360kB PC diskettes */ | ||
88 | { 2400,15,2,80,0,0x1B,0x00,0xDF }, /* 1.2 MB AT-diskettes */ | ||
89 | { 720, 9,2,40,1,0x2A,0x02,0xDF }, /* 360kB in 720kB drive */ | ||
90 | { 1440, 9,2,80,0,0x2A,0x02,0xDF }, /* 3.5" 720kB diskette */ | ||
91 | { 720, 9,2,40,1,0x23,0x01,0xDF }, /* 360kB in 1.2MB drive */ | ||
92 | { 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */ | ||
93 | { 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */ | ||
94 | }; | ||
95 | /* | ||
96 | * Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps | ||
97 | * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc), | ||
98 | * H is head unload time (1=16ms, 2=32ms, etc) | ||
99 | * | ||
100 | * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc) | ||
101 | * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA). | ||
102 | */ | ||
103 | |||
104 | extern void floppy_interrupt(void); | ||
105 | extern char tmp_floppy_area[1024]; | ||
106 | |||
107 | /* | ||
108 | * These are global variables, as that's the easiest way to give | ||
109 | * information to interrupts. They are the data used for the current | ||
110 | * request. | ||
111 | */ | ||
112 | static int cur_spec1 = -1; | ||
113 | static int cur_rate = -1; | ||
114 | static struct floppy_struct * floppy = floppy_type; | ||
115 | static unsigned char current_drive = 0; | ||
116 | static unsigned char sector = 0; | ||
117 | static unsigned char head = 0; | ||
118 | static unsigned char track = 0; | ||
119 | static unsigned char seek_track = 0; | ||
120 | static unsigned char current_track = 255; | ||
121 | static unsigned char command = 0; | ||
122 | unsigned char selected = 0; | ||
123 | struct task_struct * wait_on_floppy_select = NULL; | ||
124 | |||
125 | void floppy_deselect(unsigned int nr) | ||
126 | { | ||
127 | if (nr != (current_DOR & 3)) | ||
128 | printk("floppy_deselect: drive not selected\n\r"); | ||
129 | selected = 0; | ||
130 | wake_up(&wait_on_floppy_select); | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * floppy-change is never called from an interrupt, so we can relax a bit | ||
135 | * here, sleep etc. Note that floppy-on tries to set current_DOR to point | ||
136 | * to the desired drive, but it will probably not survive the sleep if | ||
137 | * several floppies are used at the same time: thus the loop. | ||
138 | */ | ||
139 | int floppy_change(unsigned int nr) | ||
140 | { | ||
141 | repeat: | ||
142 | floppy_on(nr); | ||
143 | while ((current_DOR & 3) != nr && selected) | ||
144 | interruptible_sleep_on(&wait_on_floppy_select); | ||
145 | if ((current_DOR & 3) != nr) | ||
146 | goto repeat; | ||
147 | if (inb(FD_DIR) & 0x80) { | ||
148 | floppy_off(nr); | ||
149 | return 1; | ||
150 | } | ||
151 | floppy_off(nr); | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | #define copy_buffer(from,to) \ | ||
156 | __asm__("cld ; rep ; movsl" \ | ||
157 | ::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \ | ||
158 | ) | ||
159 | |||
160 | static void setup_DMA(void) | ||
161 | { | ||
162 | long addr = (long) CURRENT->buffer; | ||
163 | |||
164 | cli(); | ||
165 | if (addr >= 0x100000) { | ||
166 | addr = (long) tmp_floppy_area; | ||
167 | if (command == FD_WRITE) | ||
168 | copy_buffer(CURRENT->buffer,tmp_floppy_area); | ||
169 | } | ||
170 | /* mask DMA 2 */ | ||
171 | immoutb_p(4|2,10); | ||
172 | /* output command byte. I don't know why, but everyone (minix, */ | ||
173 | /* sanches & canton) output this twice, first to 12 then to 11 */ | ||
174 | __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t" | ||
175 | "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:":: | ||
176 | "a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE))); | ||
177 | /* 8 low bits of addr */ | ||
178 | immoutb_p(addr,4); | ||
179 | addr >>= 8; | ||
180 | /* bits 8-15 of addr */ | ||
181 | immoutb_p(addr,4); | ||
182 | addr >>= 8; | ||
183 | /* bits 16-19 of addr */ | ||
184 | immoutb_p(addr,0x81); | ||
185 | /* low 8 bits of count-1 (1024-1=0x3ff) */ | ||
186 | immoutb_p(0xff,5); | ||
187 | /* high 8 bits of count-1 */ | ||
188 | immoutb_p(3,5); | ||
189 | /* activate DMA 2 */ | ||
190 | immoutb_p(0|2,10); | ||
191 | sti(); | ||
192 | } | ||
193 | |||
194 | static void output_byte(char byte) | ||
195 | { | ||
196 | int counter; | ||
197 | unsigned char status; | ||
198 | |||
199 | if (reset) | ||
200 | return; | ||
201 | for(counter = 0 ; counter < 10000 ; counter++) { | ||
202 | status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR); | ||
203 | if (status == STATUS_READY) { | ||
204 | outb(byte,FD_DATA); | ||
205 | return; | ||
206 | } | ||
207 | } | ||
208 | reset = 1; | ||
209 | printk("Unable to send byte to FDC\n\r"); | ||
210 | } | ||
211 | |||
212 | static int result(void) | ||
213 | { | ||
214 | int i = 0, counter, status; | ||
215 | |||
216 | if (reset) | ||
217 | return -1; | ||
218 | for (counter = 0 ; counter < 10000 ; counter++) { | ||
219 | status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY); | ||
220 | if (status == STATUS_READY) | ||
221 | return i; | ||
222 | if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) { | ||
223 | if (i >= MAX_REPLIES) | ||
224 | break; | ||
225 | reply_buffer[i++] = inb_p(FD_DATA); | ||
226 | } | ||
227 | } | ||
228 | reset = 1; | ||
229 | printk("Getstatus times out\n\r"); | ||
230 | return -1; | ||
231 | } | ||
232 | |||
233 | static void bad_flp_intr(void) | ||
234 | { | ||
235 | CURRENT->errors++; | ||
236 | if (CURRENT->errors > MAX_ERRORS) { | ||
237 | floppy_deselect(current_drive); | ||
238 | end_request(0); | ||
239 | } | ||
240 | if (CURRENT->errors > MAX_ERRORS/2) | ||
241 | reset = 1; | ||
242 | else | ||
243 | recalibrate = 1; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * Ok, this interrupt is called after a DMA read/write has succeeded, | ||
248 | * so we check the results, and copy any buffers. | ||
249 | */ | ||
250 | static void rw_interrupt(void) | ||
251 | { | ||
252 | if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73)) { | ||
253 | if (ST1 & 0x02) { | ||
254 | printk("Drive %d is write protected\n\r",current_drive); | ||
255 | floppy_deselect(current_drive); | ||
256 | end_request(0); | ||
257 | } else | ||
258 | bad_flp_intr(); | ||
259 | do_fd_request(); | ||
260 | return; | ||
261 | } | ||
262 | if (command == FD_READ && (unsigned long)(CURRENT->buffer) >= 0x100000) | ||
263 | copy_buffer(tmp_floppy_area,CURRENT->buffer); | ||
264 | floppy_deselect(current_drive); | ||
265 | end_request(1); | ||
266 | do_fd_request(); | ||
267 | } | ||
268 | |||
269 | static inline void setup_rw_floppy(void) | ||
270 | { | ||
271 | setup_DMA(); | ||
272 | do_floppy = rw_interrupt; | ||
273 | output_byte(command); | ||
274 | output_byte(head<<2 | current_drive); | ||
275 | output_byte(track); | ||
276 | output_byte(head); | ||
277 | output_byte(sector); | ||
278 | output_byte(2); /* sector size = 512 */ | ||
279 | output_byte(floppy->sect); | ||
280 | output_byte(floppy->gap); | ||
281 | output_byte(0xFF); /* sector size (0xff when n!=0 ?) */ | ||
282 | if (reset) | ||
283 | do_fd_request(); | ||
284 | } | ||
285 | |||
286 | /* | ||
287 | * This is the routine called after every seek (or recalibrate) interrupt | ||
288 | * from the floppy controller. Note that the "unexpected interrupt" routine | ||
289 | * also does a recalibrate, but doesn't come here. | ||
290 | */ | ||
291 | static void seek_interrupt(void) | ||
292 | { | ||
293 | /* sense drive status */ | ||
294 | output_byte(FD_SENSEI); | ||
295 | if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) { | ||
296 | bad_flp_intr(); | ||
297 | do_fd_request(); | ||
298 | return; | ||
299 | } | ||
300 | current_track = ST1; | ||
301 | setup_rw_floppy(); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * This routine is called when everything should be correctly set up | ||
306 | * for the transfer (ie floppy motor is on and the correct floppy is | ||
307 | * selected). | ||
308 | */ | ||
309 | static void transfer(void) | ||
310 | { | ||
311 | if (cur_spec1 != floppy->spec1) { | ||
312 | cur_spec1 = floppy->spec1; | ||
313 | output_byte(FD_SPECIFY); | ||
314 | output_byte(cur_spec1); /* hut etc */ | ||
315 | output_byte(6); /* Head load time =6ms, DMA */ | ||
316 | } | ||
317 | if (cur_rate != floppy->rate) | ||
318 | outb_p(cur_rate = floppy->rate,FD_DCR); | ||
319 | if (reset) { | ||
320 | do_fd_request(); | ||
321 | return; | ||
322 | } | ||
323 | if (!seek) { | ||
324 | setup_rw_floppy(); | ||
325 | return; | ||
326 | } | ||
327 | do_floppy = seek_interrupt; | ||
328 | if (seek_track) { | ||
329 | output_byte(FD_SEEK); | ||
330 | output_byte(head<<2 | current_drive); | ||
331 | output_byte(seek_track); | ||
332 | } else { | ||
333 | output_byte(FD_RECALIBRATE); | ||
334 | output_byte(head<<2 | current_drive); | ||
335 | } | ||
336 | if (reset) | ||
337 | do_fd_request(); | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * Special case - used after a unexpected interrupt (or reset) | ||
342 | */ | ||
343 | static void recal_interrupt(void) | ||
344 | { | ||
345 | output_byte(FD_SENSEI); | ||
346 | if (result()!=2 || (ST0 & 0xE0) == 0x60) | ||
347 | reset = 1; | ||
348 | else | ||
349 | recalibrate = 0; | ||
350 | do_fd_request(); | ||
351 | } | ||
352 | |||
353 | void unexpected_floppy_interrupt(void) | ||
354 | { | ||
355 | output_byte(FD_SENSEI); | ||
356 | if (result()!=2 || (ST0 & 0xE0) == 0x60) | ||
357 | reset = 1; | ||
358 | else | ||
359 | recalibrate = 1; | ||
360 | } | ||
361 | |||
362 | static void recalibrate_floppy(void) | ||
363 | { | ||
364 | recalibrate = 0; | ||
365 | current_track = 0; | ||
366 | do_floppy = recal_interrupt; | ||
367 | output_byte(FD_RECALIBRATE); | ||
368 | output_byte(head<<2 | current_drive); | ||
369 | if (reset) | ||
370 | do_fd_request(); | ||
371 | } | ||
372 | |||
373 | static void reset_interrupt(void) | ||
374 | { | ||
375 | output_byte(FD_SENSEI); | ||
376 | (void) result(); | ||
377 | output_byte(FD_SPECIFY); | ||
378 | output_byte(cur_spec1); /* hut etc */ | ||
379 | output_byte(6); /* Head load time =6ms, DMA */ | ||
380 | do_fd_request(); | ||
381 | } | ||
382 | |||
383 | /* | ||
384 | * reset is done by pulling bit 2 of DOR low for a while. | ||
385 | */ | ||
386 | static void reset_floppy(void) | ||
387 | { | ||
388 | int i; | ||
389 | |||
390 | reset = 0; | ||
391 | cur_spec1 = -1; | ||
392 | cur_rate = -1; | ||
393 | recalibrate = 1; | ||
394 | printk("Reset-floppy called\n\r"); | ||
395 | cli(); | ||
396 | do_floppy = reset_interrupt; | ||
397 | outb_p(current_DOR & ~0x04,FD_DOR); | ||
398 | for (i=0 ; i<100 ; i++) | ||
399 | __asm__("nop"); | ||
400 | outb(current_DOR,FD_DOR); | ||
401 | sti(); | ||
402 | } | ||
403 | |||
404 | static void floppy_on_interrupt(void) | ||
405 | { | ||
406 | /* We cannot do a floppy-select, as that might sleep. We just force it */ | ||
407 | selected = 1; | ||
408 | if (current_drive != (current_DOR & 3)) { | ||
409 | current_DOR &= 0xFC; | ||
410 | current_DOR |= current_drive; | ||
411 | outb(current_DOR,FD_DOR); | ||
412 | add_timer(2,&transfer); | ||
413 | } else | ||
414 | transfer(); | ||
415 | } | ||
416 | |||
417 | void do_fd_request(void) | ||
418 | { | ||
419 | unsigned int block; | ||
420 | |||
421 | seek = 0; | ||
422 | if (reset) { | ||
423 | reset_floppy(); | ||
424 | return; | ||
425 | } | ||
426 | if (recalibrate) { | ||
427 | recalibrate_floppy(); | ||
428 | return; | ||
429 | } | ||
430 | INIT_REQUEST; | ||
431 | floppy = (MINOR(CURRENT->dev)>>2) + floppy_type; | ||
432 | if (current_drive != CURRENT_DEV) | ||
433 | seek = 1; | ||
434 | current_drive = CURRENT_DEV; | ||
435 | block = CURRENT->sector; | ||
436 | if (block+2 > floppy->size) { | ||
437 | end_request(0); | ||
438 | goto repeat; | ||
439 | } | ||
440 | sector = block % floppy->sect; | ||
441 | block /= floppy->sect; | ||
442 | head = block % floppy->head; | ||
443 | track = block / floppy->head; | ||
444 | seek_track = track << floppy->stretch; | ||
445 | if (seek_track != current_track) | ||
446 | seek = 1; | ||
447 | sector++; | ||
448 | if (CURRENT->cmd == READ) | ||
449 | command = FD_READ; | ||
450 | else if (CURRENT->cmd == WRITE) | ||
451 | command = FD_WRITE; | ||
452 | else | ||
453 | panic("do_fd_request: unknown command"); | ||
454 | add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt); | ||
455 | } | ||
456 | |||
457 | void floppy_init(void) | ||
458 | { | ||
459 | blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; | ||
460 | set_trap_gate(0x26,&floppy_interrupt); | ||
461 | outb(inb_p(0x21)&~0x40,0x21); | ||
462 | } | ||
diff --git a/src/kernel/blk_drv/hd.c b/src/kernel/blk_drv/hd.c new file mode 100644 index 0000000..d5e1aa9 --- /dev/null +++ b/src/kernel/blk_drv/hd.c | |||
@@ -0,0 +1,349 @@ | |||
1 | /* | ||
2 | * linux/kernel/hd.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This is the low-level hd interrupt support. It traverses the | ||
9 | * request-list, using interrupts to jump between functions. As | ||
10 | * all the functions are called within interrupts, we may not | ||
11 | * sleep. Special care is recommended. | ||
12 | * | ||
13 | * modified by Drew Eckhardt to check nr of hd's from the CMOS. | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/hdreg.h> | ||
21 | #include <asm/system.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/segment.h> | ||
24 | |||
25 | #define MAJOR_NR 3 | ||
26 | #include "blk.h" | ||
27 | |||
28 | #define CMOS_READ(addr) ({ \ | ||
29 | outb_p(0x80|addr,0x70); \ | ||
30 | inb_p(0x71); \ | ||
31 | }) | ||
32 | |||
33 | /* Max read/write errors/sector */ | ||
34 | #define MAX_ERRORS 7 | ||
35 | #define MAX_HD 2 | ||
36 | |||
37 | static void recal_intr(void); | ||
38 | |||
39 | static int recalibrate = 0; | ||
40 | static int reset = 0; | ||
41 | |||
42 | /* | ||
43 | * This struct defines the HD's and their types. | ||
44 | */ | ||
45 | struct hd_i_struct { | ||
46 | int head,sect,cyl,wpcom,lzone,ctl; | ||
47 | }; | ||
48 | #ifdef HD_TYPE | ||
49 | struct hd_i_struct hd_info[] = { HD_TYPE }; | ||
50 | #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) | ||
51 | #else | ||
52 | struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} }; | ||
53 | static int NR_HD = 0; | ||
54 | #endif | ||
55 | |||
56 | static struct hd_struct { | ||
57 | long start_sect; | ||
58 | long nr_sects; | ||
59 | } hd[5*MAX_HD]={{0,0},}; | ||
60 | |||
61 | #define port_read(port,buf,nr) \ | ||
62 | __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr)) | ||
63 | |||
64 | #define port_write(port,buf,nr) \ | ||
65 | __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr)) | ||
66 | |||
67 | extern void hd_interrupt(void); | ||
68 | extern void rd_load(void); | ||
69 | |||
70 | /* This may be used only once, enforced by 'static int callable' */ | ||
71 | int sys_setup(void * BIOS) | ||
72 | { | ||
73 | static int callable = 1; | ||
74 | int i,drive; | ||
75 | unsigned char cmos_disks; | ||
76 | struct partition *p; | ||
77 | struct buffer_head * bh; | ||
78 | |||
79 | if (!callable) | ||
80 | return -1; | ||
81 | callable = 0; | ||
82 | #ifndef HD_TYPE | ||
83 | for (drive=0 ; drive<2 ; drive++) { | ||
84 | hd_info[drive].cyl = *(unsigned short *) BIOS; | ||
85 | hd_info[drive].head = *(unsigned char *) (2+BIOS); | ||
86 | hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); | ||
87 | hd_info[drive].ctl = *(unsigned char *) (8+BIOS); | ||
88 | hd_info[drive].lzone = *(unsigned short *) (12+BIOS); | ||
89 | hd_info[drive].sect = *(unsigned char *) (14+BIOS); | ||
90 | BIOS += 16; | ||
91 | } | ||
92 | if (hd_info[1].cyl) | ||
93 | NR_HD=2; | ||
94 | else | ||
95 | NR_HD=1; | ||
96 | #endif | ||
97 | for (i=0 ; i<NR_HD ; i++) { | ||
98 | hd[i*5].start_sect = 0; | ||
99 | hd[i*5].nr_sects = hd_info[i].head* | ||
100 | hd_info[i].sect*hd_info[i].cyl; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | We querry CMOS about hard disks : it could be that | ||
105 | we have a SCSI/ESDI/etc controller that is BIOS | ||
106 | compatable with ST-506, and thus showing up in our | ||
107 | BIOS table, but not register compatable, and therefore | ||
108 | not present in CMOS. | ||
109 | |||
110 | Furthurmore, we will assume that our ST-506 drives | ||
111 | <if any> are the primary drives in the system, and | ||
112 | the ones reflected as drive 1 or 2. | ||
113 | |||
114 | The first drive is stored in the high nibble of CMOS | ||
115 | byte 0x12, the second in the low nibble. This will be | ||
116 | either a 4 bit drive type or 0xf indicating use byte 0x19 | ||
117 | for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. | ||
118 | |||
119 | Needless to say, a non-zero value means we have | ||
120 | an AT controller hard disk for that drive. | ||
121 | |||
122 | |||
123 | */ | ||
124 | |||
125 | if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) | ||
126 | if (cmos_disks & 0x0f) | ||
127 | NR_HD = 2; | ||
128 | else | ||
129 | NR_HD = 1; | ||
130 | else | ||
131 | NR_HD = 0; | ||
132 | for (i = NR_HD ; i < 2 ; i++) { | ||
133 | hd[i*5].start_sect = 0; | ||
134 | hd[i*5].nr_sects = 0; | ||
135 | } | ||
136 | for (drive=0 ; drive<NR_HD ; drive++) { | ||
137 | if (!(bh = bread(0x300 + drive*5,0))) { | ||
138 | printk("Unable to read partition table of drive %d\n\r", | ||
139 | drive); | ||
140 | panic(""); | ||
141 | } | ||
142 | if (bh->b_data[510] != 0x55 || (unsigned char) | ||
143 | bh->b_data[511] != 0xAA) { | ||
144 | printk("Bad partition table on drive %d\n\r",drive); | ||
145 | panic(""); | ||
146 | } | ||
147 | p = 0x1BE + (void *)bh->b_data; | ||
148 | for (i=1;i<5;i++,p++) { | ||
149 | hd[i+5*drive].start_sect = p->start_sect; | ||
150 | hd[i+5*drive].nr_sects = p->nr_sects; | ||
151 | } | ||
152 | brelse(bh); | ||
153 | } | ||
154 | if (NR_HD) | ||
155 | printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":""); | ||
156 | rd_load(); | ||
157 | mount_root(); | ||
158 | return (0); | ||
159 | } | ||
160 | |||
161 | static int controller_ready(void) | ||
162 | { | ||
163 | int retries=100000; | ||
164 | |||
165 | while (--retries && (inb_p(HD_STATUS)&0x80)); | ||
166 | return (retries); | ||
167 | } | ||
168 | |||
169 | static int win_result(void) | ||
170 | { | ||
171 | int i=inb_p(HD_STATUS); | ||
172 | |||
173 | if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)) | ||
174 | == (READY_STAT | SEEK_STAT)) | ||
175 | return(0); /* ok */ | ||
176 | if (i&1) i=inb(HD_ERROR); | ||
177 | return (1); | ||
178 | } | ||
179 | |||
180 | static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, | ||
181 | unsigned int head,unsigned int cyl,unsigned int cmd, | ||
182 | void (*intr_addr)(void)) | ||
183 | { | ||
184 | register int port asm("dx"); | ||
185 | |||
186 | if (drive>1 || head>15) | ||
187 | panic("Trying to write bad sector"); | ||
188 | if (!controller_ready()) | ||
189 | panic("HD controller not ready"); | ||
190 | do_hd = intr_addr; | ||
191 | outb_p(hd_info[drive].ctl,HD_CMD); | ||
192 | port=HD_DATA; | ||
193 | outb_p(hd_info[drive].wpcom>>2,++port); | ||
194 | outb_p(nsect,++port); | ||
195 | outb_p(sect,++port); | ||
196 | outb_p(cyl,++port); | ||
197 | outb_p(cyl>>8,++port); | ||
198 | outb_p(0xA0|(drive<<4)|head,++port); | ||
199 | outb(cmd,++port); | ||
200 | } | ||
201 | |||
202 | static int drive_busy(void) | ||
203 | { | ||
204 | unsigned int i; | ||
205 | |||
206 | for (i = 0; i < 10000; i++) | ||
207 | if (READY_STAT == (inb_p(HD_STATUS) & (BUSY_STAT|READY_STAT))) | ||
208 | break; | ||
209 | i = inb(HD_STATUS); | ||
210 | i &= BUSY_STAT | READY_STAT | SEEK_STAT; | ||
211 | if (i == (READY_STAT | SEEK_STAT)) | ||
212 | return(0); | ||
213 | printk("HD controller times out\n\r"); | ||
214 | return(1); | ||
215 | } | ||
216 | |||
217 | static void reset_controller(void) | ||
218 | { | ||
219 | int i; | ||
220 | |||
221 | outb(4,HD_CMD); | ||
222 | for(i = 0; i < 100; i++) nop(); | ||
223 | outb(hd_info[0].ctl & 0x0f ,HD_CMD); | ||
224 | if (drive_busy()) | ||
225 | printk("HD-controller still busy\n\r"); | ||
226 | if ((i = inb(HD_ERROR)) != 1) | ||
227 | printk("HD-controller reset failed: %02x\n\r",i); | ||
228 | } | ||
229 | |||
230 | static void reset_hd(int nr) | ||
231 | { | ||
232 | reset_controller(); | ||
233 | hd_out(nr,hd_info[nr].sect,hd_info[nr].sect,hd_info[nr].head-1, | ||
234 | hd_info[nr].cyl,WIN_SPECIFY,&recal_intr); | ||
235 | } | ||
236 | |||
237 | void unexpected_hd_interrupt(void) | ||
238 | { | ||
239 | printk("Unexpected HD interrupt\n\r"); | ||
240 | } | ||
241 | |||
242 | static void bad_rw_intr(void) | ||
243 | { | ||
244 | if (++CURRENT->errors >= MAX_ERRORS) | ||
245 | end_request(0); | ||
246 | if (CURRENT->errors > MAX_ERRORS/2) | ||
247 | reset = 1; | ||
248 | } | ||
249 | |||
250 | static void read_intr(void) | ||
251 | { | ||
252 | if (win_result()) { | ||
253 | bad_rw_intr(); | ||
254 | do_hd_request(); | ||
255 | return; | ||
256 | } | ||
257 | port_read(HD_DATA,CURRENT->buffer,256); | ||
258 | CURRENT->errors = 0; | ||
259 | CURRENT->buffer += 512; | ||
260 | CURRENT->sector++; | ||
261 | if (--CURRENT->nr_sectors) { | ||
262 | do_hd = &read_intr; | ||
263 | return; | ||
264 | } | ||
265 | end_request(1); | ||
266 | do_hd_request(); | ||
267 | } | ||
268 | |||
269 | static void write_intr(void) | ||
270 | { | ||
271 | if (win_result()) { | ||
272 | bad_rw_intr(); | ||
273 | do_hd_request(); | ||
274 | return; | ||
275 | } | ||
276 | if (--CURRENT->nr_sectors) { | ||
277 | CURRENT->sector++; | ||
278 | CURRENT->buffer += 512; | ||
279 | do_hd = &write_intr; | ||
280 | port_write(HD_DATA,CURRENT->buffer,256); | ||
281 | return; | ||
282 | } | ||
283 | end_request(1); | ||
284 | do_hd_request(); | ||
285 | } | ||
286 | |||
287 | static void recal_intr(void) | ||
288 | { | ||
289 | if (win_result()) | ||
290 | bad_rw_intr(); | ||
291 | do_hd_request(); | ||
292 | } | ||
293 | |||
294 | void do_hd_request(void) | ||
295 | { | ||
296 | int i,r = 0; | ||
297 | unsigned int block,dev; | ||
298 | unsigned int sec,head,cyl; | ||
299 | unsigned int nsect; | ||
300 | |||
301 | INIT_REQUEST; | ||
302 | dev = MINOR(CURRENT->dev); | ||
303 | block = CURRENT->sector; | ||
304 | if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) { | ||
305 | end_request(0); | ||
306 | goto repeat; | ||
307 | } | ||
308 | block += hd[dev].start_sect; | ||
309 | dev /= 5; | ||
310 | __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0), | ||
311 | "r" (hd_info[dev].sect)); | ||
312 | __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0), | ||
313 | "r" (hd_info[dev].head)); | ||
314 | sec++; | ||
315 | nsect = CURRENT->nr_sectors; | ||
316 | if (reset) { | ||
317 | reset = 0; | ||
318 | recalibrate = 1; | ||
319 | reset_hd(CURRENT_DEV); | ||
320 | return; | ||
321 | } | ||
322 | if (recalibrate) { | ||
323 | recalibrate = 0; | ||
324 | hd_out(dev,hd_info[CURRENT_DEV].sect,0,0,0, | ||
325 | WIN_RESTORE,&recal_intr); | ||
326 | return; | ||
327 | } | ||
328 | if (CURRENT->cmd == WRITE) { | ||
329 | hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr); | ||
330 | for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++) | ||
331 | /* nothing */ ; | ||
332 | if (!r) { | ||
333 | bad_rw_intr(); | ||
334 | goto repeat; | ||
335 | } | ||
336 | port_write(HD_DATA,CURRENT->buffer,256); | ||
337 | } else if (CURRENT->cmd == READ) { | ||
338 | hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); | ||
339 | } else | ||
340 | panic("unknown hd-command"); | ||
341 | } | ||
342 | |||
343 | void hd_init(void) | ||
344 | { | ||
345 | blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; | ||
346 | set_intr_gate(0x2E,&hd_interrupt); | ||
347 | outb_p(inb_p(0x21)&0xfb,0x21); | ||
348 | outb(inb_p(0xA1)&0xbf,0xA1); | ||
349 | } | ||
diff --git a/src/kernel/blk_drv/ll_rw_blk.c b/src/kernel/blk_drv/ll_rw_blk.c new file mode 100644 index 0000000..8931a81 --- /dev/null +++ b/src/kernel/blk_drv/ll_rw_blk.c | |||
@@ -0,0 +1,165 @@ | |||
1 | /* | ||
2 | * linux/kernel/blk_dev/ll_rw.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This handles all read/write requests to block devices | ||
9 | */ | ||
10 | #include <errno.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <asm/system.h> | ||
14 | |||
15 | #include "blk.h" | ||
16 | |||
17 | /* | ||
18 | * The request-struct contains all necessary data | ||
19 | * to load a nr of sectors into memory | ||
20 | */ | ||
21 | struct request request[NR_REQUEST]; | ||
22 | |||
23 | /* | ||
24 | * used to wait on when there are no free requests | ||
25 | */ | ||
26 | struct task_struct * wait_for_request = NULL; | ||
27 | |||
28 | /* blk_dev_struct is: | ||
29 | * do_request-address | ||
30 | * next-request | ||
31 | */ | ||
32 | struct blk_dev_struct blk_dev[NR_BLK_DEV] = { | ||
33 | { NULL, NULL }, /* no_dev */ | ||
34 | { NULL, NULL }, /* dev mem */ | ||
35 | { NULL, NULL }, /* dev fd */ | ||
36 | { NULL, NULL }, /* dev hd */ | ||
37 | { NULL, NULL }, /* dev ttyx */ | ||
38 | { NULL, NULL }, /* dev tty */ | ||
39 | { NULL, NULL } /* dev lp */ | ||
40 | }; | ||
41 | |||
42 | static inline void lock_buffer(struct buffer_head * bh) | ||
43 | { | ||
44 | cli(); | ||
45 | while (bh->b_lock) | ||
46 | sleep_on(&bh->b_wait); | ||
47 | bh->b_lock=1; | ||
48 | sti(); | ||
49 | } | ||
50 | |||
51 | static inline void unlock_buffer(struct buffer_head * bh) | ||
52 | { | ||
53 | if (!bh->b_lock) | ||
54 | printk("ll_rw_block.c: buffer not locked\n\r"); | ||
55 | bh->b_lock = 0; | ||
56 | wake_up(&bh->b_wait); | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * add-request adds a request to the linked list. | ||
61 | * It disables interrupts so that it can muck with the | ||
62 | * request-lists in peace. | ||
63 | */ | ||
64 | static void add_request(struct blk_dev_struct * dev, struct request * req) | ||
65 | { | ||
66 | struct request * tmp; | ||
67 | |||
68 | req->next = NULL; | ||
69 | cli(); | ||
70 | if (req->bh) | ||
71 | req->bh->b_dirt = 0; | ||
72 | if (!(tmp = dev->current_request)) { | ||
73 | dev->current_request = req; | ||
74 | sti(); | ||
75 | (dev->request_fn)(); | ||
76 | return; | ||
77 | } | ||
78 | for ( ; tmp->next ; tmp=tmp->next) | ||
79 | if ((IN_ORDER(tmp,req) || | ||
80 | !IN_ORDER(tmp,tmp->next)) && | ||
81 | IN_ORDER(req,tmp->next)) | ||
82 | break; | ||
83 | req->next=tmp->next; | ||
84 | tmp->next=req; | ||
85 | sti(); | ||
86 | } | ||
87 | |||
88 | static void make_request(int major,int rw, struct buffer_head * bh) | ||
89 | { | ||
90 | struct request * req; | ||
91 | int rw_ahead; | ||
92 | |||
93 | /* WRITEA/READA is special case - it is not really needed, so if the */ | ||
94 | /* buffer is locked, we just forget about it, else it's a normal read */ | ||
95 | if ((rw_ahead = (rw == READA || rw == WRITEA))) { | ||
96 | if (bh->b_lock) | ||
97 | return; | ||
98 | if (rw == READA) | ||
99 | rw = READ; | ||
100 | else | ||
101 | rw = WRITE; | ||
102 | } | ||
103 | if (rw!=READ && rw!=WRITE) | ||
104 | panic("Bad block dev command, must be R/W/RA/WA"); | ||
105 | lock_buffer(bh); | ||
106 | if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) { | ||
107 | unlock_buffer(bh); | ||
108 | return; | ||
109 | } | ||
110 | repeat: | ||
111 | /* we don't allow the write-requests to fill up the queue completely: | ||
112 | * we want some room for reads: they take precedence. The last third | ||
113 | * of the requests are only for reads. | ||
114 | */ | ||
115 | if (rw == READ) | ||
116 | req = request+NR_REQUEST; | ||
117 | else | ||
118 | req = request+((NR_REQUEST*2)/3); | ||
119 | /* find an empty request */ | ||
120 | while (--req >= request) | ||
121 | if (req->dev<0) | ||
122 | break; | ||
123 | /* if none found, sleep on new requests: check for rw_ahead */ | ||
124 | if (req < request) { | ||
125 | if (rw_ahead) { | ||
126 | unlock_buffer(bh); | ||
127 | return; | ||
128 | } | ||
129 | sleep_on(&wait_for_request); | ||
130 | goto repeat; | ||
131 | } | ||
132 | /* fill up the request-info, and add it to the queue */ | ||
133 | req->dev = bh->b_dev; | ||
134 | req->cmd = rw; | ||
135 | req->errors=0; | ||
136 | req->sector = bh->b_blocknr<<1; | ||
137 | req->nr_sectors = 2; | ||
138 | req->buffer = bh->b_data; | ||
139 | req->waiting = NULL; | ||
140 | req->bh = bh; | ||
141 | req->next = NULL; | ||
142 | add_request(major+blk_dev,req); | ||
143 | } | ||
144 | |||
145 | void ll_rw_block(int rw, struct buffer_head * bh) | ||
146 | { | ||
147 | unsigned int major; | ||
148 | |||
149 | if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || | ||
150 | !(blk_dev[major].request_fn)) { | ||
151 | printk("Trying to read nonexistent block-device\n\r"); | ||
152 | return; | ||
153 | } | ||
154 | make_request(major,rw,bh); | ||
155 | } | ||
156 | |||
157 | void blk_dev_init(void) | ||
158 | { | ||
159 | int i; | ||
160 | |||
161 | for (i=0 ; i<NR_REQUEST ; i++) { | ||
162 | request[i].dev = -1; | ||
163 | request[i].next = NULL; | ||
164 | } | ||
165 | } | ||
diff --git a/src/kernel/blk_drv/ramdisk.c b/src/kernel/blk_drv/ramdisk.c new file mode 100644 index 0000000..dc99f7c --- /dev/null +++ b/src/kernel/blk_drv/ramdisk.c | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * linux/kernel/blk_drv/ramdisk.c | ||
3 | * | ||
4 | * Written by Theodore Ts'o, 12/2/91 | ||
5 | */ | ||
6 | |||
7 | #include <string.h> | ||
8 | |||
9 | #include <linux/config.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include <linux/fs.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <asm/system.h> | ||
14 | #include <asm/segment.h> | ||
15 | #include <asm/memory.h> | ||
16 | |||
17 | #define MAJOR_NR 1 | ||
18 | #include "blk.h" | ||
19 | |||
20 | char *rd_start; | ||
21 | int rd_length = 0; | ||
22 | |||
23 | void do_rd_request(void) | ||
24 | { | ||
25 | int len; | ||
26 | char *addr; | ||
27 | |||
28 | INIT_REQUEST; | ||
29 | addr = rd_start + (CURRENT->sector << 9); | ||
30 | len = CURRENT->nr_sectors << 9; | ||
31 | if ((MINOR(CURRENT->dev) != 1) || (addr+len > rd_start+rd_length)) { | ||
32 | end_request(0); | ||
33 | goto repeat; | ||
34 | } | ||
35 | if (CURRENT-> cmd == WRITE) { | ||
36 | (void ) memcpy(addr, | ||
37 | CURRENT->buffer, | ||
38 | len); | ||
39 | } else if (CURRENT->cmd == READ) { | ||
40 | (void) memcpy(CURRENT->buffer, | ||
41 | addr, | ||
42 | len); | ||
43 | } else | ||
44 | panic("unknown ramdisk-command"); | ||
45 | end_request(1); | ||
46 | goto repeat; | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * Returns amount of memory which needs to be reserved. | ||
51 | */ | ||
52 | long rd_init(long mem_start, int length) | ||
53 | { | ||
54 | int i; | ||
55 | char *cp; | ||
56 | |||
57 | blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; | ||
58 | rd_start = (char *) mem_start; | ||
59 | rd_length = length; | ||
60 | cp = rd_start; | ||
61 | for (i=0; i < length; i++) | ||
62 | *cp++ = '\0'; | ||
63 | return(length); | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * If the root device is the ram disk, try to load it. | ||
68 | * In order to do this, the root device is originally set to the | ||
69 | * floppy, and we later change it to be ram disk. | ||
70 | */ | ||
71 | void rd_load(void) | ||
72 | { | ||
73 | struct buffer_head *bh; | ||
74 | struct super_block s; | ||
75 | int block = 256; /* Start at block 256 */ | ||
76 | int i = 1; | ||
77 | int nblocks; | ||
78 | char *cp; /* Move pointer */ | ||
79 | |||
80 | if (!rd_length) | ||
81 | return; | ||
82 | printk("Ram disk: %d bytes, starting at 0x%x\n", rd_length, | ||
83 | (int) rd_start); | ||
84 | if (MAJOR(ROOT_DEV) != 2) | ||
85 | return; | ||
86 | bh = breada(ROOT_DEV,block+1,block,block+2,-1); | ||
87 | if (!bh) { | ||
88 | printk("Disk error while looking for ramdisk!\n"); | ||
89 | return; | ||
90 | } | ||
91 | *((struct d_super_block *) &s) = *((struct d_super_block *) bh->b_data); | ||
92 | brelse(bh); | ||
93 | if (s.s_magic != SUPER_MAGIC) | ||
94 | /* No ram disk image present, assume normal floppy boot */ | ||
95 | return; | ||
96 | nblocks = s.s_nzones << s.s_log_zone_size; | ||
97 | if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) { | ||
98 | printk("Ram disk image too big! (%d blocks, %d avail)\n", | ||
99 | nblocks, rd_length >> BLOCK_SIZE_BITS); | ||
100 | return; | ||
101 | } | ||
102 | printk("Loading %d bytes into ram disk... 0000k", | ||
103 | nblocks << BLOCK_SIZE_BITS); | ||
104 | cp = rd_start; | ||
105 | while (nblocks) { | ||
106 | if (nblocks > 2) | ||
107 | bh = breada(ROOT_DEV, block, block+1, block+2, -1); | ||
108 | else | ||
109 | bh = bread(ROOT_DEV, block); | ||
110 | if (!bh) { | ||
111 | printk("I/O error on block %d, aborting load\n", | ||
112 | block); | ||
113 | return; | ||
114 | } | ||
115 | (void) memcpy(cp, bh->b_data, BLOCK_SIZE); | ||
116 | brelse(bh); | ||
117 | printk("\010\010\010\010\010%4dk",i); | ||
118 | cp += BLOCK_SIZE; | ||
119 | block++; | ||
120 | nblocks--; | ||
121 | i++; | ||
122 | } | ||
123 | printk("\010\010\010\010\010done \n"); | ||
124 | ROOT_DEV=0x0101; | ||
125 | } | ||
diff --git a/src/kernel/chr_drv/Makefile b/src/kernel/chr_drv/Makefile new file mode 100644 index 0000000..3dd5449 --- /dev/null +++ b/src/kernel/chr_drv/Makefile | |||
@@ -0,0 +1,67 @@ | |||
1 | # | ||
2 | # Makefile for the FREAX-kernel character device drivers. | ||
3 | # | ||
4 | # Note! Dependencies are done automagically by 'make dep', which also | ||
5 | # removes any old dependencies. DON'T put your own dependencies here | ||
6 | # unless it's something special (ie not a .c file). | ||
7 | # | ||
8 | |||
9 | include ../../Makefile.header | ||
10 | |||
11 | CFLAGS += -I../../include | ||
12 | CPP += -I../../include | ||
13 | |||
14 | .c.s: | ||
15 | @$(CC) $(CFLAGS) \ | ||
16 | -S -o $*.s $< | ||
17 | .s.o: | ||
18 | @$(AS) -o $*.o $< | ||
19 | .c.o: | ||
20 | @$(CC) $(CFLAGS) \ | ||
21 | -c -o $*.o $< | ||
22 | |||
23 | OBJS = tty_io.o console.o keyboard.o serial.o rs_io.o \ | ||
24 | tty_ioctl.o | ||
25 | |||
26 | chr_drv.a: $(OBJS) | ||
27 | @$(AR) rcs chr_drv.a $(OBJS) | ||
28 | sync | ||
29 | |||
30 | keyboard.s: kb.S ../../include/linux/config.h | ||
31 | @$(CPP) kb.S -o keyboard.s | ||
32 | |||
33 | clean: | ||
34 | @rm -f core *.o *.a tmp_make keyboard.s | ||
35 | @for i in *.c;do rm -f `basename $$i .c`.s;done | ||
36 | |||
37 | dep: | ||
38 | @sed '/\#\#\# Dependencies/q' < Makefile > tmp_make | ||
39 | @(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ | ||
40 | $(CPP) -M $$i;done) >> tmp_make | ||
41 | @cp tmp_make Makefile | ||
42 | |||
43 | ### Dependencies: | ||
44 | console.s console.o: console.c ../../include/linux/sched.h \ | ||
45 | ../../include/linux/head.h ../../include/linux/fs.h \ | ||
46 | ../../include/sys/types.h ../../include/linux/mm.h \ | ||
47 | ../../include/signal.h ../../include/linux/tty.h \ | ||
48 | ../../include/termios.h ../../include/asm/io.h \ | ||
49 | ../../include/asm/system.h | ||
50 | serial.s serial.o: serial.c ../../include/linux/tty.h ../../include/termios.h \ | ||
51 | ../../include/linux/sched.h ../../include/linux/head.h \ | ||
52 | ../../include/linux/fs.h ../../include/sys/types.h \ | ||
53 | ../../include/linux/mm.h ../../include/signal.h \ | ||
54 | ../../include/asm/system.h ../../include/asm/io.h | ||
55 | tty_io.s tty_io.o: tty_io.c ../../include/ctype.h ../../include/errno.h \ | ||
56 | ../../include/signal.h ../../include/sys/types.h \ | ||
57 | ../../include/linux/sched.h ../../include/linux/head.h \ | ||
58 | ../../include/linux/fs.h ../../include/linux/mm.h \ | ||
59 | ../../include/linux/tty.h ../../include/termios.h \ | ||
60 | ../../include/asm/segment.h ../../include/asm/system.h | ||
61 | tty_ioctl.s tty_ioctl.o: tty_ioctl.c ../../include/errno.h ../../include/termios.h \ | ||
62 | ../../include/linux/sched.h ../../include/linux/head.h \ | ||
63 | ../../include/linux/fs.h ../../include/sys/types.h \ | ||
64 | ../../include/linux/mm.h ../../include/signal.h \ | ||
65 | ../../include/linux/kernel.h ../../include/linux/tty.h \ | ||
66 | ../../include/asm/io.h ../../include/asm/segment.h \ | ||
67 | ../../include/asm/system.h | ||
diff --git a/src/kernel/chr_drv/console.c b/src/kernel/chr_drv/console.c new file mode 100644 index 0000000..2529569 --- /dev/null +++ b/src/kernel/chr_drv/console.c | |||
@@ -0,0 +1,710 @@ | |||
1 | /* | ||
2 | * linux/kernel/console.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * console.c | ||
9 | * | ||
10 | * This module implements the console io functions | ||
11 | * 'void con_init(void)' | ||
12 | * 'void con_write(struct tty_queue * queue)' | ||
13 | * Hopefully this will be a rather complete VT102 implementation. | ||
14 | * | ||
15 | * Beeping thanks to John T Kohl. | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | * NOTE!!! We sometimes disable and enable interrupts for a short while | ||
20 | * (to put a word in video IO), but this will work even for keyboard | ||
21 | * interrupts. We know interrupts aren't enabled when getting a keyboard | ||
22 | * interrupt, as we use trap-gates. Hopefully all is well. | ||
23 | */ | ||
24 | |||
25 | /* | ||
26 | * Code to check for different video-cards mostly by Galen Hunt, | ||
27 | * <g-hunt@ee.utah.edu> | ||
28 | */ | ||
29 | |||
30 | #include <linux/sched.h> | ||
31 | #include <linux/tty.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <asm/system.h> | ||
34 | |||
35 | /* | ||
36 | * These are set up by the setup-routine at boot-time: | ||
37 | */ | ||
38 | |||
39 | #define ORIG_X (*(unsigned char *)0x90000) | ||
40 | #define ORIG_Y (*(unsigned char *)0x90001) | ||
41 | #define ORIG_VIDEO_PAGE (*(unsigned short *)0x90004) | ||
42 | #define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff) | ||
43 | #define ORIG_VIDEO_COLS (((*(unsigned short *)0x90006) & 0xff00) >> 8) | ||
44 | #define ORIG_VIDEO_LINES (25) | ||
45 | #define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008) | ||
46 | #define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a) | ||
47 | #define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c) | ||
48 | |||
49 | #define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */ | ||
50 | #define VIDEO_TYPE_CGA 0x11 /* CGA Display */ | ||
51 | #define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */ | ||
52 | #define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode */ | ||
53 | |||
54 | #define NPAR 16 | ||
55 | |||
56 | extern void keyboard_interrupt(void); | ||
57 | |||
58 | static unsigned char video_type; /* Type of display being used */ | ||
59 | static unsigned long video_num_columns; /* Number of text columns */ | ||
60 | static unsigned long video_size_row; /* Bytes per row */ | ||
61 | static unsigned long video_num_lines; /* Number of test lines */ | ||
62 | static unsigned char video_page; /* Initial video page */ | ||
63 | static unsigned long video_mem_start; /* Start of video RAM */ | ||
64 | static unsigned long video_mem_end; /* End of video RAM (sort of) */ | ||
65 | static unsigned short video_port_reg; /* Video register select port */ | ||
66 | static unsigned short video_port_val; /* Video register value port */ | ||
67 | static unsigned short video_erase_char; /* Char+Attrib to erase with */ | ||
68 | |||
69 | static unsigned long origin; /* Used for EGA/VGA fast scroll */ | ||
70 | static unsigned long scr_end; /* Used for EGA/VGA fast scroll */ | ||
71 | static unsigned long pos; | ||
72 | static unsigned long x,y; | ||
73 | static unsigned long top,bottom; | ||
74 | static unsigned long state=0; | ||
75 | static unsigned long npar,par[NPAR]; | ||
76 | static unsigned long ques=0; | ||
77 | static unsigned char attr=0x07; | ||
78 | |||
79 | static void sysbeep(void); | ||
80 | |||
81 | /* | ||
82 | * this is what the terminal answers to a ESC-Z or csi0c | ||
83 | * query (= vt100 response). | ||
84 | */ | ||
85 | #define RESPONSE "\033[?1;2c" | ||
86 | |||
87 | /* NOTE! gotoxy thinks x==video_num_columns is ok */ | ||
88 | static inline void gotoxy(unsigned int new_x,unsigned int new_y) | ||
89 | { | ||
90 | if (new_x > video_num_columns || new_y >= video_num_lines) | ||
91 | return; | ||
92 | x=new_x; | ||
93 | y=new_y; | ||
94 | pos=origin + y*video_size_row + (x<<1); | ||
95 | } | ||
96 | |||
97 | static inline void set_origin(void) | ||
98 | { | ||
99 | cli(); | ||
100 | outb_p(12, video_port_reg); | ||
101 | outb_p(0xff&((origin-video_mem_start)>>9), video_port_val); | ||
102 | outb_p(13, video_port_reg); | ||
103 | outb_p(0xff&((origin-video_mem_start)>>1), video_port_val); | ||
104 | sti(); | ||
105 | } | ||
106 | |||
107 | static void scrup(void) | ||
108 | { | ||
109 | if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM) | ||
110 | { | ||
111 | if (!top && bottom == video_num_lines) { | ||
112 | origin += video_size_row; | ||
113 | pos += video_size_row; | ||
114 | scr_end += video_size_row; | ||
115 | if (scr_end > video_mem_end) { | ||
116 | __asm__("cld\n\t" | ||
117 | "rep\n\t" | ||
118 | "movsl\n\t" | ||
119 | "movl video_num_columns,%1\n\t" | ||
120 | "rep\n\t" | ||
121 | "stosw" | ||
122 | ::"a" (video_erase_char), | ||
123 | "c" ((video_num_lines-1)*video_num_columns>>1), | ||
124 | "D" (video_mem_start), | ||
125 | "S" (origin) | ||
126 | ); | ||
127 | scr_end -= origin-video_mem_start; | ||
128 | pos -= origin-video_mem_start; | ||
129 | origin = video_mem_start; | ||
130 | } else { | ||
131 | __asm__("cld\n\t" | ||
132 | "rep\n\t" | ||
133 | "stosw" | ||
134 | ::"a" (video_erase_char), | ||
135 | "c" (video_num_columns), | ||
136 | "D" (scr_end-video_size_row) | ||
137 | ); | ||
138 | } | ||
139 | set_origin(); | ||
140 | } else { | ||
141 | __asm__("cld\n\t" | ||
142 | "rep\n\t" | ||
143 | "movsl\n\t" | ||
144 | "movl video_num_columns,%%ecx\n\t" | ||
145 | "rep\n\t" | ||
146 | "stosw" | ||
147 | ::"a" (video_erase_char), | ||
148 | "c" ((bottom-top-1)*video_num_columns>>1), | ||
149 | "D" (origin+video_size_row*top), | ||
150 | "S" (origin+video_size_row*(top+1)) | ||
151 | ); | ||
152 | } | ||
153 | } | ||
154 | else /* Not EGA/VGA */ | ||
155 | { | ||
156 | __asm__("cld\n\t" | ||
157 | "rep\n\t" | ||
158 | "movsl\n\t" | ||
159 | "movl video_num_columns,%%ecx\n\t" | ||
160 | "rep\n\t" | ||
161 | "stosw" | ||
162 | ::"a" (video_erase_char), | ||
163 | "c" ((bottom-top-1)*video_num_columns>>1), | ||
164 | "D" (origin+video_size_row*top), | ||
165 | "S" (origin+video_size_row*(top+1)) | ||
166 | ); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | static void scrdown(void) | ||
171 | { | ||
172 | if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM) | ||
173 | { | ||
174 | __asm__("std\n\t" | ||
175 | "rep\n\t" | ||
176 | "movsl\n\t" | ||
177 | "addl $2,%%edi\n\t" /* %edi has been decremented by 4 */ | ||
178 | "movl video_num_columns,%%ecx\n\t" | ||
179 | "rep\n\t" | ||
180 | "stosw" | ||
181 | ::"a" (video_erase_char), | ||
182 | "c" ((bottom-top-1)*video_num_columns>>1), | ||
183 | "D" (origin+video_size_row*bottom-4), | ||
184 | "S" (origin+video_size_row*(bottom-1)-4) | ||
185 | ); | ||
186 | } | ||
187 | else /* Not EGA/VGA */ | ||
188 | { | ||
189 | __asm__("std\n\t" | ||
190 | "rep\n\t" | ||
191 | "movsl\n\t" | ||
192 | "addl $2,%%edi\n\t" /* %edi has been decremented by 4 */ | ||
193 | "movl video_num_columns,%%ecx\n\t" | ||
194 | "rep\n\t" | ||
195 | "stosw" | ||
196 | ::"a" (video_erase_char), | ||
197 | "c" ((bottom-top-1)*video_num_columns>>1), | ||
198 | "D" (origin+video_size_row*bottom-4), | ||
199 | "S" (origin+video_size_row*(bottom-1)-4) | ||
200 | ); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | static void lf(void) | ||
205 | { | ||
206 | if (y+1<bottom) { | ||
207 | y++; | ||
208 | pos += video_size_row; | ||
209 | return; | ||
210 | } | ||
211 | scrup(); | ||
212 | } | ||
213 | |||
214 | static void ri(void) | ||
215 | { | ||
216 | if (y>top) { | ||
217 | y--; | ||
218 | pos -= video_size_row; | ||
219 | return; | ||
220 | } | ||
221 | scrdown(); | ||
222 | } | ||
223 | |||
224 | static void cr(void) | ||
225 | { | ||
226 | pos -= x<<1; | ||
227 | x=0; | ||
228 | } | ||
229 | |||
230 | static void del(void) | ||
231 | { | ||
232 | if (x) { | ||
233 | pos -= 2; | ||
234 | x--; | ||
235 | *(unsigned short *)pos = video_erase_char; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | static void csi_J(int par) | ||
240 | { | ||
241 | long count; | ||
242 | long start; | ||
243 | |||
244 | switch (par) { | ||
245 | case 0: /* erase from cursor to end of display */ | ||
246 | count = (scr_end-pos)>>1; | ||
247 | start = pos; | ||
248 | break; | ||
249 | case 1: /* erase from start to cursor */ | ||
250 | count = (pos-origin)>>1; | ||
251 | start = origin; | ||
252 | break; | ||
253 | case 2: /* erase whole display */ | ||
254 | count = video_num_columns * video_num_lines; | ||
255 | start = origin; | ||
256 | break; | ||
257 | default: | ||
258 | return; | ||
259 | } | ||
260 | __asm__("cld\n\t" | ||
261 | "rep\n\t" | ||
262 | "stosw\n\t" | ||
263 | ::"c" (count), | ||
264 | "D" (start),"a" (video_erase_char) | ||
265 | ); | ||
266 | } | ||
267 | |||
268 | static void csi_K(int par) | ||
269 | { | ||
270 | long count; | ||
271 | long start; | ||
272 | |||
273 | switch (par) { | ||
274 | case 0: /* erase from cursor to end of line */ | ||
275 | if (x>=video_num_columns) | ||
276 | return; | ||
277 | count = video_num_columns-x; | ||
278 | start = pos; | ||
279 | break; | ||
280 | case 1: /* erase from start of line to cursor */ | ||
281 | start = pos - (x<<1); | ||
282 | count = (x<video_num_columns)?x:video_num_columns; | ||
283 | break; | ||
284 | case 2: /* erase whole line */ | ||
285 | start = pos - (x<<1); | ||
286 | count = video_num_columns; | ||
287 | break; | ||
288 | default: | ||
289 | return; | ||
290 | } | ||
291 | __asm__("cld\n\t" | ||
292 | "rep\n\t" | ||
293 | "stosw\n\t" | ||
294 | ::"c" (count), | ||
295 | "D" (start),"a" (video_erase_char) | ||
296 | ); | ||
297 | } | ||
298 | |||
299 | void csi_m(void) | ||
300 | { | ||
301 | int i; | ||
302 | |||
303 | for (i=0;i<=npar;i++) | ||
304 | switch (par[i]) { | ||
305 | case 0:attr=0x07;break; | ||
306 | case 1:attr=0x0f;break; | ||
307 | case 4:attr=0x0f;break; | ||
308 | case 7:attr=0x70;break; | ||
309 | case 27:attr=0x07;break; | ||
310 | } | ||
311 | } | ||
312 | |||
313 | static inline void set_cursor(void) | ||
314 | { | ||
315 | cli(); | ||
316 | outb_p(14, video_port_reg); | ||
317 | outb_p(0xff&((pos-video_mem_start)>>9), video_port_val); | ||
318 | outb_p(15, video_port_reg); | ||
319 | outb_p(0xff&((pos-video_mem_start)>>1), video_port_val); | ||
320 | sti(); | ||
321 | } | ||
322 | |||
323 | static void respond(struct tty_struct * tty) | ||
324 | { | ||
325 | char * p = RESPONSE; | ||
326 | |||
327 | cli(); | ||
328 | while (*p) { | ||
329 | PUTCH(*p,tty->read_q); | ||
330 | p++; | ||
331 | } | ||
332 | sti(); | ||
333 | copy_to_cooked(tty); | ||
334 | } | ||
335 | |||
336 | static void insert_char(void) | ||
337 | { | ||
338 | int i=x; | ||
339 | unsigned short tmp, old = video_erase_char; | ||
340 | unsigned short * p = (unsigned short *) pos; | ||
341 | |||
342 | while (i++<video_num_columns) { | ||
343 | tmp=*p; | ||
344 | *p=old; | ||
345 | old=tmp; | ||
346 | p++; | ||
347 | } | ||
348 | } | ||
349 | |||
350 | static void insert_line(void) | ||
351 | { | ||
352 | int oldtop,oldbottom; | ||
353 | |||
354 | oldtop=top; | ||
355 | oldbottom=bottom; | ||
356 | top=y; | ||
357 | bottom = video_num_lines; | ||
358 | scrdown(); | ||
359 | top=oldtop; | ||
360 | bottom=oldbottom; | ||
361 | } | ||
362 | |||
363 | static void delete_char(void) | ||
364 | { | ||
365 | int i; | ||
366 | unsigned short * p = (unsigned short *) pos; | ||
367 | |||
368 | if (x>=video_num_columns) | ||
369 | return; | ||
370 | i = x; | ||
371 | while (++i < video_num_columns) { | ||
372 | *p = *(p+1); | ||
373 | p++; | ||
374 | } | ||
375 | *p = video_erase_char; | ||
376 | } | ||
377 | |||
378 | static void delete_line(void) | ||
379 | { | ||
380 | int oldtop,oldbottom; | ||
381 | |||
382 | oldtop=top; | ||
383 | oldbottom=bottom; | ||
384 | top=y; | ||
385 | bottom = video_num_lines; | ||
386 | scrup(); | ||
387 | top=oldtop; | ||
388 | bottom=oldbottom; | ||
389 | } | ||
390 | |||
391 | static void csi_at(unsigned int nr) | ||
392 | { | ||
393 | if (nr > video_num_columns) | ||
394 | nr = video_num_columns; | ||
395 | else if (!nr) | ||
396 | nr = 1; | ||
397 | while (nr--) | ||
398 | insert_char(); | ||
399 | } | ||
400 | |||
401 | static void csi_L(unsigned int nr) | ||
402 | { | ||
403 | if (nr > video_num_lines) | ||
404 | nr = video_num_lines; | ||
405 | else if (!nr) | ||
406 | nr = 1; | ||
407 | while (nr--) | ||
408 | insert_line(); | ||
409 | } | ||
410 | |||
411 | static void csi_P(unsigned int nr) | ||
412 | { | ||
413 | if (nr > video_num_columns) | ||
414 | nr = video_num_columns; | ||
415 | else if (!nr) | ||
416 | nr = 1; | ||
417 | while (nr--) | ||
418 | delete_char(); | ||
419 | } | ||
420 | |||
421 | static void csi_M(unsigned int nr) | ||
422 | { | ||
423 | if (nr > video_num_lines) | ||
424 | nr = video_num_lines; | ||
425 | else if (!nr) | ||
426 | nr=1; | ||
427 | while (nr--) | ||
428 | delete_line(); | ||
429 | } | ||
430 | |||
431 | static int saved_x=0; | ||
432 | static int saved_y=0; | ||
433 | |||
434 | static void save_cur(void) | ||
435 | { | ||
436 | saved_x=x; | ||
437 | saved_y=y; | ||
438 | } | ||
439 | |||
440 | static void restore_cur(void) | ||
441 | { | ||
442 | gotoxy(saved_x, saved_y); | ||
443 | } | ||
444 | |||
445 | void con_write(struct tty_struct * tty) | ||
446 | { | ||
447 | int nr; | ||
448 | char c; | ||
449 | |||
450 | nr = CHARS(tty->write_q); | ||
451 | while (nr--) { | ||
452 | GETCH(tty->write_q,c); | ||
453 | switch(state) { | ||
454 | case 0: | ||
455 | if (c>31 && c<127) { | ||
456 | if (x>=video_num_columns) { | ||
457 | x -= video_num_columns; | ||
458 | pos -= video_size_row; | ||
459 | lf(); | ||
460 | } | ||
461 | __asm__("movb attr,%%ah\n\t" | ||
462 | "movw %%ax,%1\n\t" | ||
463 | ::"a" (c),"m" (*(short *)pos) | ||
464 | ); | ||
465 | pos += 2; | ||
466 | x++; | ||
467 | } else if (c==27) | ||
468 | state=1; | ||
469 | else if (c==10 || c==11 || c==12) | ||
470 | lf(); | ||
471 | else if (c==13) | ||
472 | cr(); | ||
473 | else if (c==ERASE_CHAR(tty)) | ||
474 | del(); | ||
475 | else if (c==8) { | ||
476 | if (x) { | ||
477 | x--; | ||
478 | pos -= 2; | ||
479 | } | ||
480 | } else if (c==9) { | ||
481 | c=8-(x&7); | ||
482 | x += c; | ||
483 | pos += c<<1; | ||
484 | if (x>video_num_columns) { | ||
485 | x -= video_num_columns; | ||
486 | pos -= video_size_row; | ||
487 | lf(); | ||
488 | } | ||
489 | c=9; | ||
490 | } else if (c==7) | ||
491 | sysbeep(); | ||
492 | break; | ||
493 | case 1: | ||
494 | state=0; | ||
495 | if (c=='[') | ||
496 | state=2; | ||
497 | else if (c=='E') | ||
498 | gotoxy(0,y+1); | ||
499 | else if (c=='M') | ||
500 | ri(); | ||
501 | else if (c=='D') | ||
502 | lf(); | ||
503 | else if (c=='Z') | ||
504 | respond(tty); | ||
505 | else if (x=='7') | ||
506 | save_cur(); | ||
507 | else if (x=='8') | ||
508 | restore_cur(); | ||
509 | break; | ||
510 | case 2: | ||
511 | for(npar=0;npar<NPAR;npar++) | ||
512 | par[npar]=0; | ||
513 | npar=0; | ||
514 | state=3; | ||
515 | if ((ques=(c=='?'))) | ||
516 | break; | ||
517 | case 3: | ||
518 | if (c==';' && npar<NPAR-1) { | ||
519 | npar++; | ||
520 | break; | ||
521 | } else if (c>='0' && c<='9') { | ||
522 | par[npar]=10*par[npar]+c-'0'; | ||
523 | break; | ||
524 | } else state=4; | ||
525 | case 4: | ||
526 | state=0; | ||
527 | switch(c) { | ||
528 | case 'G': case '`': | ||
529 | if (par[0]) par[0]--; | ||
530 | gotoxy(par[0],y); | ||
531 | break; | ||
532 | case 'A': | ||
533 | if (!par[0]) par[0]++; | ||
534 | gotoxy(x,y-par[0]); | ||
535 | break; | ||
536 | case 'B': case 'e': | ||
537 | if (!par[0]) par[0]++; | ||
538 | gotoxy(x,y+par[0]); | ||
539 | break; | ||
540 | case 'C': case 'a': | ||
541 | if (!par[0]) par[0]++; | ||
542 | gotoxy(x+par[0],y); | ||
543 | break; | ||
544 | case 'D': | ||
545 | if (!par[0]) par[0]++; | ||
546 | gotoxy(x-par[0],y); | ||
547 | break; | ||
548 | case 'E': | ||
549 | if (!par[0]) par[0]++; | ||
550 | gotoxy(0,y+par[0]); | ||
551 | break; | ||
552 | case 'F': | ||
553 | if (!par[0]) par[0]++; | ||
554 | gotoxy(0,y-par[0]); | ||
555 | break; | ||
556 | case 'd': | ||
557 | if (par[0]) par[0]--; | ||
558 | gotoxy(x,par[0]); | ||
559 | break; | ||
560 | case 'H': case 'f': | ||
561 | if (par[0]) par[0]--; | ||
562 | if (par[1]) par[1]--; | ||
563 | gotoxy(par[1],par[0]); | ||
564 | break; | ||
565 | case 'J': | ||
566 | csi_J(par[0]); | ||
567 | break; | ||
568 | case 'K': | ||
569 | csi_K(par[0]); | ||
570 | break; | ||
571 | case 'L': | ||
572 | csi_L(par[0]); | ||
573 | break; | ||
574 | case 'M': | ||
575 | csi_M(par[0]); | ||
576 | break; | ||
577 | case 'P': | ||
578 | csi_P(par[0]); | ||
579 | break; | ||
580 | case '@': | ||
581 | csi_at(par[0]); | ||
582 | break; | ||
583 | case 'm': | ||
584 | csi_m(); | ||
585 | break; | ||
586 | case 'r': | ||
587 | if (par[0]) par[0]--; | ||
588 | if (!par[1]) par[1] = video_num_lines; | ||
589 | if (par[0] < par[1] && | ||
590 | par[1] <= video_num_lines) { | ||
591 | top=par[0]; | ||
592 | bottom=par[1]; | ||
593 | } | ||
594 | break; | ||
595 | case 's': | ||
596 | save_cur(); | ||
597 | break; | ||
598 | case 'u': | ||
599 | restore_cur(); | ||
600 | break; | ||
601 | } | ||
602 | } | ||
603 | } | ||
604 | set_cursor(); | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * void con_init(void); | ||
609 | * | ||
610 | * This routine initalizes console interrupts, and does nothing | ||
611 | * else. If you want the screen to clear, call tty_write with | ||
612 | * the appropriate escape-sequece. | ||
613 | * | ||
614 | * Reads the information preserved by setup.s to determine the current display | ||
615 | * type and sets everything accordingly. | ||
616 | */ | ||
617 | void con_init(void) | ||
618 | { | ||
619 | register unsigned char a; | ||
620 | char *display_desc = "????"; | ||
621 | char *display_ptr; | ||
622 | |||
623 | video_num_columns = ORIG_VIDEO_COLS; | ||
624 | video_size_row = video_num_columns * 2; | ||
625 | video_num_lines = ORIG_VIDEO_LINES; | ||
626 | video_page = ORIG_VIDEO_PAGE; | ||
627 | video_erase_char = 0x0720; | ||
628 | |||
629 | if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */ | ||
630 | { | ||
631 | video_mem_start = 0xb0000; | ||
632 | video_port_reg = 0x3b4; | ||
633 | video_port_val = 0x3b5; | ||
634 | if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) | ||
635 | { | ||
636 | video_type = VIDEO_TYPE_EGAM; | ||
637 | video_mem_end = 0xb8000; | ||
638 | display_desc = "EGAm"; | ||
639 | } | ||
640 | else | ||
641 | { | ||
642 | video_type = VIDEO_TYPE_MDA; | ||
643 | video_mem_end = 0xb2000; | ||
644 | display_desc = "*MDA"; | ||
645 | } | ||
646 | } | ||
647 | else /* If not, it is color. */ | ||
648 | { | ||
649 | video_mem_start = 0xb8000; | ||
650 | video_port_reg = 0x3d4; | ||
651 | video_port_val = 0x3d5; | ||
652 | if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) | ||
653 | { | ||
654 | video_type = VIDEO_TYPE_EGAC; | ||
655 | video_mem_end = 0xbc000; | ||
656 | display_desc = "EGAc"; | ||
657 | } | ||
658 | else | ||
659 | { | ||
660 | video_type = VIDEO_TYPE_CGA; | ||
661 | video_mem_end = 0xba000; | ||
662 | display_desc = "*CGA"; | ||
663 | } | ||
664 | } | ||
665 | |||
666 | /* Let the user known what kind of display driver we are using */ | ||
667 | |||
668 | display_ptr = ((char *)video_mem_start) + video_size_row - 8; | ||
669 | while (*display_desc) | ||
670 | { | ||
671 | *display_ptr++ = *display_desc++; | ||
672 | display_ptr++; | ||
673 | } | ||
674 | |||
675 | /* Initialize the variables used for scrolling (mostly EGA/VGA) */ | ||
676 | |||
677 | origin = video_mem_start; | ||
678 | scr_end = video_mem_start + video_num_lines * video_size_row; | ||
679 | top = 0; | ||
680 | bottom = video_num_lines; | ||
681 | |||
682 | gotoxy(ORIG_X,ORIG_Y); | ||
683 | set_trap_gate(0x21,&keyboard_interrupt); | ||
684 | outb_p(inb_p(0x21)&0xfd,0x21); | ||
685 | a=inb_p(0x61); | ||
686 | outb_p(a|0x80,0x61); | ||
687 | outb(a,0x61); | ||
688 | } | ||
689 | /* from bsd-net-2: */ | ||
690 | |||
691 | void sysbeepstop(void) | ||
692 | { | ||
693 | /* disable counter 2 */ | ||
694 | outb(inb_p(0x61)&0xFC, 0x61); | ||
695 | } | ||
696 | |||
697 | int beepcount = 0; | ||
698 | |||
699 | static void sysbeep(void) | ||
700 | { | ||
701 | /* enable counter 2 */ | ||
702 | outb_p(inb_p(0x61)|3, 0x61); | ||
703 | /* set command for counter 2, 2 byte write */ | ||
704 | outb_p(0xB6, 0x43); | ||
705 | /* send 0x637 for 750 HZ */ | ||
706 | outb_p(0x37, 0x42); | ||
707 | outb(0x06, 0x42); | ||
708 | /* 1/8 second */ | ||
709 | beepcount = HZ/8; | ||
710 | } | ||
diff --git a/src/kernel/chr_drv/kb.S b/src/kernel/chr_drv/kb.S new file mode 100644 index 0000000..ffd28cf --- /dev/null +++ b/src/kernel/chr_drv/kb.S | |||
@@ -0,0 +1,588 @@ | |||
1 | /* | ||
2 | * linux/kernel/keyboard.S | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * Thanks to Alfred Leung for US keyboard patches | ||
9 | * Wolfgang Thiel for German keyboard patches | ||
10 | * Marc Corsini for the French keyboard | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | |||
15 | .text | ||
16 | .globl keyboard_interrupt | ||
17 | |||
18 | /* | ||
19 | * these are for the keyboard read functions | ||
20 | */ | ||
21 | size = 1024 /* must be a power of two ! And MUST be the same | ||
22 | as in tty_io.c !!!! */ | ||
23 | head = 4 | ||
24 | tail = 8 | ||
25 | proc_list = 12 | ||
26 | buf = 16 | ||
27 | |||
28 | mode: .byte 0 /* caps, alt, ctrl and shift mode */ | ||
29 | leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */ | ||
30 | e0: .byte 0 | ||
31 | |||
32 | /* | ||
33 | * con_int is the real interrupt routine that reads the | ||
34 | * keyboard scan-code and converts it into the appropriate | ||
35 | * ascii character(s). | ||
36 | */ | ||
37 | keyboard_interrupt: | ||
38 | pushl %eax | ||
39 | pushl %ebx | ||
40 | pushl %ecx | ||
41 | pushl %edx | ||
42 | push %ds | ||
43 | push %es | ||
44 | movl $0x10,%eax | ||
45 | mov %ax,%ds | ||
46 | mov %ax,%es | ||
47 | xor %al,%al /* %eax is scan code */ | ||
48 | inb $0x60,%al | ||
49 | cmpb $0xe0,%al | ||
50 | je set_e0 | ||
51 | cmpb $0xe1,%al | ||
52 | je set_e1 | ||
53 | call *key_table(,%eax,4) | ||
54 | movb $0,e0 | ||
55 | e0_e1: inb $0x61,%al | ||
56 | jmp 1f | ||
57 | 1: jmp 1f | ||
58 | 1: orb $0x80,%al | ||
59 | jmp 1f | ||
60 | 1: jmp 1f | ||
61 | 1: outb %al,$0x61 | ||
62 | jmp 1f | ||
63 | 1: jmp 1f | ||
64 | 1: andb $0x7F,%al | ||
65 | outb %al,$0x61 | ||
66 | movb $0x20,%al | ||
67 | outb %al,$0x20 | ||
68 | pushl $0 | ||
69 | call do_tty_interrupt | ||
70 | addl $4,%esp | ||
71 | pop %es | ||
72 | pop %ds | ||
73 | popl %edx | ||
74 | popl %ecx | ||
75 | popl %ebx | ||
76 | popl %eax | ||
77 | iret | ||
78 | set_e0: movb $1,e0 | ||
79 | jmp e0_e1 | ||
80 | set_e1: movb $2,e0 | ||
81 | jmp e0_e1 | ||
82 | |||
83 | /* | ||
84 | * This routine fills the buffer with max 8 bytes, taken from | ||
85 | * %ebx:%eax. (%edx is high). The bytes are written in the | ||
86 | * order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero. | ||
87 | */ | ||
88 | put_queue: | ||
89 | pushl %ecx | ||
90 | pushl %edx | ||
91 | movl table_list,%edx # read-queue for console | ||
92 | movl head(%edx),%ecx | ||
93 | 1: movb %al,buf(%edx,%ecx) | ||
94 | incl %ecx | ||
95 | andl $size-1,%ecx | ||
96 | cmpl tail(%edx),%ecx # buffer full - discard everything | ||
97 | je 3f | ||
98 | shrdl $8,%ebx,%eax | ||
99 | je 2f | ||
100 | shrl $8,%ebx | ||
101 | jmp 1b | ||
102 | 2: movl %ecx,head(%edx) | ||
103 | movl proc_list(%edx),%ecx | ||
104 | testl %ecx,%ecx | ||
105 | je 3f | ||
106 | movl $0,(%ecx) | ||
107 | 3: popl %edx | ||
108 | popl %ecx | ||
109 | ret | ||
110 | |||
111 | ctrl: movb $0x04,%al | ||
112 | jmp 1f | ||
113 | alt: movb $0x10,%al | ||
114 | 1: cmpb $0,e0 | ||
115 | je 2f | ||
116 | addb %al,%al | ||
117 | 2: orb %al,mode | ||
118 | ret | ||
119 | unctrl: movb $0x04,%al | ||
120 | jmp 1f | ||
121 | unalt: movb $0x10,%al | ||
122 | 1: cmpb $0,e0 | ||
123 | je 2f | ||
124 | addb %al,%al | ||
125 | 2: notb %al | ||
126 | andb %al,mode | ||
127 | ret | ||
128 | |||
129 | lshift: | ||
130 | orb $0x01,mode | ||
131 | ret | ||
132 | unlshift: | ||
133 | andb $0xfe,mode | ||
134 | ret | ||
135 | rshift: | ||
136 | orb $0x02,mode | ||
137 | ret | ||
138 | unrshift: | ||
139 | andb $0xfd,mode | ||
140 | ret | ||
141 | |||
142 | caps: testb $0x80,mode | ||
143 | jne 1f | ||
144 | xorb $4,leds | ||
145 | xorb $0x40,mode | ||
146 | orb $0x80,mode | ||
147 | set_leds: | ||
148 | call kb_wait | ||
149 | movb $0xed,%al /* set leds command */ | ||
150 | outb %al,$0x60 | ||
151 | call kb_wait | ||
152 | movb leds,%al | ||
153 | outb %al,$0x60 | ||
154 | ret | ||
155 | uncaps: andb $0x7f,mode | ||
156 | ret | ||
157 | scroll: | ||
158 | xorb $1,leds | ||
159 | jmp set_leds | ||
160 | num: xorb $2,leds | ||
161 | jmp set_leds | ||
162 | |||
163 | /* | ||
164 | * curosr-key/numeric keypad cursor keys are handled here. | ||
165 | * checking for numeric keypad etc. | ||
166 | */ | ||
167 | cursor: | ||
168 | subb $0x47,%al | ||
169 | jb 1f | ||
170 | cmpb $12,%al | ||
171 | ja 1f | ||
172 | jne cur2 /* check for ctrl-alt-del */ | ||
173 | testb $0x0c,mode | ||
174 | je cur2 | ||
175 | testb $0x30,mode | ||
176 | jne reboot | ||
177 | cur2: cmpb $0x01,e0 /* e0 forces cursor movement */ | ||
178 | je cur | ||
179 | testb $0x02,leds /* not num-lock forces cursor */ | ||
180 | je cur | ||
181 | testb $0x03,mode /* shift forces cursor */ | ||
182 | jne cur | ||
183 | xorl %ebx,%ebx | ||
184 | movb num_table(%eax),%al | ||
185 | jmp put_queue | ||
186 | 1: ret | ||
187 | |||
188 | cur: movb cur_table(%eax),%al | ||
189 | cmpb $'9,%al | ||
190 | ja ok_cur | ||
191 | movb $'~,%ah | ||
192 | ok_cur: shll $16,%eax | ||
193 | movw $0x5b1b,%ax | ||
194 | xorl %ebx,%ebx | ||
195 | jmp put_queue | ||
196 | |||
197 | #if defined(KBD_FR) | ||
198 | num_table: | ||
199 | .ascii "789 456 1230." | ||
200 | #else | ||
201 | num_table: | ||
202 | .ascii "789 456 1230," | ||
203 | #endif | ||
204 | cur_table: | ||
205 | .ascii "HA5 DGC YB623" | ||
206 | |||
207 | /* | ||
208 | * this routine handles function keys | ||
209 | */ | ||
210 | func: | ||
211 | pushl %eax | ||
212 | pushl %ecx | ||
213 | pushl %edx | ||
214 | call show_stat | ||
215 | popl %edx | ||
216 | popl %ecx | ||
217 | popl %eax | ||
218 | subb $0x3B,%al | ||
219 | jb end_func | ||
220 | cmpb $9,%al | ||
221 | jbe ok_func | ||
222 | subb $18,%al | ||
223 | cmpb $10,%al | ||
224 | jb end_func | ||
225 | cmpb $11,%al | ||
226 | ja end_func | ||
227 | ok_func: | ||
228 | cmpl $4,%ecx /* check that there is enough room */ | ||
229 | jl end_func | ||
230 | movl func_table(,%eax,4),%eax | ||
231 | xorl %ebx,%ebx | ||
232 | jmp put_queue | ||
233 | end_func: | ||
234 | ret | ||
235 | |||
236 | /* | ||
237 | * function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc. | ||
238 | */ | ||
239 | func_table: | ||
240 | .long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b | ||
241 | .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b | ||
242 | .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b | ||
243 | |||
244 | #if defined(KBD_FINNISH) | ||
245 | key_map: | ||
246 | .byte 0,27 | ||
247 | .ascii "1234567890+'" | ||
248 | .byte 127,9 | ||
249 | .ascii "qwertyuiop}" | ||
250 | .byte 0,13,0 | ||
251 | .ascii "asdfghjkl|{" | ||
252 | .byte 0,0 | ||
253 | .ascii "'zxcvbnm,.-" | ||
254 | .byte 0,'*,0,32 /* 36-39 */ | ||
255 | .fill 16,1,0 /* 3A-49 */ | ||
256 | .byte '-,0,0,0,'+ /* 4A-4E */ | ||
257 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
258 | .byte '< | ||
259 | .fill 10,1,0 | ||
260 | |||
261 | shift_map: | ||
262 | .byte 0,27 | ||
263 | .ascii "!\"#$%&/()=?`" | ||
264 | .byte 127,9 | ||
265 | .ascii "QWERTYUIOP]^" | ||
266 | .byte 13,0 | ||
267 | .ascii "ASDFGHJKL\\[" | ||
268 | .byte 0,0 | ||
269 | .ascii "*ZXCVBNM;:_" | ||
270 | .byte 0,'*,0,32 /* 36-39 */ | ||
271 | .fill 16,1,0 /* 3A-49 */ | ||
272 | .byte '-,0,0,0,'+ /* 4A-4E */ | ||
273 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
274 | .byte '> | ||
275 | .fill 10,1,0 | ||
276 | |||
277 | alt_map: | ||
278 | .byte 0,0 | ||
279 | .ascii "\0@\0$\0\0{[]}\\\0" | ||
280 | .byte 0,0 | ||
281 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
282 | .byte '~,13,0 | ||
283 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
284 | .byte 0,0 | ||
285 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
286 | .byte 0,0,0,0 /* 36-39 */ | ||
287 | .fill 16,1,0 /* 3A-49 */ | ||
288 | .byte 0,0,0,0,0 /* 4A-4E */ | ||
289 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
290 | .byte '| | ||
291 | .fill 10,1,0 | ||
292 | |||
293 | #elif defined(KBD_US) | ||
294 | |||
295 | key_map: | ||
296 | .byte 0,27 | ||
297 | .ascii "1234567890-=" | ||
298 | .byte 127,9 | ||
299 | .ascii "qwertyuiop[]" | ||
300 | .byte 13,0 | ||
301 | .ascii "asdfghjkl;'" | ||
302 | .byte '`,0 | ||
303 | .ascii "\\zxcvbnm,./" | ||
304 | .byte 0,'*,0,32 /* 36-39 */ | ||
305 | .fill 16,1,0 /* 3A-49 */ | ||
306 | .byte '-,0,0,0,'+ /* 4A-4E */ | ||
307 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
308 | .byte '< | ||
309 | .fill 10,1,0 | ||
310 | |||
311 | |||
312 | shift_map: | ||
313 | .byte 0,27 | ||
314 | .ascii "!@#$%^&*()_+" | ||
315 | .byte 127,9 | ||
316 | .ascii "QWERTYUIOP{}" | ||
317 | .byte 13,0 | ||
318 | .ascii "ASDFGHJKL:\"" | ||
319 | .byte '~,0 | ||
320 | .ascii "|ZXCVBNM<>?" | ||
321 | .byte 0,'*,0,32 /* 36-39 */ | ||
322 | .fill 16,1,0 /* 3A-49 */ | ||
323 | .byte '-,0,0,0,'+ /* 4A-4E */ | ||
324 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
325 | .byte '> | ||
326 | .fill 10,1,0 | ||
327 | |||
328 | alt_map: | ||
329 | .byte 0,0 | ||
330 | .ascii "\0@\0$\0\0{[]}\\\0" | ||
331 | .byte 0,0 | ||
332 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
333 | .byte '~,13,0 | ||
334 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
335 | .byte 0,0 | ||
336 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
337 | .byte 0,0,0,0 /* 36-39 */ | ||
338 | .fill 16,1,0 /* 3A-49 */ | ||
339 | .byte 0,0,0,0,0 /* 4A-4E */ | ||
340 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
341 | .byte '| | ||
342 | .fill 10,1,0 | ||
343 | |||
344 | #elif defined(KBD_GR) | ||
345 | |||
346 | key_map: | ||
347 | .byte 0,27 | ||
348 | .ascii "1234567890\\'" | ||
349 | .byte 127,9 | ||
350 | .ascii "qwertzuiop@+" | ||
351 | .byte 13,0 | ||
352 | .ascii "asdfghjkl[]^" | ||
353 | .byte 0,'# | ||
354 | .ascii "yxcvbnm,.-" | ||
355 | .byte 0,'*,0,32 /* 36-39 */ | ||
356 | .fill 16,1,0 /* 3A-49 */ | ||
357 | .byte '-,0,0,0,'+ /* 4A-4E */ | ||
358 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
359 | .byte '< | ||
360 | .fill 10,1,0 | ||
361 | |||
362 | |||
363 | shift_map: | ||
364 | .byte 0,27 | ||
365 | .ascii "!\"#$%&/()=?`" | ||
366 | .byte 127,9 | ||
367 | .ascii "QWERTZUIOP\\*" | ||
368 | .byte 13,0 | ||
369 | .ascii "ASDFGHJKL{}~" | ||
370 | .byte 0,'' | ||
371 | .ascii "YXCVBNM;:_" | ||
372 | .byte 0,'*,0,32 /* 36-39 */ | ||
373 | .fill 16,1,0 /* 3A-49 */ | ||
374 | .byte '-,0,0,0,'+ /* 4A-4E */ | ||
375 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
376 | .byte '> | ||
377 | .fill 10,1,0 | ||
378 | |||
379 | alt_map: | ||
380 | .byte 0,0 | ||
381 | .ascii "\0@\0$\0\0{[]}\\\0" | ||
382 | .byte 0,0 | ||
383 | .byte '@,0,0,0,0,0,0,0,0,0,0 | ||
384 | .byte '~,13,0 | ||
385 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
386 | .byte 0,0 | ||
387 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
388 | .byte 0,0,0,0 /* 36-39 */ | ||
389 | .fill 16,1,0 /* 3A-49 */ | ||
390 | .byte 0,0,0,0,0 /* 4A-4E */ | ||
391 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
392 | .byte '| | ||
393 | .fill 10,1,0 | ||
394 | |||
395 | |||
396 | #elif defined(KBD_FR) | ||
397 | |||
398 | key_map: | ||
399 | .byte 0,27 | ||
400 | .ascii "&{\"'(-}_/@)=" | ||
401 | .byte 127,9 | ||
402 | .ascii "azertyuiop^$" | ||
403 | .byte 13,0 | ||
404 | .ascii "qsdfghjklm|" | ||
405 | .byte '`,0,42 /* coin sup gauche, don't know, [*|mu] */ | ||
406 | .ascii "wxcvbn,;:!" | ||
407 | .byte 0,'*,0,32 /* 36-39 */ | ||
408 | .fill 16,1,0 /* 3A-49 */ | ||
409 | .byte '-,0,0,0,'+ /* 4A-4E */ | ||
410 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
411 | .byte '< | ||
412 | .fill 10,1,0 | ||
413 | |||
414 | shift_map: | ||
415 | .byte 0,27 | ||
416 | .ascii "1234567890]+" | ||
417 | .byte 127,9 | ||
418 | .ascii "AZERTYUIOP<>" | ||
419 | .byte 13,0 | ||
420 | .ascii "QSDFGHJKLM%" | ||
421 | .byte '~,0,'# | ||
422 | .ascii "WXCVBN?./\\" | ||
423 | .byte 0,'*,0,32 /* 36-39 */ | ||
424 | .fill 16,1,0 /* 3A-49 */ | ||
425 | .byte '-,0,0,0,'+ /* 4A-4E */ | ||
426 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
427 | .byte '> | ||
428 | .fill 10,1,0 | ||
429 | |||
430 | alt_map: | ||
431 | .byte 0,0 | ||
432 | .ascii "\0~#{[|`\\^@]}" | ||
433 | .byte 0,0 | ||
434 | .byte '@,0,0,0,0,0,0,0,0,0,0 | ||
435 | .byte '~,13,0 | ||
436 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
437 | .byte 0,0 | ||
438 | .byte 0,0,0,0,0,0,0,0,0,0,0 | ||
439 | .byte 0,0,0,0 /* 36-39 */ | ||
440 | .fill 16,1,0 /* 3A-49 */ | ||
441 | .byte 0,0,0,0,0 /* 4A-4E */ | ||
442 | .byte 0,0,0,0,0,0,0 /* 4F-55 */ | ||
443 | .byte '| | ||
444 | .fill 10,1,0 | ||
445 | |||
446 | #else | ||
447 | #error "KBD-type not defined" | ||
448 | #endif | ||
449 | /* | ||
450 | * do_self handles "normal" keys, ie keys that don't change meaning | ||
451 | * and which have just one character returns. | ||
452 | */ | ||
453 | do_self: | ||
454 | lea alt_map,%ebx | ||
455 | testb $0x20,mode /* alt-gr */ | ||
456 | jne 1f | ||
457 | lea shift_map,%ebx | ||
458 | testb $0x03,mode | ||
459 | jne 1f | ||
460 | lea key_map,%ebx | ||
461 | 1: movb (%ebx,%eax),%al | ||
462 | orb %al,%al | ||
463 | je none | ||
464 | testb $0x4c,mode /* ctrl or caps */ | ||
465 | je 2f | ||
466 | cmpb $'a,%al | ||
467 | jb 2f | ||
468 | cmpb $'},%al | ||
469 | ja 2f | ||
470 | subb $32,%al | ||
471 | 2: testb $0x0c,mode /* ctrl */ | ||
472 | je 3f | ||
473 | cmpb $64,%al | ||
474 | jb 3f | ||
475 | cmpb $64+32,%al | ||
476 | jae 3f | ||
477 | subb $64,%al | ||
478 | 3: testb $0x10,mode /* left alt */ | ||
479 | je 4f | ||
480 | orb $0x80,%al | ||
481 | 4: andl $0xff,%eax | ||
482 | xorl %ebx,%ebx | ||
483 | call put_queue | ||
484 | none: ret | ||
485 | |||
486 | /* | ||
487 | * minus has a routine of it's own, as a 'E0h' before | ||
488 | * the scan code for minus means that the numeric keypad | ||
489 | * slash was pushed. | ||
490 | */ | ||
491 | minus: cmpb $1,e0 | ||
492 | jne do_self | ||
493 | movl $'/,%eax | ||
494 | xorl %ebx,%ebx | ||
495 | jmp put_queue | ||
496 | |||
497 | /* | ||
498 | * This table decides which routine to call when a scan-code has been | ||
499 | * gotten. Most routines just call do_self, or none, depending if | ||
500 | * they are make or break. | ||
501 | */ | ||
502 | key_table: | ||
503 | .long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */ | ||
504 | .long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */ | ||
505 | .long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */ | ||
506 | .long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */ | ||
507 | .long do_self,do_self,do_self,do_self /* 10-13 q w e r */ | ||
508 | .long do_self,do_self,do_self,do_self /* 14-17 t y u i */ | ||
509 | .long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */ | ||
510 | .long do_self,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */ | ||
511 | .long do_self,do_self,do_self,do_self /* 20-23 d f g h */ | ||
512 | .long do_self,do_self,do_self,do_self /* 24-27 j k l | */ | ||
513 | .long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */ | ||
514 | .long do_self,do_self,do_self,do_self /* 2C-2F z x c v */ | ||
515 | .long do_self,do_self,do_self,do_self /* 30-33 b n m , */ | ||
516 | .long do_self,minus,rshift,do_self /* 34-37 . - rshift * */ | ||
517 | .long alt,do_self,caps,func /* 38-3B alt sp caps f1 */ | ||
518 | .long func,func,func,func /* 3C-3F f2 f3 f4 f5 */ | ||
519 | .long func,func,func,func /* 40-43 f6 f7 f8 f9 */ | ||
520 | .long func,num,scroll,cursor /* 44-47 f10 num scr home */ | ||
521 | .long cursor,cursor,do_self,cursor /* 48-4B up pgup - left */ | ||
522 | .long cursor,cursor,do_self,cursor /* 4C-4F n5 right + end */ | ||
523 | .long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */ | ||
524 | .long none,none,do_self,func /* 54-57 sysreq ? < f11 */ | ||
525 | .long func,none,none,none /* 58-5B f12 ? ? ? */ | ||
526 | .long none,none,none,none /* 5C-5F ? ? ? ? */ | ||
527 | .long none,none,none,none /* 60-63 ? ? ? ? */ | ||
528 | .long none,none,none,none /* 64-67 ? ? ? ? */ | ||
529 | .long none,none,none,none /* 68-6B ? ? ? ? */ | ||
530 | .long none,none,none,none /* 6C-6F ? ? ? ? */ | ||
531 | .long none,none,none,none /* 70-73 ? ? ? ? */ | ||
532 | .long none,none,none,none /* 74-77 ? ? ? ? */ | ||
533 | .long none,none,none,none /* 78-7B ? ? ? ? */ | ||
534 | .long none,none,none,none /* 7C-7F ? ? ? ? */ | ||
535 | .long none,none,none,none /* 80-83 ? br br br */ | ||
536 | .long none,none,none,none /* 84-87 br br br br */ | ||
537 | .long none,none,none,none /* 88-8B br br br br */ | ||
538 | .long none,none,none,none /* 8C-8F br br br br */ | ||
539 | .long none,none,none,none /* 90-93 br br br br */ | ||
540 | .long none,none,none,none /* 94-97 br br br br */ | ||
541 | .long none,none,none,none /* 98-9B br br br br */ | ||
542 | .long none,unctrl,none,none /* 9C-9F br unctrl br br */ | ||
543 | .long none,none,none,none /* A0-A3 br br br br */ | ||
544 | .long none,none,none,none /* A4-A7 br br br br */ | ||
545 | .long none,none,unlshift,none /* A8-AB br br unlshift br */ | ||
546 | .long none,none,none,none /* AC-AF br br br br */ | ||
547 | .long none,none,none,none /* B0-B3 br br br br */ | ||
548 | .long none,none,unrshift,none /* B4-B7 br br unrshift br */ | ||
549 | .long unalt,none,uncaps,none /* B8-BB unalt br uncaps br */ | ||
550 | .long none,none,none,none /* BC-BF br br br br */ | ||
551 | .long none,none,none,none /* C0-C3 br br br br */ | ||
552 | .long none,none,none,none /* C4-C7 br br br br */ | ||
553 | .long none,none,none,none /* C8-CB br br br br */ | ||
554 | .long none,none,none,none /* CC-CF br br br br */ | ||
555 | .long none,none,none,none /* D0-D3 br br br br */ | ||
556 | .long none,none,none,none /* D4-D7 br br br br */ | ||
557 | .long none,none,none,none /* D8-DB br ? ? ? */ | ||
558 | .long none,none,none,none /* DC-DF ? ? ? ? */ | ||
559 | .long none,none,none,none /* E0-E3 e0 e1 ? ? */ | ||
560 | .long none,none,none,none /* E4-E7 ? ? ? ? */ | ||
561 | .long none,none,none,none /* E8-EB ? ? ? ? */ | ||
562 | .long none,none,none,none /* EC-EF ? ? ? ? */ | ||
563 | .long none,none,none,none /* F0-F3 ? ? ? ? */ | ||
564 | .long none,none,none,none /* F4-F7 ? ? ? ? */ | ||
565 | .long none,none,none,none /* F8-FB ? ? ? ? */ | ||
566 | .long none,none,none,none /* FC-FF ? ? ? ? */ | ||
567 | |||
568 | /* | ||
569 | * kb_wait waits for the keyboard controller buffer to empty. | ||
570 | * there is no timeout - if the buffer doesn't empty, we hang. | ||
571 | */ | ||
572 | kb_wait: | ||
573 | pushl %eax | ||
574 | 1: inb $0x64,%al | ||
575 | testb $0x02,%al | ||
576 | jne 1b | ||
577 | popl %eax | ||
578 | ret | ||
579 | /* | ||
580 | * This routine reboots the machine by asking the keyboard | ||
581 | * controller to pulse the reset-line low. | ||
582 | */ | ||
583 | reboot: | ||
584 | call kb_wait | ||
585 | movw $0x1234,0x472 /* don't do memory check */ | ||
586 | movb $0xfc,%al /* pulse reset and A20 low */ | ||
587 | outb %al,$0x64 | ||
588 | die: jmp die | ||
diff --git a/src/kernel/chr_drv/rs_io.s b/src/kernel/chr_drv/rs_io.s new file mode 100644 index 0000000..6788658 --- /dev/null +++ b/src/kernel/chr_drv/rs_io.s | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * linux/kernel/rs_io.s | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * rs_io.s | ||
9 | * | ||
10 | * This module implements the rs232 io interrupts. | ||
11 | */ | ||
12 | |||
13 | .text | ||
14 | .globl rs1_interrupt,rs2_interrupt | ||
15 | |||
16 | size = 1024 /* must be power of two ! | ||
17 | and must match the value | ||
18 | in tty_io.c!!! */ | ||
19 | |||
20 | /* these are the offsets into the read/write buffer structures */ | ||
21 | rs_addr = 0 | ||
22 | head = 4 | ||
23 | tail = 8 | ||
24 | proc_list = 12 | ||
25 | buf = 16 | ||
26 | |||
27 | startup = 256 /* chars left in write queue when we restart it */ | ||
28 | |||
29 | /* | ||
30 | * These are the actual interrupt routines. They look where | ||
31 | * the interrupt is coming from, and take appropriate action. | ||
32 | */ | ||
33 | .align 2 | ||
34 | rs1_interrupt: | ||
35 | pushl $table_list+8 | ||
36 | jmp rs_int | ||
37 | .align 2 | ||
38 | rs2_interrupt: | ||
39 | pushl $table_list+16 | ||
40 | rs_int: | ||
41 | pushl %edx | ||
42 | pushl %ecx | ||
43 | pushl %ebx | ||
44 | pushl %eax | ||
45 | push %es | ||
46 | push %ds /* as this is an interrupt, we cannot */ | ||
47 | pushl $0x10 /* know that bs is ok. Load it */ | ||
48 | pop %ds | ||
49 | pushl $0x10 | ||
50 | pop %es | ||
51 | movl 24(%esp),%edx | ||
52 | movl (%edx),%edx | ||
53 | movl rs_addr(%edx),%edx | ||
54 | addl $2,%edx /* interrupt ident. reg */ | ||
55 | rep_int: | ||
56 | xorl %eax,%eax | ||
57 | inb %dx,%al | ||
58 | testb $1,%al | ||
59 | jne end | ||
60 | cmpb $6,%al /* this shouldn't happen, but ... */ | ||
61 | ja end | ||
62 | movl 24(%esp),%ecx | ||
63 | pushl %edx | ||
64 | subl $2,%edx | ||
65 | call *jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */ | ||
66 | popl %edx | ||
67 | jmp rep_int | ||
68 | end: movb $0x20,%al | ||
69 | outb %al,$0x20 /* EOI */ | ||
70 | pop %ds | ||
71 | pop %es | ||
72 | popl %eax | ||
73 | popl %ebx | ||
74 | popl %ecx | ||
75 | popl %edx | ||
76 | addl $4,%esp # jump over _table_list entry | ||
77 | iret | ||
78 | |||
79 | jmp_table: | ||
80 | .long modem_status,write_char,read_char,line_status | ||
81 | |||
82 | .align 2 | ||
83 | modem_status: | ||
84 | addl $6,%edx /* clear intr by reading modem status reg */ | ||
85 | inb %dx,%al | ||
86 | ret | ||
87 | |||
88 | .align 2 | ||
89 | line_status: | ||
90 | addl $5,%edx /* clear intr by reading line status reg. */ | ||
91 | inb %dx,%al | ||
92 | ret | ||
93 | |||
94 | .align 2 | ||
95 | read_char: | ||
96 | inb %dx,%al | ||
97 | movl %ecx,%edx | ||
98 | subl $table_list,%edx | ||
99 | shrl $3,%edx | ||
100 | movl (%ecx),%ecx # read-queue | ||
101 | movl head(%ecx),%ebx | ||
102 | movb %al,buf(%ecx,%ebx) | ||
103 | incl %ebx | ||
104 | andl $size-1,%ebx | ||
105 | cmpl tail(%ecx),%ebx | ||
106 | je 1f | ||
107 | movl %ebx,head(%ecx) | ||
108 | 1: pushl %edx | ||
109 | call do_tty_interrupt | ||
110 | addl $4,%esp | ||
111 | ret | ||
112 | |||
113 | .align 2 | ||
114 | write_char: | ||
115 | movl 4(%ecx),%ecx # write-queue | ||
116 | movl head(%ecx),%ebx | ||
117 | subl tail(%ecx),%ebx | ||
118 | andl $size-1,%ebx # nr chars in queue | ||
119 | je write_buffer_empty | ||
120 | cmpl $startup,%ebx | ||
121 | ja 1f | ||
122 | movl proc_list(%ecx),%ebx # wake up sleeping process | ||
123 | testl %ebx,%ebx # is there any? | ||
124 | je 1f | ||
125 | movl $0,(%ebx) | ||
126 | 1: movl tail(%ecx),%ebx | ||
127 | movb buf(%ecx,%ebx),%al | ||
128 | outb %al,%dx | ||
129 | incl %ebx | ||
130 | andl $size-1,%ebx | ||
131 | movl %ebx,tail(%ecx) | ||
132 | cmpl head(%ecx),%ebx | ||
133 | je write_buffer_empty | ||
134 | ret | ||
135 | .align 2 | ||
136 | write_buffer_empty: | ||
137 | movl proc_list(%ecx),%ebx # wake up sleeping process | ||
138 | testl %ebx,%ebx # is there any? | ||
139 | je 1f | ||
140 | movl $0,(%ebx) | ||
141 | 1: incl %edx | ||
142 | inb %dx,%al | ||
143 | jmp 1f | ||
144 | 1: jmp 1f | ||
145 | 1: andb $0xd,%al /* disable transmit interrupt */ | ||
146 | outb %al,%dx | ||
147 | ret | ||
diff --git a/src/kernel/chr_drv/serial.c b/src/kernel/chr_drv/serial.c new file mode 100644 index 0000000..aba25df --- /dev/null +++ b/src/kernel/chr_drv/serial.c | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * linux/kernel/serial.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * serial.c | ||
9 | * | ||
10 | * This module implements the rs232 io functions | ||
11 | * void rs_write(struct tty_struct * queue); | ||
12 | * void rs_init(void); | ||
13 | * and all interrupts pertaining to serial IO. | ||
14 | */ | ||
15 | |||
16 | #include <linux/tty.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <asm/system.h> | ||
19 | #include <asm/io.h> | ||
20 | |||
21 | #define WAKEUP_CHARS (TTY_BUF_SIZE/4) | ||
22 | |||
23 | extern void rs1_interrupt(void); | ||
24 | extern void rs2_interrupt(void); | ||
25 | |||
26 | static void init(int port) | ||
27 | { | ||
28 | outb_p(0x80,port+3); /* set DLAB of line control reg */ | ||
29 | outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps */ | ||
30 | outb_p(0x00,port+1); /* MS of divisor */ | ||
31 | outb_p(0x03,port+3); /* reset DLAB */ | ||
32 | outb_p(0x0b,port+4); /* set DTR,RTS, OUT_2 */ | ||
33 | outb_p(0x0d,port+1); /* enable all intrs but writes */ | ||
34 | (void)inb(port); /* read data port to reset things (?) */ | ||
35 | } | ||
36 | |||
37 | void rs_init(void) | ||
38 | { | ||
39 | set_intr_gate(0x24,rs1_interrupt); | ||
40 | set_intr_gate(0x23,rs2_interrupt); | ||
41 | init(tty_table[1].read_q.data); | ||
42 | init(tty_table[2].read_q.data); | ||
43 | outb(inb_p(0x21)&0xE7,0x21); | ||
44 | } | ||
45 | |||
46 | /* | ||
47 | * This routine gets called when tty_write has put something into | ||
48 | * the write_queue. It must check wheter the queue is empty, and | ||
49 | * set the interrupt register accordingly | ||
50 | * | ||
51 | * void _rs_write(struct tty_struct * tty); | ||
52 | */ | ||
53 | void rs_write(struct tty_struct * tty) | ||
54 | { | ||
55 | cli(); | ||
56 | if (!EMPTY(tty->write_q)) | ||
57 | outb(inb_p(tty->write_q.data+1)|0x02,tty->write_q.data+1); | ||
58 | sti(); | ||
59 | } | ||
diff --git a/src/kernel/chr_drv/tty_io.c b/src/kernel/chr_drv/tty_io.c new file mode 100644 index 0000000..ed14fa8 --- /dev/null +++ b/src/kernel/chr_drv/tty_io.c | |||
@@ -0,0 +1,350 @@ | |||
1 | /* | ||
2 | * linux/kernel/tty_io.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles | ||
9 | * or rs-channels. It also implements echoing, cooked mode etc. | ||
10 | * | ||
11 | * Kill-line thanks to John T Kohl. | ||
12 | */ | ||
13 | #include <ctype.h> | ||
14 | #include <errno.h> | ||
15 | #include <signal.h> | ||
16 | |||
17 | #define ALRMMASK (1<<(SIGALRM-1)) | ||
18 | #define KILLMASK (1<<(SIGKILL-1)) | ||
19 | #define INTMASK (1<<(SIGINT-1)) | ||
20 | #define QUITMASK (1<<(SIGQUIT-1)) | ||
21 | #define TSTPMASK (1<<(SIGTSTP-1)) | ||
22 | |||
23 | #include <linux/sched.h> | ||
24 | #include <linux/tty.h> | ||
25 | #include <asm/segment.h> | ||
26 | #include <asm/system.h> | ||
27 | |||
28 | #define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f) | ||
29 | #define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f) | ||
30 | #define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f) | ||
31 | |||
32 | #define L_CANON(tty) _L_FLAG((tty),ICANON) | ||
33 | #define L_ISIG(tty) _L_FLAG((tty),ISIG) | ||
34 | #define L_ECHO(tty) _L_FLAG((tty),ECHO) | ||
35 | #define L_ECHOE(tty) _L_FLAG((tty),ECHOE) | ||
36 | #define L_ECHOK(tty) _L_FLAG((tty),ECHOK) | ||
37 | #define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) | ||
38 | #define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) | ||
39 | |||
40 | #define I_UCLC(tty) _I_FLAG((tty),IUCLC) | ||
41 | #define I_NLCR(tty) _I_FLAG((tty),INLCR) | ||
42 | #define I_CRNL(tty) _I_FLAG((tty),ICRNL) | ||
43 | #define I_NOCR(tty) _I_FLAG((tty),IGNCR) | ||
44 | |||
45 | #define O_POST(tty) _O_FLAG((tty),OPOST) | ||
46 | #define O_NLCR(tty) _O_FLAG((tty),ONLCR) | ||
47 | #define O_CRNL(tty) _O_FLAG((tty),OCRNL) | ||
48 | #define O_NLRET(tty) _O_FLAG((tty),ONLRET) | ||
49 | #define O_LCUC(tty) _O_FLAG((tty),OLCUC) | ||
50 | |||
51 | struct tty_struct tty_table[] = { | ||
52 | { | ||
53 | {ICRNL, /* change incoming CR to NL */ | ||
54 | OPOST|ONLCR, /* change outgoing NL to CRNL */ | ||
55 | 0, | ||
56 | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE, | ||
57 | 0, /* console termio */ | ||
58 | INIT_C_CC}, | ||
59 | 0, /* initial pgrp */ | ||
60 | 0, /* initial stopped */ | ||
61 | con_write, | ||
62 | {0,0,0,0,""}, /* console read-queue */ | ||
63 | {0,0,0,0,""}, /* console write-queue */ | ||
64 | {0,0,0,0,""} /* console secondary queue */ | ||
65 | },{ | ||
66 | {0, /* no translation */ | ||
67 | 0, /* no translation */ | ||
68 | B2400 | CS8, | ||
69 | 0, | ||
70 | 0, | ||
71 | INIT_C_CC}, | ||
72 | 0, | ||
73 | 0, | ||
74 | rs_write, | ||
75 | {0x3f8,0,0,0,""}, /* rs 1 */ | ||
76 | {0x3f8,0,0,0,""}, | ||
77 | {0,0,0,0,""} | ||
78 | },{ | ||
79 | {0, /* no translation */ | ||
80 | 0, /* no translation */ | ||
81 | B2400 | CS8, | ||
82 | 0, | ||
83 | 0, | ||
84 | INIT_C_CC}, | ||
85 | 0, | ||
86 | 0, | ||
87 | rs_write, | ||
88 | {0x2f8,0,0,0,""}, /* rs 2 */ | ||
89 | {0x2f8,0,0,0,""}, | ||
90 | {0,0,0,0,""} | ||
91 | } | ||
92 | }; | ||
93 | |||
94 | /* | ||
95 | * these are the tables used by the machine code handlers. | ||
96 | * you can implement pseudo-tty's or something by changing | ||
97 | * them. Currently not done. | ||
98 | */ | ||
99 | struct tty_queue * table_list[]={ | ||
100 | &tty_table[0].read_q, &tty_table[0].write_q, | ||
101 | &tty_table[1].read_q, &tty_table[1].write_q, | ||
102 | &tty_table[2].read_q, &tty_table[2].write_q | ||
103 | }; | ||
104 | |||
105 | void tty_init(void) | ||
106 | { | ||
107 | rs_init(); | ||
108 | con_init(); | ||
109 | } | ||
110 | |||
111 | void tty_intr(struct tty_struct * tty, int mask) | ||
112 | { | ||
113 | int i; | ||
114 | |||
115 | if (tty->pgrp <= 0) | ||
116 | return; | ||
117 | for (i=0;i<NR_TASKS;i++) | ||
118 | if (task[i] && task[i]->pgrp==tty->pgrp) | ||
119 | task[i]->signal |= mask; | ||
120 | } | ||
121 | |||
122 | static void sleep_if_empty(struct tty_queue * queue) | ||
123 | { | ||
124 | cli(); | ||
125 | while (!current->signal && EMPTY(*queue)) | ||
126 | interruptible_sleep_on(&queue->proc_list); | ||
127 | sti(); | ||
128 | } | ||
129 | |||
130 | static void sleep_if_full(struct tty_queue * queue) | ||
131 | { | ||
132 | if (!FULL(*queue)) | ||
133 | return; | ||
134 | cli(); | ||
135 | while (!current->signal && LEFT(*queue)<128) | ||
136 | interruptible_sleep_on(&queue->proc_list); | ||
137 | sti(); | ||
138 | } | ||
139 | |||
140 | void wait_for_keypress(void) | ||
141 | { | ||
142 | sleep_if_empty(&tty_table[0].secondary); | ||
143 | } | ||
144 | |||
145 | void copy_to_cooked(struct tty_struct * tty) | ||
146 | { | ||
147 | signed char c; | ||
148 | |||
149 | while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) { | ||
150 | GETCH(tty->read_q,c); | ||
151 | if (c==13) | ||
152 | if (I_CRNL(tty)) | ||
153 | c=10; | ||
154 | else if (I_NOCR(tty)) | ||
155 | continue; | ||
156 | else ; | ||
157 | else if (c==10 && I_NLCR(tty)) | ||
158 | c=13; | ||
159 | if (I_UCLC(tty)) | ||
160 | c=tolower(c); | ||
161 | if (L_CANON(tty)) { | ||
162 | if (c==KILL_CHAR(tty)) { | ||
163 | /* deal with killing the input line */ | ||
164 | while(!(EMPTY(tty->secondary) || | ||
165 | (c=LAST(tty->secondary))==10 || | ||
166 | c==EOF_CHAR(tty))) { | ||
167 | if (L_ECHO(tty)) { | ||
168 | if (c<32) | ||
169 | PUTCH(127,tty->write_q); | ||
170 | PUTCH(127,tty->write_q); | ||
171 | tty->write(tty); | ||
172 | } | ||
173 | DEC(tty->secondary.head); | ||
174 | } | ||
175 | continue; | ||
176 | } | ||
177 | if (c==ERASE_CHAR(tty)) { | ||
178 | if (EMPTY(tty->secondary) || | ||
179 | (c=LAST(tty->secondary))==10 || | ||
180 | c==EOF_CHAR(tty)) | ||
181 | continue; | ||
182 | if (L_ECHO(tty)) { | ||
183 | if (c<32) | ||
184 | PUTCH(127,tty->write_q); | ||
185 | PUTCH(127,tty->write_q); | ||
186 | tty->write(tty); | ||
187 | } | ||
188 | DEC(tty->secondary.head); | ||
189 | continue; | ||
190 | } | ||
191 | if (c==STOP_CHAR(tty)) { | ||
192 | tty->stopped=1; | ||
193 | continue; | ||
194 | } | ||
195 | if (c==START_CHAR(tty)) { | ||
196 | tty->stopped=0; | ||
197 | continue; | ||
198 | } | ||
199 | } | ||
200 | if (L_ISIG(tty)) { | ||
201 | if (c==INTR_CHAR(tty)) { | ||
202 | tty_intr(tty,INTMASK); | ||
203 | continue; | ||
204 | } | ||
205 | if (c==QUIT_CHAR(tty)) { | ||
206 | tty_intr(tty,QUITMASK); | ||
207 | continue; | ||
208 | } | ||
209 | } | ||
210 | if (c==10 || c==EOF_CHAR(tty)) | ||
211 | tty->secondary.data++; | ||
212 | if (L_ECHO(tty)) { | ||
213 | if (c==10) { | ||
214 | PUTCH(10,tty->write_q); | ||
215 | PUTCH(13,tty->write_q); | ||
216 | } else if (c<32) { | ||
217 | if (L_ECHOCTL(tty)) { | ||
218 | PUTCH('^',tty->write_q); | ||
219 | PUTCH(c+64,tty->write_q); | ||
220 | } | ||
221 | } else | ||
222 | PUTCH(c,tty->write_q); | ||
223 | tty->write(tty); | ||
224 | } | ||
225 | PUTCH(c,tty->secondary); | ||
226 | } | ||
227 | wake_up(&tty->secondary.proc_list); | ||
228 | } | ||
229 | |||
230 | int tty_read(unsigned channel, char * buf, int nr) | ||
231 | { | ||
232 | struct tty_struct * tty; | ||
233 | char c, * b=buf; | ||
234 | int minimum,time,flag=0; | ||
235 | long oldalarm; | ||
236 | |||
237 | if (channel>2 || nr<0) return -1; | ||
238 | tty = &tty_table[channel]; | ||
239 | oldalarm = current->alarm; | ||
240 | time = 10L*tty->termios.c_cc[VTIME]; | ||
241 | minimum = tty->termios.c_cc[VMIN]; | ||
242 | if (time && !minimum) { | ||
243 | minimum=1; | ||
244 | if ((flag=(!oldalarm || time+jiffies<oldalarm))) | ||
245 | current->alarm = time+jiffies; | ||
246 | } | ||
247 | if (minimum>nr) | ||
248 | minimum=nr; | ||
249 | while (nr>0) { | ||
250 | if (flag && (current->signal & ALRMMASK)) { | ||
251 | current->signal &= ~ALRMMASK; | ||
252 | break; | ||
253 | } | ||
254 | if (current->signal) | ||
255 | break; | ||
256 | if (EMPTY(tty->secondary) || (L_CANON(tty) && | ||
257 | !tty->secondary.data && LEFT(tty->secondary)>20)) { | ||
258 | sleep_if_empty(&tty->secondary); | ||
259 | continue; | ||
260 | } | ||
261 | do { | ||
262 | GETCH(tty->secondary,c); | ||
263 | if (c==EOF_CHAR(tty) || c==10) | ||
264 | tty->secondary.data--; | ||
265 | if (c==EOF_CHAR(tty) && L_CANON(tty)) | ||
266 | return (b-buf); | ||
267 | else { | ||
268 | put_fs_byte(c,b++); | ||
269 | if (!--nr) | ||
270 | break; | ||
271 | } | ||
272 | } while (nr>0 && !EMPTY(tty->secondary)); | ||
273 | if (time && !L_CANON(tty)) { | ||
274 | if ((flag=(!oldalarm || time+jiffies<oldalarm))) | ||
275 | current->alarm = time+jiffies; | ||
276 | else | ||
277 | current->alarm = oldalarm; | ||
278 | } | ||
279 | if (L_CANON(tty)) { | ||
280 | if (b-buf) | ||
281 | break; | ||
282 | } else if (b-buf >= minimum) | ||
283 | break; | ||
284 | } | ||
285 | current->alarm = oldalarm; | ||
286 | if (current->signal && !(b-buf)) | ||
287 | return -EINTR; | ||
288 | return (b-buf); | ||
289 | } | ||
290 | |||
291 | int tty_write(unsigned channel, char * buf, int nr) | ||
292 | { | ||
293 | static int cr_flag=0; | ||
294 | struct tty_struct * tty; | ||
295 | char c, *b=buf; | ||
296 | |||
297 | if (channel>2 || nr<0) return -1; | ||
298 | tty = channel + tty_table; | ||
299 | while (nr>0) { | ||
300 | sleep_if_full(&tty->write_q); | ||
301 | if (current->signal) | ||
302 | break; | ||
303 | while (nr>0 && !FULL(tty->write_q)) { | ||
304 | c=get_fs_byte(b); | ||
305 | if (O_POST(tty)) { | ||
306 | if (c=='\r' && O_CRNL(tty)) | ||
307 | c='\n'; | ||
308 | else if (c=='\n' && O_NLRET(tty)) | ||
309 | c='\r'; | ||
310 | if (c=='\n' && !cr_flag && O_NLCR(tty)) { | ||
311 | cr_flag = 1; | ||
312 | PUTCH(13,tty->write_q); | ||
313 | continue; | ||
314 | } | ||
315 | if (O_LCUC(tty)) | ||
316 | c=toupper(c); | ||
317 | } | ||
318 | b++; nr--; | ||
319 | cr_flag = 0; | ||
320 | PUTCH(c,tty->write_q); | ||
321 | } | ||
322 | tty->write(tty); | ||
323 | if (nr>0) | ||
324 | schedule(); | ||
325 | } | ||
326 | return (b-buf); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * Jeh, sometimes I really like the 386. | ||
331 | * This routine is called from an interrupt, | ||
332 | * and there should be absolutely no problem | ||
333 | * with sleeping even in an interrupt (I hope). | ||
334 | * Of course, if somebody proves me wrong, I'll | ||
335 | * hate intel for all time :-). We'll have to | ||
336 | * be careful and see to reinstating the interrupt | ||
337 | * chips before calling this, though. | ||
338 | * | ||
339 | * I don't think we sleep here under normal circumstances | ||
340 | * anyway, which is good, as the task sleeping might be | ||
341 | * totally innocent. | ||
342 | */ | ||
343 | void do_tty_interrupt(int tty) | ||
344 | { | ||
345 | copy_to_cooked(tty_table+tty); | ||
346 | } | ||
347 | |||
348 | void chr_dev_init(void) | ||
349 | { | ||
350 | } | ||
diff --git a/src/kernel/chr_drv/tty_ioctl.c b/src/kernel/chr_drv/tty_ioctl.c new file mode 100644 index 0000000..e4e3745 --- /dev/null +++ b/src/kernel/chr_drv/tty_ioctl.c | |||
@@ -0,0 +1,204 @@ | |||
1 | /* | ||
2 | * linux/kernel/chr_drv/tty_ioctl.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | #include <errno.h> | ||
8 | #include <termios.h> | ||
9 | |||
10 | #include <linux/sched.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/tty.h> | ||
13 | |||
14 | #include <asm/io.h> | ||
15 | #include <asm/segment.h> | ||
16 | #include <asm/system.h> | ||
17 | |||
18 | static unsigned short quotient[] = { | ||
19 | 0, 2304, 1536, 1047, 857, | ||
20 | 768, 576, 384, 192, 96, | ||
21 | 64, 48, 24, 12, 6, 3 | ||
22 | }; | ||
23 | |||
24 | static void change_speed(struct tty_struct * tty) | ||
25 | { | ||
26 | unsigned short port,quot; | ||
27 | |||
28 | if (!(port = tty->read_q.data)) | ||
29 | return; | ||
30 | quot = quotient[tty->termios.c_cflag & CBAUD]; | ||
31 | cli(); | ||
32 | outb_p(0x80,port+3); /* set DLAB */ | ||
33 | outb_p(quot & 0xff,port); /* LS of divisor */ | ||
34 | outb_p(quot >> 8,port+1); /* MS of divisor */ | ||
35 | outb(0x03,port+3); /* reset DLAB */ | ||
36 | sti(); | ||
37 | } | ||
38 | |||
39 | static void flush(struct tty_queue * queue) | ||
40 | { | ||
41 | cli(); | ||
42 | queue->head = queue->tail; | ||
43 | sti(); | ||
44 | } | ||
45 | |||
46 | static void wait_until_sent(struct tty_struct * tty) | ||
47 | { | ||
48 | /* do nothing - not implemented */ | ||
49 | } | ||
50 | |||
51 | static void send_break(struct tty_struct * tty) | ||
52 | { | ||
53 | /* do nothing - not implemented */ | ||
54 | } | ||
55 | |||
56 | static int get_termios(struct tty_struct * tty, struct termios * termios) | ||
57 | { | ||
58 | int i; | ||
59 | |||
60 | verify_area(termios, sizeof (*termios)); | ||
61 | for (i=0 ; i< (sizeof (*termios)) ; i++) | ||
62 | put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios ); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static int set_termios(struct tty_struct * tty, struct termios * termios) | ||
67 | { | ||
68 | int i; | ||
69 | |||
70 | for (i=0 ; i< (sizeof (*termios)) ; i++) | ||
71 | ((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios); | ||
72 | change_speed(tty); | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int get_termio(struct tty_struct * tty, struct termio * termio) | ||
77 | { | ||
78 | int i; | ||
79 | struct termio tmp_termio; | ||
80 | |||
81 | verify_area(termio, sizeof (*termio)); | ||
82 | tmp_termio.c_iflag = tty->termios.c_iflag; | ||
83 | tmp_termio.c_oflag = tty->termios.c_oflag; | ||
84 | tmp_termio.c_cflag = tty->termios.c_cflag; | ||
85 | tmp_termio.c_lflag = tty->termios.c_lflag; | ||
86 | tmp_termio.c_line = tty->termios.c_line; | ||
87 | for(i=0 ; i < NCC ; i++) | ||
88 | tmp_termio.c_cc[i] = tty->termios.c_cc[i]; | ||
89 | for (i=0 ; i< (sizeof (*termio)) ; i++) | ||
90 | put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio ); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * This only works as the 386 is low-byt-first | ||
96 | */ | ||
97 | static int set_termio(struct tty_struct * tty, struct termio * termio) | ||
98 | { | ||
99 | int i; | ||
100 | struct termio tmp_termio; | ||
101 | |||
102 | for (i=0 ; i< (sizeof (*termio)) ; i++) | ||
103 | ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio); | ||
104 | *(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag; | ||
105 | *(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag; | ||
106 | *(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag; | ||
107 | *(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag; | ||
108 | tty->termios.c_line = tmp_termio.c_line; | ||
109 | for(i=0 ; i < NCC ; i++) | ||
110 | tty->termios.c_cc[i] = tmp_termio.c_cc[i]; | ||
111 | change_speed(tty); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | int tty_ioctl(int dev, int cmd, int arg) | ||
116 | { | ||
117 | struct tty_struct * tty; | ||
118 | if (MAJOR(dev) == 5) { | ||
119 | dev=current->tty; | ||
120 | if (dev<0) | ||
121 | panic("tty_ioctl: dev<0"); | ||
122 | } else | ||
123 | dev=MINOR(dev); | ||
124 | tty = dev + tty_table; | ||
125 | switch (cmd) { | ||
126 | case TCGETS: | ||
127 | return get_termios(tty,(struct termios *) arg); | ||
128 | case TCSETSF: | ||
129 | flush(&tty->read_q); /* fallthrough */ | ||
130 | case TCSETSW: | ||
131 | wait_until_sent(tty); /* fallthrough */ | ||
132 | case TCSETS: | ||
133 | return set_termios(tty,(struct termios *) arg); | ||
134 | case TCGETA: | ||
135 | return get_termio(tty,(struct termio *) arg); | ||
136 | case TCSETAF: | ||
137 | flush(&tty->read_q); /* fallthrough */ | ||
138 | case TCSETAW: | ||
139 | wait_until_sent(tty); /* fallthrough */ | ||
140 | case TCSETA: | ||
141 | return set_termio(tty,(struct termio *) arg); | ||
142 | case TCSBRK: | ||
143 | if (!arg) { | ||
144 | wait_until_sent(tty); | ||
145 | send_break(tty); | ||
146 | } | ||
147 | return 0; | ||
148 | case TCXONC: | ||
149 | return -EINVAL; /* not implemented */ | ||
150 | case TCFLSH: | ||
151 | if (arg==0) | ||
152 | flush(&tty->read_q); | ||
153 | else if (arg==1) | ||
154 | flush(&tty->write_q); | ||
155 | else if (arg==2) { | ||
156 | flush(&tty->read_q); | ||
157 | flush(&tty->write_q); | ||
158 | } else | ||
159 | return -EINVAL; | ||
160 | return 0; | ||
161 | case TIOCEXCL: | ||
162 | return -EINVAL; /* not implemented */ | ||
163 | case TIOCNXCL: | ||
164 | return -EINVAL; /* not implemented */ | ||
165 | case TIOCSCTTY: | ||
166 | return -EINVAL; /* set controlling term NI */ | ||
167 | case TIOCGPGRP: | ||
168 | verify_area((void *) arg,4); | ||
169 | put_fs_long(tty->pgrp,(unsigned long *) arg); | ||
170 | return 0; | ||
171 | case TIOCSPGRP: | ||
172 | tty->pgrp=get_fs_long((unsigned long *) arg); | ||
173 | return 0; | ||
174 | case TIOCOUTQ: | ||
175 | verify_area((void *) arg,4); | ||
176 | put_fs_long(CHARS(tty->write_q),(unsigned long *) arg); | ||
177 | return 0; | ||
178 | case TIOCINQ: | ||
179 | verify_area((void *) arg,4); | ||
180 | put_fs_long(CHARS(tty->secondary), | ||
181 | (unsigned long *) arg); | ||
182 | return 0; | ||
183 | case TIOCSTI: | ||
184 | return -EINVAL; /* not implemented */ | ||
185 | case TIOCGWINSZ: | ||
186 | return -EINVAL; /* not implemented */ | ||
187 | case TIOCSWINSZ: | ||
188 | return -EINVAL; /* not implemented */ | ||
189 | case TIOCMGET: | ||
190 | return -EINVAL; /* not implemented */ | ||
191 | case TIOCMBIS: | ||
192 | return -EINVAL; /* not implemented */ | ||
193 | case TIOCMBIC: | ||
194 | return -EINVAL; /* not implemented */ | ||
195 | case TIOCMSET: | ||
196 | return -EINVAL; /* not implemented */ | ||
197 | case TIOCGSOFTCAR: | ||
198 | return -EINVAL; /* not implemented */ | ||
199 | case TIOCSSOFTCAR: | ||
200 | return -EINVAL; /* not implemented */ | ||
201 | default: | ||
202 | return -EINVAL; | ||
203 | } | ||
204 | } | ||
diff --git a/src/kernel/exit.c b/src/kernel/exit.c new file mode 100644 index 0000000..b22de34 --- /dev/null +++ b/src/kernel/exit.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | * linux/kernel/exit.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | #include <errno.h> | ||
8 | #include <signal.h> | ||
9 | #include <sys/wait.h> | ||
10 | |||
11 | #include <linux/sched.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/tty.h> | ||
14 | #include <asm/segment.h> | ||
15 | |||
16 | int sys_pause(void); | ||
17 | int sys_close(int fd); | ||
18 | |||
19 | void release(struct task_struct * p) | ||
20 | { | ||
21 | int i; | ||
22 | |||
23 | if (!p) | ||
24 | return; | ||
25 | for (i=1 ; i<NR_TASKS ; i++) | ||
26 | if (task[i]==p) { | ||
27 | task[i]=NULL; | ||
28 | free_page((long)p); | ||
29 | schedule(); | ||
30 | return; | ||
31 | } | ||
32 | panic("trying to release non-existent task"); | ||
33 | } | ||
34 | |||
35 | static inline int send_sig(long sig,struct task_struct * p,int priv) | ||
36 | { | ||
37 | if (!p || sig<1 || sig>32) | ||
38 | return -EINVAL; | ||
39 | if (priv || (current->euid==p->euid) || suser()) | ||
40 | p->signal |= (1<<(sig-1)); | ||
41 | else | ||
42 | return -EPERM; | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static void kill_session(void) | ||
47 | { | ||
48 | struct task_struct **p = NR_TASKS + task; | ||
49 | |||
50 | while (--p > &FIRST_TASK) { | ||
51 | if (*p && (*p)->session == current->session) | ||
52 | (*p)->signal |= 1<<(SIGHUP-1); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * XXX need to check permissions needed to send signals to process | ||
58 | * groups, etc. etc. kill() permissions semantics are tricky! | ||
59 | */ | ||
60 | int sys_kill(int pid,int sig) | ||
61 | { | ||
62 | struct task_struct **p = NR_TASKS + task; | ||
63 | int err, retval = 0; | ||
64 | |||
65 | if (!pid) while (--p > &FIRST_TASK) { | ||
66 | if (*p && (*p)->pgrp == current->pid) | ||
67 | if ((err=send_sig(sig,*p,1))) | ||
68 | retval = err; | ||
69 | } else if (pid>0) while (--p > &FIRST_TASK) { | ||
70 | if (*p && (*p)->pid == pid) | ||
71 | if ((err=send_sig(sig,*p,0))) | ||
72 | retval = err; | ||
73 | } else if (pid == -1) while (--p > &FIRST_TASK) { | ||
74 | if ((err = send_sig(sig,*p,0))) | ||
75 | retval = err; | ||
76 | } else while (--p > &FIRST_TASK) | ||
77 | if (*p && (*p)->pgrp == -pid) | ||
78 | if ((err = send_sig(sig,*p,0))) | ||
79 | retval = err; | ||
80 | return retval; | ||
81 | } | ||
82 | |||
83 | static void tell_father(int pid) | ||
84 | { | ||
85 | int i; | ||
86 | |||
87 | if (pid) | ||
88 | for (i=0;i<NR_TASKS;i++) { | ||
89 | if (!task[i]) | ||
90 | continue; | ||
91 | if (task[i]->pid != pid) | ||
92 | continue; | ||
93 | task[i]->signal |= (1<<(SIGCHLD-1)); | ||
94 | return; | ||
95 | } | ||
96 | /* if we don't find any fathers, we just release ourselves */ | ||
97 | /* This is not really OK. Must change it to make father 1 */ | ||
98 | printk("BAD BAD - no father found\n\r"); | ||
99 | release(current); | ||
100 | } | ||
101 | |||
102 | int do_exit(long code) | ||
103 | { | ||
104 | int i; | ||
105 | free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); | ||
106 | free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); | ||
107 | for (i=0 ; i<NR_TASKS ; i++) | ||
108 | if (task[i] && task[i]->father == current->pid) { | ||
109 | task[i]->father = 1; | ||
110 | if (task[i]->state == TASK_ZOMBIE) | ||
111 | /* assumption task[1] is always init */ | ||
112 | (void) send_sig(SIGCHLD, task[1], 1); | ||
113 | } | ||
114 | for (i=0 ; i<NR_OPEN ; i++) | ||
115 | if (current->filp[i]) | ||
116 | sys_close(i); | ||
117 | iput(current->pwd); | ||
118 | current->pwd=NULL; | ||
119 | iput(current->root); | ||
120 | current->root=NULL; | ||
121 | iput(current->executable); | ||
122 | current->executable=NULL; | ||
123 | if (current->leader && current->tty >= 0) | ||
124 | tty_table[current->tty].pgrp = 0; | ||
125 | if (last_task_used_math == current) | ||
126 | last_task_used_math = NULL; | ||
127 | if (current->leader) | ||
128 | kill_session(); | ||
129 | current->state = TASK_ZOMBIE; | ||
130 | current->exit_code = code; | ||
131 | tell_father(current->father); | ||
132 | schedule(); | ||
133 | return (-1); /* just to suppress warnings */ | ||
134 | } | ||
135 | |||
136 | int sys_exit(int error_code) | ||
137 | { | ||
138 | return do_exit((error_code&0xff)<<8); | ||
139 | } | ||
140 | |||
141 | int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) | ||
142 | { | ||
143 | int flag, code; | ||
144 | struct task_struct ** p; | ||
145 | |||
146 | verify_area(stat_addr,4); | ||
147 | repeat: | ||
148 | flag=0; | ||
149 | for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) { | ||
150 | if (!*p || *p == current) | ||
151 | continue; | ||
152 | if ((*p)->father != current->pid) | ||
153 | continue; | ||
154 | if (pid>0) { | ||
155 | if ((*p)->pid != pid) | ||
156 | continue; | ||
157 | } else if (!pid) { | ||
158 | if ((*p)->pgrp != current->pgrp) | ||
159 | continue; | ||
160 | } else if (pid != -1) { | ||
161 | if ((*p)->pgrp != -pid) | ||
162 | continue; | ||
163 | } | ||
164 | switch ((*p)->state) { | ||
165 | case TASK_STOPPED: | ||
166 | if (!(options & WUNTRACED)) | ||
167 | continue; | ||
168 | put_fs_long(0x7f,stat_addr); | ||
169 | return (*p)->pid; | ||
170 | case TASK_ZOMBIE: | ||
171 | current->cutime += (*p)->utime; | ||
172 | current->cstime += (*p)->stime; | ||
173 | flag = (*p)->pid; | ||
174 | code = (*p)->exit_code; | ||
175 | release(*p); | ||
176 | put_fs_long(code,stat_addr); | ||
177 | return flag; | ||
178 | default: | ||
179 | flag=1; | ||
180 | continue; | ||
181 | } | ||
182 | } | ||
183 | if (flag) { | ||
184 | if (options & WNOHANG) | ||
185 | return 0; | ||
186 | current->state=TASK_INTERRUPTIBLE; | ||
187 | schedule(); | ||
188 | if (!(current->signal &= ~(1<<(SIGCHLD-1)))) | ||
189 | goto repeat; | ||
190 | else | ||
191 | return -EINTR; | ||
192 | } | ||
193 | return -ECHILD; | ||
194 | } | ||
195 | |||
196 | |||
diff --git a/src/kernel/fork.c b/src/kernel/fork.c new file mode 100644 index 0000000..018a501 --- /dev/null +++ b/src/kernel/fork.c | |||
@@ -0,0 +1,152 @@ | |||
1 | /* | ||
2 | * linux/kernel/fork.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * 'fork.c' contains the help-routines for the 'fork' system call | ||
9 | * (see also system_call.s), and some misc functions ('verify_area'). | ||
10 | * Fork is rather simple, once you get the hang of it, but the memory | ||
11 | * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' | ||
12 | */ | ||
13 | #include <string.h> | ||
14 | #include <errno.h> | ||
15 | |||
16 | #include <linux/sched.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <asm/segment.h> | ||
19 | #include <asm/system.h> | ||
20 | |||
21 | extern void write_verify(unsigned long address); | ||
22 | |||
23 | long last_pid=0; | ||
24 | |||
25 | void verify_area(void * addr,int size) | ||
26 | { | ||
27 | unsigned long start; | ||
28 | |||
29 | start = (unsigned long) addr; | ||
30 | size += start & 0xfff; | ||
31 | start &= 0xfffff000; | ||
32 | start += get_base(current->ldt[2]); | ||
33 | while (size>0) { | ||
34 | size -= 4096; | ||
35 | write_verify(start); | ||
36 | start += 4096; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | int copy_mem(int nr,struct task_struct * p) | ||
41 | { | ||
42 | unsigned long old_data_base,new_data_base,data_limit; | ||
43 | unsigned long old_code_base,new_code_base,code_limit; | ||
44 | |||
45 | code_limit=get_limit(0x0f); | ||
46 | data_limit=get_limit(0x17); | ||
47 | old_code_base = get_base(current->ldt[1]); | ||
48 | old_data_base = get_base(current->ldt[2]); | ||
49 | if (old_data_base != old_code_base) | ||
50 | panic("We don't support separate I&D"); | ||
51 | if (data_limit < code_limit) | ||
52 | panic("Bad data_limit"); | ||
53 | new_data_base = new_code_base = nr * 0x4000000; | ||
54 | p->start_code = new_code_base; | ||
55 | set_base(p->ldt[1],new_code_base); | ||
56 | set_base(p->ldt[2],new_data_base); | ||
57 | if (copy_page_tables(old_data_base,new_data_base,data_limit)) { | ||
58 | printk("free_page_tables: from copy_mem\n"); | ||
59 | free_page_tables(new_data_base,data_limit); | ||
60 | return -ENOMEM; | ||
61 | } | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * Ok, this is the main fork-routine. It copies the system process | ||
67 | * information (task[nr]) and sets up the necessary registers. It | ||
68 | * also copies the data segment in it's entirety. | ||
69 | */ | ||
70 | int copy_process(int nr,long ebp,long edi,long esi,long gs,long none, | ||
71 | long ebx,long ecx,long edx, | ||
72 | long fs,long es,long ds, | ||
73 | long eip,long cs,long eflags,long esp,long ss) | ||
74 | { | ||
75 | struct task_struct *p; | ||
76 | int i; | ||
77 | struct file *f; | ||
78 | |||
79 | p = (struct task_struct *) get_free_page(); | ||
80 | if (!p) | ||
81 | return -EAGAIN; | ||
82 | task[nr] = p; | ||
83 | |||
84 | // NOTE!: the following statement now work with gcc 4.3.2 now, and you | ||
85 | // must compile _THIS_ memcpy without no -O of gcc.#ifndef GCC4_3 | ||
86 | *p = *current; /* NOTE! this doesn't copy the supervisor stack */ | ||
87 | p->state = TASK_UNINTERRUPTIBLE; | ||
88 | p->pid = last_pid; | ||
89 | p->father = current->pid; | ||
90 | p->counter = p->priority; | ||
91 | p->signal = 0; | ||
92 | p->alarm = 0; | ||
93 | p->leader = 0; /* process leadership doesn't inherit */ | ||
94 | p->utime = p->stime = 0; | ||
95 | p->cutime = p->cstime = 0; | ||
96 | p->start_time = jiffies; | ||
97 | p->tss.back_link = 0; | ||
98 | p->tss.esp0 = PAGE_SIZE + (long) p; | ||
99 | p->tss.ss0 = 0x10; | ||
100 | p->tss.eip = eip; | ||
101 | p->tss.eflags = eflags; | ||
102 | p->tss.eax = 0; | ||
103 | p->tss.ecx = ecx; | ||
104 | p->tss.edx = edx; | ||
105 | p->tss.ebx = ebx; | ||
106 | p->tss.esp = esp; | ||
107 | p->tss.ebp = ebp; | ||
108 | p->tss.esi = esi; | ||
109 | p->tss.edi = edi; | ||
110 | p->tss.es = es & 0xffff; | ||
111 | p->tss.cs = cs & 0xffff; | ||
112 | p->tss.ss = ss & 0xffff; | ||
113 | p->tss.ds = ds & 0xffff; | ||
114 | p->tss.fs = fs & 0xffff; | ||
115 | p->tss.gs = gs & 0xffff; | ||
116 | p->tss.ldt = _LDT(nr); | ||
117 | p->tss.trace_bitmap = 0x80000000; | ||
118 | if (last_task_used_math == current) | ||
119 | __asm__("clts ; fnsave %0"::"m" (p->tss.i387)); | ||
120 | if (copy_mem(nr,p)) { | ||
121 | task[nr] = NULL; | ||
122 | free_page((long) p); | ||
123 | return -EAGAIN; | ||
124 | } | ||
125 | for (i=0; i<NR_OPEN;i++) | ||
126 | if ((f=p->filp[i])) | ||
127 | f->f_count++; | ||
128 | if (current->pwd) | ||
129 | current->pwd->i_count++; | ||
130 | if (current->root) | ||
131 | current->root->i_count++; | ||
132 | if (current->executable) | ||
133 | current->executable->i_count++; | ||
134 | set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); | ||
135 | set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt)); | ||
136 | p->state = TASK_RUNNING; /* do this last, just in case */ | ||
137 | return last_pid; | ||
138 | } | ||
139 | |||
140 | int find_empty_process(void) | ||
141 | { | ||
142 | int i; | ||
143 | |||
144 | repeat: | ||
145 | if ((++last_pid)<0) last_pid=1; | ||
146 | for(i=0 ; i<NR_TASKS ; i++) | ||
147 | if (task[i] && task[i]->pid == last_pid) goto repeat; | ||
148 | for(i=1 ; i<NR_TASKS ; i++) | ||
149 | if (!task[i]) | ||
150 | return i; | ||
151 | return -EAGAIN; | ||
152 | } | ||
diff --git a/src/kernel/math/Makefile b/src/kernel/math/Makefile new file mode 100644 index 0000000..b79ddd7 --- /dev/null +++ b/src/kernel/math/Makefile | |||
@@ -0,0 +1,39 @@ | |||
1 | # | ||
2 | # Makefile for the FREAX-kernel character device drivers. | ||
3 | # | ||
4 | # Note! Dependencies are done automagically by 'make dep', which also | ||
5 | # removes any old dependencies. DON'T put your own dependencies here | ||
6 | # unless it's something special (ie not a .c file). | ||
7 | # | ||
8 | |||
9 | include ../../Makefile.header | ||
10 | |||
11 | CFLAGS += -I../../include | ||
12 | CPP += -I../../include | ||
13 | |||
14 | .c.s: | ||
15 | @$(CC) $(CFLAGS) \ | ||
16 | -S -o $*.s $< | ||
17 | .s.o: | ||
18 | @$(AS) -o $*.o $< | ||
19 | .c.o: | ||
20 | @$(CC) $(CFLAGS) \ | ||
21 | -c -o $*.o $< | ||
22 | |||
23 | OBJS = math_emulate.o | ||
24 | |||
25 | math.a: $(OBJS) | ||
26 | @$(AR) rcs math.a $(OBJS) | ||
27 | @sync | ||
28 | |||
29 | clean: | ||
30 | @rm -f core *.o *.a tmp_make | ||
31 | @for i in *.c;do rm -f `basename $$i .c`.s;done | ||
32 | |||
33 | dep: | ||
34 | @sed '/\#\#\# Dependencies/q' < Makefile > tmp_make | ||
35 | @(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ | ||
36 | $(CPP) -M $$i;done) >> tmp_make | ||
37 | @cp tmp_make Makefile | ||
38 | |||
39 | ### Dependencies: | ||
diff --git a/src/kernel/math/math_emulate.c b/src/kernel/math/math_emulate.c new file mode 100644 index 0000000..825e528 --- /dev/null +++ b/src/kernel/math/math_emulate.c | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * linux/kernel/math/math_emulate.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This directory should contain the math-emulation code. | ||
9 | * Currently only results in a signal. | ||
10 | */ | ||
11 | |||
12 | #include <signal.h> | ||
13 | |||
14 | #include <linux/sched.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <asm/segment.h> | ||
17 | |||
18 | void math_emulate(long edi, long esi, long ebp, long sys_call_ret, | ||
19 | long eax,long ebx,long ecx,long edx, | ||
20 | unsigned short fs,unsigned short es,unsigned short ds, | ||
21 | unsigned long eip,unsigned short cs,unsigned long eflags, | ||
22 | unsigned short ss, unsigned long esp) | ||
23 | { | ||
24 | unsigned char first, second; | ||
25 | |||
26 | /* 0x0007 means user code space */ | ||
27 | if (cs != 0x000F) { | ||
28 | printk("math_emulate: %04x:%08x\n\r",cs,eip); | ||
29 | panic("Math emulation needed in kernel"); | ||
30 | } | ||
31 | first = get_fs_byte((char *)((*&eip)++)); | ||
32 | second = get_fs_byte((char *)((*&eip)++)); | ||
33 | printk("%04x:%08x %02x %02x\n\r",cs,eip-2,first,second); | ||
34 | current->signal |= 1<<(SIGFPE-1); | ||
35 | } | ||
36 | |||
37 | void math_error(void) | ||
38 | { | ||
39 | __asm__("fnclex"); | ||
40 | if (last_task_used_math) | ||
41 | last_task_used_math->signal |= 1<<(SIGFPE-1); | ||
42 | } | ||
diff --git a/src/kernel/mktime.c b/src/kernel/mktime.c new file mode 100644 index 0000000..d125ded --- /dev/null +++ b/src/kernel/mktime.c | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * linux/kernel/mktime.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | #include <time.h> | ||
8 | |||
9 | /* | ||
10 | * This isn't the library routine, it is only used in the kernel. | ||
11 | * as such, we don't care about years<1970 etc, but assume everything | ||
12 | * is ok. Similarly, TZ etc is happily ignored. We just do everything | ||
13 | * as easily as possible. Let's find something public for the library | ||
14 | * routines (although I think minix times is public). | ||
15 | */ | ||
16 | /* | ||
17 | * PS. I hate whoever though up the year 1970 - couldn't they have gotten | ||
18 | * a leap-year instead? I also hate Gregorius, pope or no. I'm grumpy. | ||
19 | */ | ||
20 | #define MINUTE 60 | ||
21 | #define HOUR (60*MINUTE) | ||
22 | #define DAY (24*HOUR) | ||
23 | #define YEAR (365*DAY) | ||
24 | |||
25 | /* interestingly, we assume leap-years */ | ||
26 | static int month[12] = { | ||
27 | 0, | ||
28 | DAY*(31), | ||
29 | DAY*(31+29), | ||
30 | DAY*(31+29+31), | ||
31 | DAY*(31+29+31+30), | ||
32 | DAY*(31+29+31+30+31), | ||
33 | DAY*(31+29+31+30+31+30), | ||
34 | DAY*(31+29+31+30+31+30+31), | ||
35 | DAY*(31+29+31+30+31+30+31+31), | ||
36 | DAY*(31+29+31+30+31+30+31+31+30), | ||
37 | DAY*(31+29+31+30+31+30+31+31+30+31), | ||
38 | DAY*(31+29+31+30+31+30+31+31+30+31+30) | ||
39 | }; | ||
40 | |||
41 | long kernel_mktime(struct tm * tm) | ||
42 | { | ||
43 | long res; | ||
44 | int year; | ||
45 | if (tm->tm_year >= 70) | ||
46 | year = tm->tm_year - 70; | ||
47 | else | ||
48 | year = tm->tm_year + 100 -70; /* Y2K bug fix by hellotigercn 20110803 */ | ||
49 | /* magic offsets (y+1) needed to get leapyears right.*/ | ||
50 | res = YEAR*year + DAY*((year+1)/4); | ||
51 | res += month[tm->tm_mon]; | ||
52 | /* and (y+2) here. If it wasn't a leap-year, we have to adjust */ | ||
53 | if (tm->tm_mon>1 && ((year+2)%4)) | ||
54 | res -= DAY; | ||
55 | res += DAY*(tm->tm_mday-1); | ||
56 | res += HOUR*tm->tm_hour; | ||
57 | res += MINUTE*tm->tm_min; | ||
58 | res += tm->tm_sec; | ||
59 | return res; | ||
60 | } | ||
diff --git a/src/kernel/panic.c b/src/kernel/panic.c new file mode 100644 index 0000000..9a40e45 --- /dev/null +++ b/src/kernel/panic.c | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * linux/kernel/panic.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This function is used through-out the kernel (includeinh mm and fs) | ||
9 | * to indicate a major problem. | ||
10 | */ | ||
11 | #define PANIC | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | |||
16 | void sys_sync(void); /* it's really int */ | ||
17 | |||
18 | void panic(const char * s) | ||
19 | { | ||
20 | printk("Kernel panic: %s\n\r",s); | ||
21 | if (current == task[0]) | ||
22 | printk("In swapper task - not syncing\n\r"); | ||
23 | else | ||
24 | sys_sync(); | ||
25 | for(;;); | ||
26 | } | ||
diff --git a/src/kernel/printk.c b/src/kernel/printk.c new file mode 100644 index 0000000..0daa097 --- /dev/null +++ b/src/kernel/printk.c | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * linux/kernel/printk.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * When in kernel-mode, we cannot use printf, as fs is liable to | ||
9 | * point to 'interesting' things. Make a printf with fs-saving, and | ||
10 | * all is well. | ||
11 | */ | ||
12 | #include <stdarg.h> | ||
13 | #include <stddef.h> | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | |||
17 | static char buf[1024]; | ||
18 | |||
19 | extern int vsprintf(char * buf, const char * fmt, va_list args); | ||
20 | |||
21 | int printk(const char *fmt, ...) | ||
22 | { | ||
23 | va_list args; | ||
24 | int i; | ||
25 | |||
26 | va_start(args, fmt); | ||
27 | i=vsprintf(buf,fmt,args); | ||
28 | va_end(args); | ||
29 | __asm__("push %%fs\n\t" | ||
30 | "push %%ds\n\t" | ||
31 | "pop %%fs\n\t" | ||
32 | "pushl %0\n\t" | ||
33 | "pushl $buf\n\t" | ||
34 | "pushl $0\n\t" | ||
35 | "call tty_write\n\t" | ||
36 | "addl $8,%%esp\n\t" | ||
37 | "popl %0\n\t" | ||
38 | "pop %%fs" | ||
39 | ::"r" (i):"ax","cx","dx"); | ||
40 | return i; | ||
41 | } | ||
diff --git a/src/kernel/sched.c b/src/kernel/sched.c new file mode 100644 index 0000000..15d839b --- /dev/null +++ b/src/kernel/sched.c | |||
@@ -0,0 +1,412 @@ | |||
1 | /* | ||
2 | * linux/kernel/sched.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * 'sched.c' is the main kernel file. It contains scheduling primitives | ||
9 | * (sleep_on, wakeup, schedule etc) as well as a number of simple system | ||
10 | * call functions (type getpid(), which just extracts a field from | ||
11 | * current-task | ||
12 | */ | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/sys.h> | ||
16 | #include <linux/fdreg.h> | ||
17 | #include <asm/system.h> | ||
18 | #include <asm/io.h> | ||
19 | #include <asm/segment.h> | ||
20 | |||
21 | #include <signal.h> | ||
22 | |||
23 | #define _S(nr) (1<<((nr)-1)) | ||
24 | #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) | ||
25 | |||
26 | void show_task(int nr,struct task_struct * p) | ||
27 | { | ||
28 | int i,j = 4096-sizeof(struct task_struct); | ||
29 | |||
30 | printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state); | ||
31 | i=0; | ||
32 | while (i<j && !((char *)(p+1))[i]) | ||
33 | i++; | ||
34 | printk("%d (of %d) chars free in kernel stack\n\r",i,j); | ||
35 | } | ||
36 | |||
37 | void show_stat(void) | ||
38 | { | ||
39 | int i; | ||
40 | |||
41 | for (i=0;i<NR_TASKS;i++) | ||
42 | if (task[i]) | ||
43 | show_task(i,task[i]); | ||
44 | } | ||
45 | |||
46 | #define LATCH (1193180/HZ) | ||
47 | |||
48 | extern void mem_use(void); | ||
49 | |||
50 | extern int timer_interrupt(void); | ||
51 | extern int system_call(void); | ||
52 | |||
53 | union task_union { | ||
54 | struct task_struct task; | ||
55 | char stack[PAGE_SIZE]; | ||
56 | }; | ||
57 | |||
58 | static union task_union init_task = {INIT_TASK,}; | ||
59 | |||
60 | long volatile jiffies=0; | ||
61 | long startup_time=0; | ||
62 | struct task_struct *current = &(init_task.task); | ||
63 | struct task_struct *last_task_used_math = NULL; | ||
64 | |||
65 | struct task_struct * task[NR_TASKS] = {&(init_task.task), }; | ||
66 | |||
67 | long user_stack [ PAGE_SIZE>>2 ] ; | ||
68 | |||
69 | struct { | ||
70 | long * a; | ||
71 | short b; | ||
72 | } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 }; | ||
73 | /* | ||
74 | * 'math_state_restore()' saves the current math information in the | ||
75 | * old math state array, and gets the new ones from the current task | ||
76 | */ | ||
77 | void math_state_restore() | ||
78 | { | ||
79 | if (last_task_used_math == current) | ||
80 | return; | ||
81 | __asm__("fwait"); | ||
82 | if (last_task_used_math) { | ||
83 | __asm__("fnsave %0"::"m" (last_task_used_math->tss.i387)); | ||
84 | } | ||
85 | last_task_used_math=current; | ||
86 | if (current->used_math) { | ||
87 | __asm__("frstor %0"::"m" (current->tss.i387)); | ||
88 | } else { | ||
89 | __asm__("fninit"::); | ||
90 | current->used_math=1; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * 'schedule()' is the scheduler function. This is GOOD CODE! There | ||
96 | * probably won't be any reason to change this, as it should work well | ||
97 | * in all circumstances (ie gives IO-bound processes good response etc). | ||
98 | * The one thing you might take a look at is the signal-handler code here. | ||
99 | * | ||
100 | * NOTE!! Task 0 is the 'idle' task, which gets called when no other | ||
101 | * tasks can run. It can not be killed, and it cannot sleep. The 'state' | ||
102 | * information in task[0] is never used. | ||
103 | */ | ||
104 | void schedule(void) | ||
105 | { | ||
106 | int i,next,c; | ||
107 | struct task_struct ** p; | ||
108 | |||
109 | /* check alarm, wake up any interruptible tasks that have got a signal */ | ||
110 | |||
111 | for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) | ||
112 | if (*p) { | ||
113 | if ((*p)->alarm && (*p)->alarm < jiffies) { | ||
114 | (*p)->signal |= (1<<(SIGALRM-1)); | ||
115 | (*p)->alarm = 0; | ||
116 | } | ||
117 | if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) && | ||
118 | (*p)->state==TASK_INTERRUPTIBLE) | ||
119 | (*p)->state=TASK_RUNNING; | ||
120 | } | ||
121 | |||
122 | /* this is the scheduler proper: */ | ||
123 | |||
124 | while (1) { | ||
125 | c = -1; | ||
126 | next = 0; | ||
127 | i = NR_TASKS; | ||
128 | p = &task[NR_TASKS]; | ||
129 | while (--i) { | ||
130 | if (!*--p) | ||
131 | continue; | ||
132 | if ((*p)->state == TASK_RUNNING && (*p)->counter > c) | ||
133 | c = (*p)->counter, next = i; | ||
134 | } | ||
135 | if (c) break; | ||
136 | for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) | ||
137 | if (*p) | ||
138 | (*p)->counter = ((*p)->counter >> 1) + | ||
139 | (*p)->priority; | ||
140 | } | ||
141 | switch_to(next); | ||
142 | } | ||
143 | |||
144 | int sys_pause(void) | ||
145 | { | ||
146 | current->state = TASK_INTERRUPTIBLE; | ||
147 | schedule(); | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | void sleep_on(struct task_struct **p) | ||
152 | { | ||
153 | struct task_struct *tmp; | ||
154 | |||
155 | if (!p) | ||
156 | return; | ||
157 | if (current == &(init_task.task)) | ||
158 | panic("task[0] trying to sleep"); | ||
159 | tmp = *p; | ||
160 | *p = current; | ||
161 | current->state = TASK_UNINTERRUPTIBLE; | ||
162 | schedule(); | ||
163 | if (tmp) | ||
164 | tmp->state=0; | ||
165 | } | ||
166 | |||
167 | void interruptible_sleep_on(struct task_struct **p) | ||
168 | { | ||
169 | struct task_struct *tmp; | ||
170 | |||
171 | if (!p) | ||
172 | return; | ||
173 | if (current == &(init_task.task)) | ||
174 | panic("task[0] trying to sleep"); | ||
175 | tmp=*p; | ||
176 | *p=current; | ||
177 | repeat: current->state = TASK_INTERRUPTIBLE; | ||
178 | schedule(); | ||
179 | if (*p && *p != current) { | ||
180 | (**p).state=0; | ||
181 | goto repeat; | ||
182 | } | ||
183 | *p=NULL; | ||
184 | if (tmp) | ||
185 | tmp->state=0; | ||
186 | } | ||
187 | |||
188 | void wake_up(struct task_struct **p) | ||
189 | { | ||
190 | if (p && *p) { | ||
191 | (**p).state=0; | ||
192 | *p=NULL; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * OK, here are some floppy things that shouldn't be in the kernel | ||
198 | * proper. They are here because the floppy needs a timer, and this | ||
199 | * was the easiest way of doing it. | ||
200 | */ | ||
201 | static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL}; | ||
202 | static int mon_timer[4]={0,0,0,0}; | ||
203 | static int moff_timer[4]={0,0,0,0}; | ||
204 | unsigned char current_DOR = 0x0C; | ||
205 | |||
206 | int ticks_to_floppy_on(unsigned int nr) | ||
207 | { | ||
208 | extern unsigned char selected; | ||
209 | unsigned char mask = 0x10 << nr; | ||
210 | |||
211 | if (nr>3) | ||
212 | panic("floppy_on: nr>3"); | ||
213 | moff_timer[nr]=10000; /* 100 s = very big :-) */ | ||
214 | cli(); /* use floppy_off to turn it off */ | ||
215 | mask |= current_DOR; | ||
216 | if (!selected) { | ||
217 | mask &= 0xFC; | ||
218 | mask |= nr; | ||
219 | } | ||
220 | if (mask != current_DOR) { | ||
221 | outb(mask,FD_DOR); | ||
222 | if ((mask ^ current_DOR) & 0xf0) | ||
223 | mon_timer[nr] = HZ/2; | ||
224 | else if (mon_timer[nr] < 2) | ||
225 | mon_timer[nr] = 2; | ||
226 | current_DOR = mask; | ||
227 | } | ||
228 | sti(); | ||
229 | return mon_timer[nr]; | ||
230 | } | ||
231 | |||
232 | void floppy_on(unsigned int nr) | ||
233 | { | ||
234 | cli(); | ||
235 | while (ticks_to_floppy_on(nr)) | ||
236 | sleep_on(nr+wait_motor); | ||
237 | sti(); | ||
238 | } | ||
239 | |||
240 | void floppy_off(unsigned int nr) | ||
241 | { | ||
242 | moff_timer[nr]=3*HZ; | ||
243 | } | ||
244 | |||
245 | void do_floppy_timer(void) | ||
246 | { | ||
247 | int i; | ||
248 | unsigned char mask = 0x10; | ||
249 | |||
250 | for (i=0 ; i<4 ; i++,mask <<= 1) { | ||
251 | if (!(mask & current_DOR)) | ||
252 | continue; | ||
253 | if (mon_timer[i]) { | ||
254 | if (!--mon_timer[i]) | ||
255 | wake_up(i+wait_motor); | ||
256 | } else if (!moff_timer[i]) { | ||
257 | current_DOR &= ~mask; | ||
258 | outb(current_DOR,FD_DOR); | ||
259 | } else | ||
260 | moff_timer[i]--; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | #define TIME_REQUESTS 64 | ||
265 | |||
266 | static struct timer_list { | ||
267 | long jiffies; | ||
268 | void (*fn)(); | ||
269 | struct timer_list * next; | ||
270 | } timer_list[TIME_REQUESTS], * next_timer = NULL; | ||
271 | |||
272 | void add_timer(long jiffies, void (*fn)(void)) | ||
273 | { | ||
274 | struct timer_list * p; | ||
275 | |||
276 | if (!fn) | ||
277 | return; | ||
278 | cli(); | ||
279 | if (jiffies <= 0) | ||
280 | (fn)(); | ||
281 | else { | ||
282 | for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++) | ||
283 | if (!p->fn) | ||
284 | break; | ||
285 | if (p >= timer_list + TIME_REQUESTS) | ||
286 | panic("No more time requests free"); | ||
287 | p->fn = fn; | ||
288 | p->jiffies = jiffies; | ||
289 | p->next = next_timer; | ||
290 | next_timer = p; | ||
291 | while (p->next && p->next->jiffies < p->jiffies) { | ||
292 | p->jiffies -= p->next->jiffies; | ||
293 | fn = p->fn; | ||
294 | p->fn = p->next->fn; | ||
295 | p->next->fn = fn; | ||
296 | jiffies = p->jiffies; | ||
297 | p->jiffies = p->next->jiffies; | ||
298 | p->next->jiffies = jiffies; | ||
299 | p = p->next; | ||
300 | } | ||
301 | } | ||
302 | sti(); | ||
303 | } | ||
304 | |||
305 | void do_timer(long cpl) | ||
306 | { | ||
307 | extern int beepcount; | ||
308 | extern void sysbeepstop(void); | ||
309 | |||
310 | if (beepcount) | ||
311 | if (!--beepcount) | ||
312 | sysbeepstop(); | ||
313 | |||
314 | if (cpl) | ||
315 | current->utime++; | ||
316 | else | ||
317 | current->stime++; | ||
318 | |||
319 | if (next_timer) { | ||
320 | next_timer->jiffies--; | ||
321 | while (next_timer && next_timer->jiffies <= 0) { | ||
322 | void (*fn)(void); | ||
323 | |||
324 | fn = next_timer->fn; | ||
325 | next_timer->fn = NULL; | ||
326 | next_timer = next_timer->next; | ||
327 | (fn)(); | ||
328 | } | ||
329 | } | ||
330 | if (current_DOR & 0xf0) | ||
331 | do_floppy_timer(); | ||
332 | if ((--current->counter)>0) return; | ||
333 | current->counter=0; | ||
334 | if (!cpl) return; | ||
335 | schedule(); | ||
336 | } | ||
337 | |||
338 | int sys_alarm(long seconds) | ||
339 | { | ||
340 | int old = current->alarm; | ||
341 | |||
342 | if (old) | ||
343 | old = (old - jiffies) / HZ; | ||
344 | current->alarm = (seconds>0)?(jiffies+HZ*seconds):0; | ||
345 | return (old); | ||
346 | } | ||
347 | |||
348 | int sys_getpid(void) | ||
349 | { | ||
350 | return current->pid; | ||
351 | } | ||
352 | |||
353 | int sys_getppid(void) | ||
354 | { | ||
355 | return current->father; | ||
356 | } | ||
357 | |||
358 | int sys_getuid(void) | ||
359 | { | ||
360 | return current->uid; | ||
361 | } | ||
362 | |||
363 | int sys_geteuid(void) | ||
364 | { | ||
365 | return current->euid; | ||
366 | } | ||
367 | |||
368 | int sys_getgid(void) | ||
369 | { | ||
370 | return current->gid; | ||
371 | } | ||
372 | |||
373 | int sys_getegid(void) | ||
374 | { | ||
375 | return current->egid; | ||
376 | } | ||
377 | |||
378 | int sys_nice(long increment) | ||
379 | { | ||
380 | if (current->priority-increment>0) | ||
381 | current->priority -= increment; | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | void sched_init(void) | ||
386 | { | ||
387 | int i; | ||
388 | struct desc_struct * p; | ||
389 | |||
390 | if (sizeof(struct sigaction) != 16) | ||
391 | panic("Struct sigaction MUST be 16 bytes"); | ||
392 | set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); | ||
393 | set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); | ||
394 | p = gdt+2+FIRST_TSS_ENTRY; | ||
395 | for(i=1;i<NR_TASKS;i++) { | ||
396 | task[i] = NULL; | ||
397 | p->a=p->b=0; | ||
398 | p++; | ||
399 | p->a=p->b=0; | ||
400 | p++; | ||
401 | } | ||
402 | /* Clear NT, so that we won't have troubles with that later on */ | ||
403 | __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); | ||
404 | ltr(0); | ||
405 | lldt(0); | ||
406 | outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */ | ||
407 | outb_p(LATCH & 0xff , 0x40); /* LSB */ | ||
408 | outb(LATCH >> 8 , 0x40); /* MSB */ | ||
409 | set_intr_gate(0x20,&timer_interrupt); | ||
410 | outb(inb_p(0x21)&~0x01,0x21); | ||
411 | set_system_gate(0x80,&system_call); | ||
412 | } | ||
diff --git a/src/kernel/signal.c b/src/kernel/signal.c new file mode 100644 index 0000000..c6afb3a --- /dev/null +++ b/src/kernel/signal.c | |||
@@ -0,0 +1,119 @@ | |||
1 | /* | ||
2 | * linux/kernel/signal.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | #include <linux/sched.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <asm/segment.h> | ||
10 | |||
11 | #include <signal.h> | ||
12 | |||
13 | void do_exit(int error_code); | ||
14 | |||
15 | int sys_sgetmask() | ||
16 | { | ||
17 | return current->blocked; | ||
18 | } | ||
19 | |||
20 | int sys_ssetmask(int newmask) | ||
21 | { | ||
22 | int old=current->blocked; | ||
23 | |||
24 | current->blocked = newmask & ~(1<<(SIGKILL-1)); | ||
25 | return old; | ||
26 | } | ||
27 | |||
28 | static inline void save_old(char * from,char * to) | ||
29 | { | ||
30 | int i; | ||
31 | |||
32 | verify_area(to, sizeof(struct sigaction)); | ||
33 | for (i=0 ; i< sizeof(struct sigaction) ; i++) { | ||
34 | put_fs_byte(*from,to); | ||
35 | from++; | ||
36 | to++; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | static inline void get_new(char * from,char * to) | ||
41 | { | ||
42 | int i; | ||
43 | |||
44 | for (i=0 ; i< sizeof(struct sigaction) ; i++) | ||
45 | *(to++) = get_fs_byte(from++); | ||
46 | } | ||
47 | |||
48 | int sys_signal(int signum, long handler, long restorer) | ||
49 | { | ||
50 | struct sigaction tmp; | ||
51 | |||
52 | if (signum<1 || signum>32 || signum==SIGKILL) | ||
53 | return -1; | ||
54 | tmp.sa_handler = (void (*)(int)) handler; | ||
55 | tmp.sa_mask = 0; | ||
56 | tmp.sa_flags = SA_ONESHOT | SA_NOMASK; | ||
57 | tmp.sa_restorer = (void (*)(void)) restorer; | ||
58 | handler = (long) current->sigaction[signum-1].sa_handler; | ||
59 | current->sigaction[signum-1] = tmp; | ||
60 | return handler; | ||
61 | } | ||
62 | |||
63 | int sys_sigaction(int signum, const struct sigaction * action, | ||
64 | struct sigaction * oldaction) | ||
65 | { | ||
66 | struct sigaction tmp; | ||
67 | |||
68 | if (signum<1 || signum>32 || signum==SIGKILL) | ||
69 | return -1; | ||
70 | tmp = current->sigaction[signum-1]; | ||
71 | get_new((char *) action, | ||
72 | (char *) (signum-1+current->sigaction)); | ||
73 | if (oldaction) | ||
74 | save_old((char *) &tmp,(char *) oldaction); | ||
75 | if (current->sigaction[signum-1].sa_flags & SA_NOMASK) | ||
76 | current->sigaction[signum-1].sa_mask = 0; | ||
77 | else | ||
78 | current->sigaction[signum-1].sa_mask |= (1<<(signum-1)); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | void do_signal(long signr,long eax, long ebx, long ecx, long edx, | ||
83 | long fs, long es, long ds, | ||
84 | long eip, long cs, long eflags, | ||
85 | unsigned long * esp, long ss) | ||
86 | { | ||
87 | unsigned long sa_handler; | ||
88 | long old_eip=eip; | ||
89 | struct sigaction * sa = current->sigaction + signr - 1; | ||
90 | int longs; | ||
91 | unsigned long * tmp_esp; | ||
92 | |||
93 | sa_handler = (unsigned long) sa->sa_handler; | ||
94 | if (sa_handler==1) | ||
95 | return; | ||
96 | if (!sa_handler) { | ||
97 | if (signr==SIGCHLD) | ||
98 | return; | ||
99 | else | ||
100 | do_exit(1<<(signr-1)); | ||
101 | } | ||
102 | if (sa->sa_flags & SA_ONESHOT) | ||
103 | sa->sa_handler = NULL; | ||
104 | *(&eip) = sa_handler; | ||
105 | longs = (sa->sa_flags & SA_NOMASK)?7:8; | ||
106 | *(&esp) -= longs; | ||
107 | verify_area(esp,longs*4); | ||
108 | tmp_esp=esp; | ||
109 | put_fs_long((long) sa->sa_restorer,tmp_esp++); | ||
110 | put_fs_long(signr,tmp_esp++); | ||
111 | if (!(sa->sa_flags & SA_NOMASK)) | ||
112 | put_fs_long(current->blocked,tmp_esp++); | ||
113 | put_fs_long(eax,tmp_esp++); | ||
114 | put_fs_long(ecx,tmp_esp++); | ||
115 | put_fs_long(edx,tmp_esp++); | ||
116 | put_fs_long(eflags,tmp_esp++); | ||
117 | put_fs_long(old_eip,tmp_esp++); | ||
118 | current->blocked |= sa->sa_mask; | ||
119 | } | ||
diff --git a/src/kernel/sys.c b/src/kernel/sys.c new file mode 100644 index 0000000..2c01e67 --- /dev/null +++ b/src/kernel/sys.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * linux/kernel/sys.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | #include <errno.h> | ||
8 | |||
9 | #include <linux/sched.h> | ||
10 | #include <linux/tty.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <asm/segment.h> | ||
13 | #include <sys/times.h> | ||
14 | #include <sys/utsname.h> | ||
15 | |||
16 | int sys_ftime() | ||
17 | { | ||
18 | return -ENOSYS; | ||
19 | } | ||
20 | |||
21 | int sys_break() | ||
22 | { | ||
23 | return -ENOSYS; | ||
24 | } | ||
25 | |||
26 | int sys_ptrace() | ||
27 | { | ||
28 | return -ENOSYS; | ||
29 | } | ||
30 | |||
31 | int sys_stty() | ||
32 | { | ||
33 | return -ENOSYS; | ||
34 | } | ||
35 | |||
36 | int sys_gtty() | ||
37 | { | ||
38 | return -ENOSYS; | ||
39 | } | ||
40 | |||
41 | int sys_rename() | ||
42 | { | ||
43 | return -ENOSYS; | ||
44 | } | ||
45 | |||
46 | int sys_prof() | ||
47 | { | ||
48 | return -ENOSYS; | ||
49 | } | ||
50 | |||
51 | int sys_setregid(int rgid, int egid) | ||
52 | { | ||
53 | if (rgid>0) { | ||
54 | if ((current->gid == rgid) || | ||
55 | suser()) | ||
56 | current->gid = rgid; | ||
57 | else | ||
58 | return(-EPERM); | ||
59 | } | ||
60 | if (egid>0) { | ||
61 | if ((current->gid == egid) || | ||
62 | (current->egid == egid) || | ||
63 | (current->sgid == egid) || | ||
64 | suser()) | ||
65 | current->egid = egid; | ||
66 | else | ||
67 | return(-EPERM); | ||
68 | } | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | int sys_setgid(int gid) | ||
73 | { | ||
74 | return(sys_setregid(gid, gid)); | ||
75 | } | ||
76 | |||
77 | int sys_acct() | ||
78 | { | ||
79 | return -ENOSYS; | ||
80 | } | ||
81 | |||
82 | int sys_phys() | ||
83 | { | ||
84 | return -ENOSYS; | ||
85 | } | ||
86 | |||
87 | int sys_lock() | ||
88 | { | ||
89 | return -ENOSYS; | ||
90 | } | ||
91 | |||
92 | int sys_mpx() | ||
93 | { | ||
94 | return -ENOSYS; | ||
95 | } | ||
96 | |||
97 | int sys_ulimit() | ||
98 | { | ||
99 | return -ENOSYS; | ||
100 | } | ||
101 | |||
102 | int sys_time(long * tloc) | ||
103 | { | ||
104 | int i; | ||
105 | |||
106 | i = CURRENT_TIME; | ||
107 | if (tloc) { | ||
108 | verify_area(tloc,4); | ||
109 | put_fs_long(i,(unsigned long *)tloc); | ||
110 | } | ||
111 | return i; | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * Unprivileged users may change the real user id to the effective uid | ||
116 | * or vice versa. | ||
117 | */ | ||
118 | int sys_setreuid(int ruid, int euid) | ||
119 | { | ||
120 | int old_ruid = current->uid; | ||
121 | |||
122 | if (ruid>0) { | ||
123 | if ((current->euid==ruid) || | ||
124 | (old_ruid == ruid) || | ||
125 | suser()) | ||
126 | current->uid = ruid; | ||
127 | else | ||
128 | return(-EPERM); | ||
129 | } | ||
130 | if (euid>0) { | ||
131 | if ((old_ruid == euid) || | ||
132 | (current->euid == euid) || | ||
133 | suser()) | ||
134 | current->euid = euid; | ||
135 | else { | ||
136 | current->uid = old_ruid; | ||
137 | return(-EPERM); | ||
138 | } | ||
139 | } | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | int sys_setuid(int uid) | ||
144 | { | ||
145 | return(sys_setreuid(uid, uid)); | ||
146 | } | ||
147 | |||
148 | int sys_stime(long * tptr) | ||
149 | { | ||
150 | if (!suser()) | ||
151 | return -EPERM; | ||
152 | startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ; | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | int sys_times(struct tms * tbuf) | ||
157 | { | ||
158 | if (tbuf) { | ||
159 | verify_area(tbuf,sizeof *tbuf); | ||
160 | put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime); | ||
161 | put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime); | ||
162 | put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime); | ||
163 | put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime); | ||
164 | } | ||
165 | return jiffies; | ||
166 | } | ||
167 | |||
168 | int sys_brk(unsigned long end_data_seg) | ||
169 | { | ||
170 | if (end_data_seg >= current->end_code && | ||
171 | end_data_seg < current->start_stack - 16384) | ||
172 | current->brk = end_data_seg; | ||
173 | return current->brk; | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * This needs some heave checking ... | ||
178 | * I just haven't get the stomach for it. I also don't fully | ||
179 | * understand sessions/pgrp etc. Let somebody who does explain it. | ||
180 | */ | ||
181 | int sys_setpgid(int pid, int pgid) | ||
182 | { | ||
183 | int i; | ||
184 | |||
185 | if (!pid) | ||
186 | pid = current->pid; | ||
187 | if (!pgid) | ||
188 | pgid = current->pid; | ||
189 | for (i=0 ; i<NR_TASKS ; i++) | ||
190 | if (task[i] && task[i]->pid==pid) { | ||
191 | if (task[i]->leader) | ||
192 | return -EPERM; | ||
193 | if (task[i]->session != current->session) | ||
194 | return -EPERM; | ||
195 | task[i]->pgrp = pgid; | ||
196 | return 0; | ||
197 | } | ||
198 | return -ESRCH; | ||
199 | } | ||
200 | |||
201 | int sys_getpgrp(void) | ||
202 | { | ||
203 | return current->pgrp; | ||
204 | } | ||
205 | |||
206 | int sys_setsid(void) | ||
207 | { | ||
208 | if (current->leader && !suser()) | ||
209 | return -EPERM; | ||
210 | current->leader = 1; | ||
211 | current->session = current->pgrp = current->pid; | ||
212 | current->tty = -1; | ||
213 | return current->pgrp; | ||
214 | } | ||
215 | |||
216 | int sys_uname(struct utsname * name) | ||
217 | { | ||
218 | static struct utsname thisname = { | ||
219 | "linux .0","nodename","release ","version ","machine " | ||
220 | }; | ||
221 | int i; | ||
222 | |||
223 | if (!name) return -ERROR; | ||
224 | verify_area(name,sizeof *name); | ||
225 | for(i=0;i<sizeof *name;i++) | ||
226 | put_fs_byte(((char *) &thisname)[i],i+(char *) name); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | int sys_umask(int mask) | ||
231 | { | ||
232 | int old = current->umask; | ||
233 | |||
234 | current->umask = mask & 0777; | ||
235 | return (old); | ||
236 | } | ||
diff --git a/src/kernel/system_call.s b/src/kernel/system_call.s new file mode 100644 index 0000000..c602cd8 --- /dev/null +++ b/src/kernel/system_call.s | |||
@@ -0,0 +1,285 @@ | |||
1 | /* | ||
2 | * linux/kernel/system_call.s | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * system_call.s contains the system-call low-level handling routines. | ||
9 | * This also contains the timer-interrupt handler, as some of the code is | ||
10 | * the same. The hd- and flopppy-interrupts are also here. | ||
11 | * | ||
12 | * NOTE: This code handles signal-recognition, which happens every time | ||
13 | * after a timer-interrupt and after each system call. Ordinary interrupts | ||
14 | * don't handle signal-recognition, as that would clutter them up totally | ||
15 | * unnecessarily. | ||
16 | * | ||
17 | * Stack layout in 'ret_from_system_call': | ||
18 | * | ||
19 | * 0(%esp) - %eax | ||
20 | * 4(%esp) - %ebx | ||
21 | * 8(%esp) - %ecx | ||
22 | * C(%esp) - %edx | ||
23 | * 10(%esp) - %fs | ||
24 | * 14(%esp) - %es | ||
25 | * 18(%esp) - %ds | ||
26 | * 1C(%esp) - %eip | ||
27 | * 20(%esp) - %cs | ||
28 | * 24(%esp) - %eflags | ||
29 | * 28(%esp) - %oldesp | ||
30 | * 2C(%esp) - %oldss | ||
31 | */ | ||
32 | |||
33 | SIG_CHLD = 17 | ||
34 | |||
35 | EAX = 0x00 | ||
36 | EBX = 0x04 | ||
37 | ECX = 0x08 | ||
38 | EDX = 0x0C | ||
39 | FS = 0x10 | ||
40 | ES = 0x14 | ||
41 | DS = 0x18 | ||
42 | EIP = 0x1C | ||
43 | CS = 0x20 | ||
44 | EFLAGS = 0x24 | ||
45 | OLDESP = 0x28 | ||
46 | OLDSS = 0x2C | ||
47 | |||
48 | state = 0 # these are offsets into the task-struct. | ||
49 | counter = 4 | ||
50 | priority = 8 | ||
51 | signal = 12 | ||
52 | sigaction = 16 # MUST be 16 (=len of sigaction) | ||
53 | blocked = (33*16) | ||
54 | |||
55 | # offsets within sigaction | ||
56 | sa_handler = 0 | ||
57 | sa_mask = 4 | ||
58 | sa_flags = 8 | ||
59 | sa_restorer = 12 | ||
60 | |||
61 | nr_system_calls = 72 | ||
62 | |||
63 | /* | ||
64 | * Ok, I get parallel printer interrupts while using the floppy for some | ||
65 | * strange reason. Urgel. Now I just ignore them. | ||
66 | */ | ||
67 | .globl system_call,sys_fork,timer_interrupt,sys_execve | ||
68 | .globl hd_interrupt,floppy_interrupt,parallel_interrupt | ||
69 | .globl device_not_available, coprocessor_error | ||
70 | |||
71 | .align 2 | ||
72 | bad_sys_call: | ||
73 | movl $-1,%eax | ||
74 | iret | ||
75 | .align 2 | ||
76 | reschedule: | ||
77 | pushl $ret_from_sys_call | ||
78 | jmp schedule | ||
79 | .align 2 | ||
80 | system_call: | ||
81 | cmpl $nr_system_calls-1,%eax | ||
82 | ja bad_sys_call | ||
83 | push %ds | ||
84 | push %es | ||
85 | push %fs | ||
86 | pushl %edx | ||
87 | pushl %ecx # push %ebx,%ecx,%edx as parameters | ||
88 | pushl %ebx # to the system call | ||
89 | movl $0x10,%edx # set up ds,es to kernel space | ||
90 | mov %dx,%ds | ||
91 | mov %dx,%es | ||
92 | movl $0x17,%edx # fs points to local data space | ||
93 | mov %dx,%fs | ||
94 | call *sys_call_table(,%eax,4) | ||
95 | pushl %eax | ||
96 | movl current,%eax | ||
97 | cmpl $0,state(%eax) # state | ||
98 | jne reschedule | ||
99 | cmpl $0,counter(%eax) # counter | ||
100 | je reschedule | ||
101 | ret_from_sys_call: | ||
102 | movl current,%eax # task[0] cannot have signals | ||
103 | cmpl task,%eax | ||
104 | je 3f | ||
105 | cmpw $0x0f,CS(%esp) # was old code segment supervisor ? | ||
106 | jne 3f | ||
107 | cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? | ||
108 | jne 3f | ||
109 | movl signal(%eax),%ebx | ||
110 | movl blocked(%eax),%ecx | ||
111 | notl %ecx | ||
112 | andl %ebx,%ecx | ||
113 | bsfl %ecx,%ecx | ||
114 | je 3f | ||
115 | btrl %ecx,%ebx | ||
116 | movl %ebx,signal(%eax) | ||
117 | incl %ecx | ||
118 | pushl %ecx | ||
119 | call do_signal | ||
120 | popl %eax | ||
121 | 3: popl %eax | ||
122 | popl %ebx | ||
123 | popl %ecx | ||
124 | popl %edx | ||
125 | pop %fs | ||
126 | pop %es | ||
127 | pop %ds | ||
128 | iret | ||
129 | |||
130 | .align 2 | ||
131 | coprocessor_error: | ||
132 | push %ds | ||
133 | push %es | ||
134 | push %fs | ||
135 | pushl %edx | ||
136 | pushl %ecx | ||
137 | pushl %ebx | ||
138 | pushl %eax | ||
139 | movl $0x10,%eax | ||
140 | mov %ax,%ds | ||
141 | mov %ax,%es | ||
142 | movl $0x17,%eax | ||
143 | mov %ax,%fs | ||
144 | pushl $ret_from_sys_call | ||
145 | jmp math_error | ||
146 | |||
147 | .align 2 | ||
148 | device_not_available: | ||
149 | push %ds | ||
150 | push %es | ||
151 | push %fs | ||
152 | pushl %edx | ||
153 | pushl %ecx | ||
154 | pushl %ebx | ||
155 | pushl %eax | ||
156 | movl $0x10,%eax | ||
157 | mov %ax,%ds | ||
158 | mov %ax,%es | ||
159 | movl $0x17,%eax | ||
160 | mov %ax,%fs | ||
161 | pushl $ret_from_sys_call | ||
162 | clts # clear TS so that we can use math | ||
163 | movl %cr0,%eax | ||
164 | testl $0x4,%eax # EM (math emulation bit) | ||
165 | je math_state_restore | ||
166 | pushl %ebp | ||
167 | pushl %esi | ||
168 | pushl %edi | ||
169 | call math_emulate | ||
170 | popl %edi | ||
171 | popl %esi | ||
172 | popl %ebp | ||
173 | ret | ||
174 | |||
175 | .align 2 | ||
176 | timer_interrupt: | ||
177 | push %ds # save ds,es and put kernel data space | ||
178 | push %es # into them. %fs is used by _system_call | ||
179 | push %fs | ||
180 | pushl %edx # we save %eax,%ecx,%edx as gcc doesn't | ||
181 | pushl %ecx # save those across function calls. %ebx | ||
182 | pushl %ebx # is saved as we use that in ret_sys_call | ||
183 | pushl %eax | ||
184 | movl $0x10,%eax | ||
185 | mov %ax,%ds | ||
186 | mov %ax,%es | ||
187 | movl $0x17,%eax | ||
188 | mov %ax,%fs | ||
189 | incl jiffies | ||
190 | movb $0x20,%al # EOI to interrupt controller #1 | ||
191 | outb %al,$0x20 | ||
192 | movl CS(%esp),%eax | ||
193 | andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor) | ||
194 | pushl %eax | ||
195 | call do_timer # 'do_timer(long CPL)' does everything from | ||
196 | addl $4,%esp # task switching to accounting ... | ||
197 | jmp ret_from_sys_call | ||
198 | |||
199 | .align 2 | ||
200 | sys_execve: | ||
201 | lea EIP(%esp),%eax | ||
202 | pushl %eax | ||
203 | call do_execve | ||
204 | addl $4,%esp | ||
205 | ret | ||
206 | |||
207 | .align 2 | ||
208 | sys_fork: | ||
209 | call find_empty_process | ||
210 | testl %eax,%eax | ||
211 | js 1f | ||
212 | push %gs | ||
213 | pushl %esi | ||
214 | pushl %edi | ||
215 | pushl %ebp | ||
216 | pushl %eax | ||
217 | call copy_process | ||
218 | addl $20,%esp | ||
219 | 1: ret | ||
220 | |||
221 | hd_interrupt: | ||
222 | pushl %eax | ||
223 | pushl %ecx | ||
224 | pushl %edx | ||
225 | push %ds | ||
226 | push %es | ||
227 | push %fs | ||
228 | movl $0x10,%eax | ||
229 | mov %ax,%ds | ||
230 | mov %ax,%es | ||
231 | movl $0x17,%eax | ||
232 | mov %ax,%fs | ||
233 | movb $0x20,%al | ||
234 | outb %al,$0xA0 # EOI to interrupt controller #1 | ||
235 | jmp 1f # give port chance to breathe | ||
236 | 1: jmp 1f | ||
237 | 1: xorl %edx,%edx | ||
238 | xchgl do_hd,%edx | ||
239 | testl %edx,%edx | ||
240 | jne 1f | ||
241 | movl $unexpected_hd_interrupt,%edx | ||
242 | 1: outb %al,$0x20 | ||
243 | call *%edx # "interesting" way of handling intr. | ||
244 | pop %fs | ||
245 | pop %es | ||
246 | pop %ds | ||
247 | popl %edx | ||
248 | popl %ecx | ||
249 | popl %eax | ||
250 | iret | ||
251 | |||
252 | floppy_interrupt: | ||
253 | pushl %eax | ||
254 | pushl %ecx | ||
255 | pushl %edx | ||
256 | push %ds | ||
257 | push %es | ||
258 | push %fs | ||
259 | movl $0x10,%eax | ||
260 | mov %ax,%ds | ||
261 | mov %ax,%es | ||
262 | movl $0x17,%eax | ||
263 | mov %ax,%fs | ||
264 | movb $0x20,%al | ||
265 | outb %al,$0x20 # EOI to interrupt controller #1 | ||
266 | xorl %eax,%eax | ||
267 | xchgl do_floppy,%eax | ||
268 | testl %eax,%eax | ||
269 | jne 1f | ||
270 | movl $unexpected_floppy_interrupt,%eax | ||
271 | 1: call *%eax # "interesting" way of handling intr. | ||
272 | pop %fs | ||
273 | pop %es | ||
274 | pop %ds | ||
275 | popl %edx | ||
276 | popl %ecx | ||
277 | popl %eax | ||
278 | iret | ||
279 | |||
280 | parallel_interrupt: | ||
281 | pushl %eax | ||
282 | movb $0x20,%al | ||
283 | outb %al,$0x20 | ||
284 | popl %eax | ||
285 | iret | ||
diff --git a/src/kernel/traps.c b/src/kernel/traps.c new file mode 100644 index 0000000..3629a13 --- /dev/null +++ b/src/kernel/traps.c | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * linux/kernel/traps.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * 'Traps.c' handles hardware traps and faults after we have saved some | ||
9 | * state in 'asm.s'. Currently mostly a debugging-aid, will be extended | ||
10 | * to mainly kill the offending process (probably by giving it a signal, | ||
11 | * but possibly by killing it outright if necessary). | ||
12 | */ | ||
13 | #include <string.h> | ||
14 | |||
15 | #include <linux/head.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <asm/system.h> | ||
19 | #include <asm/segment.h> | ||
20 | #include <asm/io.h> | ||
21 | |||
22 | #define get_seg_byte(seg,addr) ({ \ | ||
23 | register char __res; \ | ||
24 | __asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \ | ||
25 | :"=a" (__res):"0" (seg),"m" (*(addr))); \ | ||
26 | __res;}) | ||
27 | |||
28 | #define get_seg_long(seg,addr) ({ \ | ||
29 | register unsigned long __res; \ | ||
30 | __asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \ | ||
31 | :"=a" (__res):"0" (seg),"m" (*(addr))); \ | ||
32 | __res;}) | ||
33 | |||
34 | #define _fs() ({ \ | ||
35 | register unsigned short __res; \ | ||
36 | __asm__("mov %%fs,%%ax":"=a" (__res):); \ | ||
37 | __res;}) | ||
38 | |||
39 | int do_exit(long code); | ||
40 | |||
41 | void page_exception(void); | ||
42 | |||
43 | void divide_error(void); | ||
44 | void debug(void); | ||
45 | void nmi(void); | ||
46 | void int3(void); | ||
47 | void overflow(void); | ||
48 | void bounds(void); | ||
49 | void invalid_op(void); | ||
50 | void device_not_available(void); | ||
51 | void double_fault(void); | ||
52 | void coprocessor_segment_overrun(void); | ||
53 | void invalid_TSS(void); | ||
54 | void segment_not_present(void); | ||
55 | void stack_segment(void); | ||
56 | void general_protection(void); | ||
57 | void page_fault(void); | ||
58 | void coprocessor_error(void); | ||
59 | void reserved(void); | ||
60 | void parallel_interrupt(void); | ||
61 | void irq13(void); | ||
62 | |||
63 | static void die(char * str,long esp_ptr,long nr) | ||
64 | { | ||
65 | long * esp = (long *) esp_ptr; | ||
66 | int i; | ||
67 | |||
68 | printk("%s: %04x\n\r",str,nr&0xffff); | ||
69 | printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n", | ||
70 | esp[1],esp[0],esp[2],esp[4],esp[3]); | ||
71 | printk("fs: %04x\n",_fs()); | ||
72 | printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17)); | ||
73 | if (esp[4] == 0x17) { | ||
74 | printk("Stack: "); | ||
75 | for (i=0;i<4;i++) | ||
76 | printk("%p ",get_seg_long(0x17,i+(long *)esp[3])); | ||
77 | printk("\n"); | ||
78 | } | ||
79 | str(i); | ||
80 | printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i); | ||
81 | for(i=0;i<10;i++) | ||
82 | printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0]))); | ||
83 | printk("\n\r"); | ||
84 | do_exit(11); /* play segment exception */ | ||
85 | } | ||
86 | |||
87 | void do_double_fault(long esp, long error_code) | ||
88 | { | ||
89 | die("double fault",esp,error_code); | ||
90 | } | ||
91 | |||
92 | void do_general_protection(long esp, long error_code) | ||
93 | { | ||
94 | die("general protection",esp,error_code); | ||
95 | } | ||
96 | |||
97 | void do_divide_error(long esp, long error_code) | ||
98 | { | ||
99 | die("divide error",esp,error_code); | ||
100 | } | ||
101 | |||
102 | void do_int3(long * esp, long error_code, | ||
103 | long fs,long es,long ds, | ||
104 | long ebp,long esi,long edi, | ||
105 | long edx,long ecx,long ebx,long eax) | ||
106 | { | ||
107 | int tr; | ||
108 | |||
109 | __asm__("str %%ax":"=a" (tr):"0" (0)); | ||
110 | printk("eax\t\tebx\t\tecx\t\tedx\n\r%8x\t%8x\t%8x\t%8x\n\r", | ||
111 | eax,ebx,ecx,edx); | ||
112 | printk("esi\t\tedi\t\tebp\t\tesp\n\r%8x\t%8x\t%8x\t%8x\n\r", | ||
113 | esi,edi,ebp,(long) esp); | ||
114 | printk("\n\rds\tes\tfs\ttr\n\r%4x\t%4x\t%4x\t%4x\n\r", | ||
115 | ds,es,fs,tr); | ||
116 | printk("EIP: %8x CS: %4x EFLAGS: %8x\n\r",esp[0],esp[1],esp[2]); | ||
117 | } | ||
118 | |||
119 | void do_nmi(long esp, long error_code) | ||
120 | { | ||
121 | die("nmi",esp,error_code); | ||
122 | } | ||
123 | |||
124 | void do_debug(long esp, long error_code) | ||
125 | { | ||
126 | die("debug",esp,error_code); | ||
127 | } | ||
128 | |||
129 | void do_overflow(long esp, long error_code) | ||
130 | { | ||
131 | die("overflow",esp,error_code); | ||
132 | } | ||
133 | |||
134 | void do_bounds(long esp, long error_code) | ||
135 | { | ||
136 | die("bounds",esp,error_code); | ||
137 | } | ||
138 | |||
139 | void do_invalid_op(long esp, long error_code) | ||
140 | { | ||
141 | die("invalid operand",esp,error_code); | ||
142 | } | ||
143 | |||
144 | void do_device_not_available(long esp, long error_code) | ||
145 | { | ||
146 | die("device not available",esp,error_code); | ||
147 | } | ||
148 | |||
149 | void do_coprocessor_segment_overrun(long esp, long error_code) | ||
150 | { | ||
151 | die("coprocessor segment overrun",esp,error_code); | ||
152 | } | ||
153 | |||
154 | void do_invalid_TSS(long esp,long error_code) | ||
155 | { | ||
156 | die("invalid TSS",esp,error_code); | ||
157 | } | ||
158 | |||
159 | void do_segment_not_present(long esp,long error_code) | ||
160 | { | ||
161 | die("segment not present",esp,error_code); | ||
162 | } | ||
163 | |||
164 | void do_stack_segment(long esp,long error_code) | ||
165 | { | ||
166 | die("stack segment",esp,error_code); | ||
167 | } | ||
168 | |||
169 | void do_coprocessor_error(long esp, long error_code) | ||
170 | { | ||
171 | if (last_task_used_math != current) | ||
172 | return; | ||
173 | die("coprocessor error",esp,error_code); | ||
174 | } | ||
175 | |||
176 | void do_reserved(long esp, long error_code) | ||
177 | { | ||
178 | die("reserved (15,17-47) error",esp,error_code); | ||
179 | } | ||
180 | |||
181 | void trap_init(void) | ||
182 | { | ||
183 | int i; | ||
184 | |||
185 | set_trap_gate(0,÷_error); | ||
186 | set_trap_gate(1,&debug); | ||
187 | set_trap_gate(2,&nmi); | ||
188 | set_system_gate(3,&int3); /* int3-5 can be called from all */ | ||
189 | set_system_gate(4,&overflow); | ||
190 | set_system_gate(5,&bounds); | ||
191 | set_trap_gate(6,&invalid_op); | ||
192 | set_trap_gate(7,&device_not_available); | ||
193 | set_trap_gate(8,&double_fault); | ||
194 | set_trap_gate(9,&coprocessor_segment_overrun); | ||
195 | set_trap_gate(10,&invalid_TSS); | ||
196 | set_trap_gate(11,&segment_not_present); | ||
197 | set_trap_gate(12,&stack_segment); | ||
198 | set_trap_gate(13,&general_protection); | ||
199 | set_trap_gate(14,&page_fault); | ||
200 | set_trap_gate(15,&reserved); | ||
201 | set_trap_gate(16,&coprocessor_error); | ||
202 | for (i=17;i<48;i++) | ||
203 | set_trap_gate(i,&reserved); | ||
204 | set_trap_gate(45,&irq13); | ||
205 | outb_p(inb_p(0x21)&0xfb,0x21); | ||
206 | outb(inb_p(0xA1)&0xdf,0xA1); | ||
207 | set_trap_gate(39,¶llel_interrupt); | ||
208 | } | ||
diff --git a/src/kernel/vsprintf.c b/src/kernel/vsprintf.c new file mode 100644 index 0000000..ab70f23 --- /dev/null +++ b/src/kernel/vsprintf.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /* | ||
2 | * linux/kernel/vsprintf.c | ||
3 | * | ||
4 | * (C) 1991 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ | ||
8 | /* | ||
9 | * Wirzenius wrote this portably, Torvalds fucked it up :-) | ||
10 | */ | ||
11 | |||
12 | #include <stdarg.h> | ||
13 | #include <string.h> | ||
14 | |||
15 | /* we use this so that we can do without the ctype library */ | ||
16 | #define is_digit(c) ((c) >= '0' && (c) <= '9') | ||
17 | |||
18 | static int skip_atoi(const char **s) | ||
19 | { | ||
20 | int i=0; | ||
21 | |||
22 | while (is_digit(**s)) | ||
23 | i = i*10 + *((*s)++) - '0'; | ||
24 | return i; | ||
25 | } | ||
26 | |||
27 | #define ZEROPAD 1 /* pad with zero */ | ||
28 | #define SIGN 2 /* unsigned/signed long */ | ||
29 | #define PLUS 4 /* show plus */ | ||
30 | #define SPACE 8 /* space if plus */ | ||
31 | #define LEFT 16 /* left justified */ | ||
32 | #define SPECIAL 32 /* 0x */ | ||
33 | #define SMALL 64 /* use 'abcdef' instead of 'ABCDEF' */ | ||
34 | |||
35 | #define do_div(n,base) ({ \ | ||
36 | int __res; \ | ||
37 | __asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \ | ||
38 | __res; }) | ||
39 | |||
40 | static char * number(char * str, int num, int base, int size, int precision | ||
41 | ,int type) | ||
42 | { | ||
43 | char c,sign,tmp[36]; | ||
44 | const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
45 | int i; | ||
46 | |||
47 | if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz"; | ||
48 | if (type&LEFT) type &= ~ZEROPAD; | ||
49 | if (base<2 || base>36) | ||
50 | return 0; | ||
51 | c = (type & ZEROPAD) ? '0' : ' ' ; | ||
52 | if (type&SIGN && num<0) { | ||
53 | sign='-'; | ||
54 | num = -num; | ||
55 | } else | ||
56 | sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0); | ||
57 | if (sign) size--; | ||
58 | if (type&SPECIAL) { | ||
59 | if (base==16) size -= 2; | ||
60 | else if (base==8) size--; | ||
61 | } | ||
62 | i=0; | ||
63 | if (num==0) | ||
64 | tmp[i++]='0'; | ||
65 | else while (num!=0) | ||
66 | tmp[i++]=digits[do_div(num,base)]; | ||
67 | if (i>precision) precision=i; | ||
68 | size -= precision; | ||
69 | if (!(type&(ZEROPAD+LEFT))) | ||
70 | while(size-->0) | ||
71 | *str++ = ' '; | ||
72 | if (sign) | ||
73 | *str++ = sign; | ||
74 | if (type&SPECIAL) { | ||
75 | if (base==8) | ||
76 | *str++ = '0'; | ||
77 | else if (base==16) { | ||
78 | *str++ = '0'; | ||
79 | *str++ = digits[33]; | ||
80 | } | ||
81 | } | ||
82 | if (!(type&LEFT)) | ||
83 | while(size-->0) | ||
84 | *str++ = c; | ||
85 | while(i<precision--) | ||
86 | *str++ = '0'; | ||
87 | while(i-->0) | ||
88 | *str++ = tmp[i]; | ||
89 | while(size-->0) | ||
90 | *str++ = ' '; | ||
91 | return str; | ||
92 | } | ||
93 | |||
94 | int vsprintf(char *buf, const char *fmt, va_list args) | ||
95 | { | ||
96 | int len; | ||
97 | int i; | ||
98 | char * str; | ||
99 | char *s; | ||
100 | int *ip; | ||
101 | |||
102 | int flags; /* flags to number() */ | ||
103 | |||
104 | int field_width; /* width of output field */ | ||
105 | int precision; /* min. # of digits for integers; max | ||
106 | number of chars for from string */ | ||
107 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ | ||
108 | |||
109 | for (str=buf ; *fmt ; ++fmt) { | ||
110 | if (*fmt != '%') { | ||
111 | *str++ = *fmt; | ||
112 | continue; | ||
113 | } | ||
114 | |||
115 | /* process flags */ | ||
116 | flags = 0; | ||
117 | repeat: | ||
118 | ++fmt; /* this also skips first '%' */ | ||
119 | switch (*fmt) { | ||
120 | case '-': flags |= LEFT; goto repeat; | ||
121 | case '+': flags |= PLUS; goto repeat; | ||
122 | case ' ': flags |= SPACE; goto repeat; | ||
123 | case '#': flags |= SPECIAL; goto repeat; | ||
124 | case '0': flags |= ZEROPAD; goto repeat; | ||
125 | } | ||
126 | |||
127 | /* get field width */ | ||
128 | field_width = -1; | ||
129 | if (is_digit(*fmt)) | ||
130 | field_width = skip_atoi(&fmt); | ||
131 | else if (*fmt == '*') { | ||
132 | /* it's the next argument */ | ||
133 | field_width = va_arg(args, int); | ||
134 | if (field_width < 0) { | ||
135 | field_width = -field_width; | ||
136 | flags |= LEFT; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | /* get the precision */ | ||
141 | precision = -1; | ||
142 | if (*fmt == '.') { | ||
143 | ++fmt; | ||
144 | if (is_digit(*fmt)) | ||
145 | precision = skip_atoi(&fmt); | ||
146 | else if (*fmt == '*') { | ||
147 | /* it's the next argument */ | ||
148 | precision = va_arg(args, int); | ||
149 | } | ||
150 | if (precision < 0) | ||
151 | precision = 0; | ||
152 | } | ||
153 | |||
154 | /* get the conversion qualifier */ | ||
155 | qualifier = -1; | ||
156 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { | ||
157 | qualifier = *fmt; | ||
158 | ++fmt; | ||
159 | } | ||
160 | |||
161 | switch (*fmt) { | ||
162 | case 'c': | ||
163 | if (!(flags & LEFT)) | ||
164 | while (--field_width > 0) | ||
165 | *str++ = ' '; | ||
166 | *str++ = (unsigned char) va_arg(args, int); | ||
167 | while (--field_width > 0) | ||
168 | *str++ = ' '; | ||
169 | break; | ||
170 | |||
171 | case 's': | ||
172 | s = va_arg(args, char *); | ||
173 | len = strlen(s); | ||
174 | if (precision < 0) | ||
175 | precision = len; | ||
176 | else if (len > precision) | ||
177 | len = precision; | ||
178 | |||
179 | if (!(flags & LEFT)) | ||
180 | while (len < field_width--) | ||
181 | *str++ = ' '; | ||
182 | for (i = 0; i < len; ++i) | ||
183 | *str++ = *s++; | ||
184 | while (len < field_width--) | ||
185 | *str++ = ' '; | ||
186 | break; | ||
187 | |||
188 | case 'o': | ||
189 | str = number(str, va_arg(args, unsigned long), 8, | ||
190 | field_width, precision, flags); | ||
191 | break; | ||
192 | |||
193 | case 'p': | ||
194 | if (field_width == -1) { | ||
195 | field_width = 8; | ||
196 | flags |= ZEROPAD; | ||
197 | } | ||
198 | str = number(str, | ||
199 | (unsigned long) va_arg(args, void *), 16, | ||
200 | field_width, precision, flags); | ||
201 | break; | ||
202 | |||
203 | case 'x': | ||
204 | flags |= SMALL; | ||
205 | case 'X': | ||
206 | str = number(str, va_arg(args, unsigned long), 16, | ||
207 | field_width, precision, flags); | ||
208 | break; | ||
209 | |||
210 | case 'd': | ||
211 | case 'i': | ||
212 | flags |= SIGN; | ||
213 | case 'u': | ||
214 | str = number(str, va_arg(args, unsigned long), 10, | ||
215 | field_width, precision, flags); | ||
216 | break; | ||
217 | |||
218 | case 'n': | ||
219 | ip = va_arg(args, int *); | ||
220 | *ip = (str - buf); | ||
221 | break; | ||
222 | |||
223 | default: | ||
224 | if (*fmt != '%') | ||
225 | *str++ = '%'; | ||
226 | if (*fmt) | ||
227 | *str++ = *fmt; | ||
228 | else | ||
229 | --fmt; | ||
230 | break; | ||
231 | } | ||
232 | } | ||
233 | *str = '\0'; | ||
234 | return str-buf; | ||
235 | } | ||