initial version

This commit is contained in:
gohigh
2024-02-19 01:11:57 -05:00
parent 7c80ef0753
commit 692092f8e0
817 changed files with 430745 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
#
# Makefile for the FREAX-kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
AR =gar
AS =gas
LD =gld
LDFLAGS =-s -x
CC =gcc
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \
-finline-functions -mstring-insns -nostdinc -I../include
CPP =gcc -E -nostdinc -I../include
.c.s:
$(CC) $(CFLAGS) \
-S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-c -o $*.o $<
OBJS = sched.o sys_call.o traps.o asm.o fork.o \
panic.o printk.o vsprintf.o sys.o exit.o \
signal.o mktime.o
kernel.o: $(OBJS)
$(LD) -r -o kernel.o $(OBJS)
sync
clean:
rm -f core *.o *.a tmp_make keyboard.s
for i in *.c;do rm -f `basename $$i .c`.s;done
(cd chr_drv; make clean)
(cd blk_drv; make clean)
(cd math; make clean)
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
$(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile
(cd chr_drv; make dep)
(cd blk_drv; make dep)
### Dependencies:
exit.s exit.o : exit.c ../include/errno.h ../include/signal.h \
../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/linux/tty.h \
../include/termios.h ../include/asm/segment.h
fork.s fork.o : fork.c ../include/errno.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/asm/segment.h ../include/asm/system.h
mktime.s mktime.o : mktime.c ../include/time.h
panic.s panic.o : panic.c ../include/linux/kernel.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h
printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h \
../include/linux/kernel.h
sched.s sched.o : sched.c ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h \
../include/linux/sys.h ../include/linux/fdreg.h ../include/asm/system.h \
../include/asm/io.h ../include/asm/segment.h
signal.s signal.o : signal.c ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h \
../include/asm/segment.h ../include/errno.h
sys.s sys.o : sys.c ../include/errno.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/linux/tty.h ../include/termios.h \
../include/linux/config.h ../include/asm/segment.h ../include/sys/times.h \
../include/sys/utsname.h ../include/string.h
traps.s traps.o : traps.c ../include/string.h ../include/linux/head.h \
../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/asm/system.h ../include/asm/segment.h \
../include/asm/io.h
vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/string.h

View File

@@ -0,0 +1,151 @@
/*
* linux/kernel/asm.s
*
* (C) 1991 Linus Torvalds
*/
/*
* asm.s contains the low-level code for most hardware faults.
* page_exception is handled by the mm, so that isn't here. This
* file also handles (hopefully) fpu-exceptions due to TS-bit, as
* the fpu must be properly saved/resored. This hasn't been tested.
*/
.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op
.globl _double_fault,_coprocessor_segment_overrun
.globl _invalid_TSS,_segment_not_present,_stack_segment
.globl _general_protection,_coprocessor_error,_irq13,_reserved
.globl _alignment_check
_divide_error:
pushl $_do_divide_error
no_error_code:
xchgl %eax,(%esp)
pushl %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
pushl $0 # "error code"
lea 44(%esp),%edx
pushl %edx
movl $0x10,%edx
mov %dx,%ds
mov %dx,%es
mov %dx,%fs
call *%eax
addl $8,%esp
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
_debug:
pushl $_do_int3 # _do_debug
jmp no_error_code
_nmi:
pushl $_do_nmi
jmp no_error_code
_int3:
pushl $_do_int3
jmp no_error_code
_overflow:
pushl $_do_overflow
jmp no_error_code
_bounds:
pushl $_do_bounds
jmp no_error_code
_invalid_op:
pushl $_do_invalid_op
jmp no_error_code
_coprocessor_segment_overrun:
pushl $_do_coprocessor_segment_overrun
jmp no_error_code
_reserved:
pushl $_do_reserved
jmp no_error_code
_irq13:
pushl %eax
xorb %al,%al
outb %al,$0xF0
movb $0x20,%al
outb %al,$0x20
jmp 1f
1: jmp 1f
1: outb %al,$0xA0
popl %eax
jmp _coprocessor_error
_double_fault:
pushl $_do_double_fault
error_code:
xchgl %eax,4(%esp) # error code <-> %eax
xchgl %ebx,(%esp) # &function <-> %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
pushl %eax # error code
lea 44(%esp),%eax # offset
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
call *%ebx
addl $8,%esp
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
_invalid_TSS:
pushl $_do_invalid_TSS
jmp error_code
_segment_not_present:
pushl $_do_segment_not_present
jmp error_code
_stack_segment:
pushl $_do_stack_segment
jmp error_code
_general_protection:
pushl $_do_general_protection
jmp error_code
_alignment_check:
pushl $_do_alignment_check
jmp error_code

View File

@@ -0,0 +1,71 @@
#
# Makefile for the FREAX-kernel block device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
AR =gar
AS =gas
LD =gld
LDFLAGS =-s -x
CC =gcc
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \
-finline-functions -mstring-insns -nostdinc -I../../include
CPP =gcc -E -nostdinc -I../../include
.c.s:
$(CC) $(CFLAGS) \
-S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-c -o $*.o $<
OBJS = ll_rw_blk.o floppy.o hd.o ramdisk.o
blk_drv.a: $(OBJS)
$(AR) rcs blk_drv.a $(OBJS)
sync
clean:
rm -f core *.o *.a tmp_make
for i in *.c;do rm -f `basename $$i .c`.s;done
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
$(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile
### Dependencies:
floppy.s floppy.o : floppy.c ../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/fdreg.h \
../../include/asm/system.h ../../include/asm/io.h \
../../include/asm/segment.h blk.h
hd.s hd.o : hd.c ../../include/linux/config.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/hdreg.h \
../../include/asm/system.h ../../include/asm/io.h \
../../include/asm/segment.h blk.h
ll_rw_blk.s ll_rw_blk.o : ll_rw_blk.c ../../include/errno.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/asm/system.h blk.h
ramdisk.s ramdisk.o : ramdisk.c ../../include/string.h ../../include/linux/config.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/asm/system.h \
../../include/asm/segment.h ../../include/asm/memory.h blk.h

View File

@@ -0,0 +1,164 @@
#ifndef _BLK_H
#define _BLK_H
#define NR_BLK_DEV 7
/*
* NR_REQUEST is the number of entries in the request-queue.
* NOTE that writes may use only the low 2/3 of these: reads
* take precedence.
*
* 32 seems to be a reasonable number: enough to get some benefit
* from the elevator-mechanism, but not so much as to lock a lot of
* buffers when they are in the queue. 64 seems to be too many (easily
* long pauses in reading when heavy writing/syncing is going on)
*/
#define NR_REQUEST 32
/*
* Ok, this is an expanded form so that we can use the same
* request for paging requests when that is implemented. In
* paging, 'bh' is NULL, and 'waiting' is used to wait for
* read/write completion.
*/
struct request {
int dev; /* -1 if no request */
int cmd; /* READ or WRITE */
int errors;
unsigned long sector;
unsigned long nr_sectors;
char * buffer;
struct task_struct * waiting;
struct buffer_head * bh;
struct request * next;
};
/*
* This is used in the elevator algorithm: Note that
* reads always go before writes. This is natural: reads
* are much more time-critical than writes.
*/
#define IN_ORDER(s1,s2) \
((s1)->cmd<(s2)->cmd || (s1)->cmd==(s2)->cmd && \
((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \
(s1)->sector < (s2)->sector)))
struct blk_dev_struct {
void (*request_fn)(void);
struct request * current_request;
};
extern struct blk_dev_struct blk_dev[NR_BLK_DEV];
extern struct request request[NR_REQUEST];
extern struct task_struct * wait_for_request;
extern int * blk_size[NR_BLK_DEV];
#ifdef MAJOR_NR
/*
* Add entries as needed. Currently the only block devices
* supported are hard-disks and floppies.
*/
#if (MAJOR_NR == 1)
/* ram disk */
#define DEVICE_NAME "ramdisk"
#define DEVICE_REQUEST do_rd_request
#define DEVICE_NR(device) ((device) & 7)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == 2)
/* floppy */
#define DEVICE_NAME "floppy"
#define DEVICE_INTR do_floppy
#define DEVICE_REQUEST do_fd_request
#define DEVICE_NR(device) ((device) & 3)
#define DEVICE_ON(device) floppy_on(DEVICE_NR(device))
#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
#elif (MAJOR_NR == 3)
/* harddisk */
#define DEVICE_NAME "harddisk"
#define DEVICE_INTR do_hd
#define DEVICE_TIMEOUT hd_timeout
#define DEVICE_REQUEST do_hd_request
#define DEVICE_NR(device) (MINOR(device)/5)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif
/* unknown blk device */
#error "unknown blk device"
#endif
#define CURRENT (blk_dev[MAJOR_NR].current_request)
#define CURRENT_DEV DEVICE_NR(CURRENT->dev)
#ifdef DEVICE_INTR
void (*DEVICE_INTR)(void) = NULL;
#endif
#ifdef DEVICE_TIMEOUT
int DEVICE_TIMEOUT = 0;
#define SET_INTR(x) (DEVICE_INTR = (x),DEVICE_TIMEOUT = 200)
#else
#define SET_INTR(x) (DEVICE_INTR = (x))
#endif
static void (DEVICE_REQUEST)(void);
extern inline void unlock_buffer(struct buffer_head * bh)
{
if (!bh->b_lock)
printk(DEVICE_NAME ": free buffer being unlocked\n");
bh->b_lock=0;
wake_up(&bh->b_wait);
}
extern inline void end_request(int uptodate)
{
DEVICE_OFF(CURRENT->dev);
if (CURRENT->bh) {
CURRENT->bh->b_uptodate = uptodate;
unlock_buffer(CURRENT->bh);
}
if (!uptodate) {
printk(DEVICE_NAME " I/O error\n\r");
printk("dev %04x, block %d\n\r",CURRENT->dev,
CURRENT->bh->b_blocknr);
}
wake_up(&CURRENT->waiting);
wake_up(&wait_for_request);
CURRENT->dev = -1;
CURRENT = CURRENT->next;
}
#ifdef DEVICE_TIMEOUT
#define CLEAR_DEVICE_TIMEOUT DEVICE_TIMEOUT = 0;
#else
#define CLEAR_DEVICE_TIMEOUT
#endif
#ifdef DEVICE_INTR
#define CLEAR_DEVICE_INTR DEVICE_INTR = 0;
#else
#define CLEAR_DEVICE_INTR
#endif
#define INIT_REQUEST \
repeat: \
if (!CURRENT) {\
CLEAR_DEVICE_INTR \
CLEAR_DEVICE_TIMEOUT \
return; \
} \
if (MAJOR(CURRENT->dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed"); \
if (CURRENT->bh) { \
if (!CURRENT->bh->b_lock) \
panic(DEVICE_NAME ": block not locked"); \
}
#endif
#endif

View File

@@ -0,0 +1,475 @@
/*
* linux/kernel/floppy.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 02.12.91 - Changed to static variables to indicate need for reset
* and recalibrate. This makes some things easier (output_byte reset
* checking etc), and means less interrupt jumping in case of errors,
* so the code is hopefully easier to understand.
*/
/*
* This file is certainly a mess. I've tried my best to get it working,
* but I don't like programming floppies, and I have only one anyway.
* Urgel. I should check for more errors, and do more graceful error
* recovery. Seems there are problems with several drives. I've tried to
* correct them. No promises.
*/
/*
* As with hd.c, all routines within this file can (and will) be called
* by interrupts, so extreme caution is needed. A hardware interrupt
* handler may not sleep, or a kernel panic will happen. Thus I cannot
* call "floppy-on" directly, but have to set a special timer interrupt
* etc.
*
* Also, I'm not certain this works on more than 1 floppy. Bugs may
* abund.
*/
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/fdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#define MAJOR_NR 2
#include "blk.h"
static int recalibrate = 0;
static int reset = 0;
static int seek = 0;
extern unsigned char current_DOR;
#define immoutb_p(val,port) \
__asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port))
#define TYPE(x) ((x)>>2)
#define DRIVE(x) ((x)&0x03)
/*
* Note that MAX_ERRORS=8 doesn't imply that we retry every bad read
* max 8 times - some types of errors increase the errorcount by 2,
* so we might actually retry only 5-6 times before giving up.
*/
#define MAX_ERRORS 8
/*
* globals used by 'result()'
*/
#define MAX_REPLIES 7
static unsigned char reply_buffer[MAX_REPLIES];
#define ST0 (reply_buffer[0])
#define ST1 (reply_buffer[1])
#define ST2 (reply_buffer[2])
#define ST3 (reply_buffer[3])
/*
* This struct defines the different floppy types. Unlike minix
* linux doesn't have a "search for right type"-type, as the code
* for that is convoluted and weird. I've got enough problems with
* this driver as it is.
*
* The 'stretch' tells if the tracks need to be boubled for some
* types (ie 360kB diskette in 1.2MB drive etc). Others should
* be self-explanatory.
*/
static struct floppy_struct {
unsigned int size, sect, head, track, stretch;
unsigned char gap,rate,spec1;
} floppy_type[] = {
{ 0, 0,0, 0,0,0x00,0x00,0x00 }, /* no testing */
{ 720, 9,2,40,0,0x2A,0x02,0xDF }, /* 360kB PC diskettes */
{ 2400,15,2,80,0,0x1B,0x00,0xDF }, /* 1.2 MB AT-diskettes */
{ 720, 9,2,40,1,0x2A,0x02,0xDF }, /* 360kB in 720kB drive */
{ 1440, 9,2,80,0,0x2A,0x02,0xDF }, /* 3.5" 720kB diskette */
{ 720, 9,2,40,1,0x23,0x01,0xDF }, /* 360kB in 1.2MB drive */
{ 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */
{ 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */
};
/*
* Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps
* Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
* H is head unload time (1=16ms, 2=32ms, etc)
*
* Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc)
* and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
*/
extern void floppy_interrupt(void);
extern char tmp_floppy_area[1024];
/*
* These are global variables, as that's the easiest way to give
* information to interrupts. They are the data used for the current
* request.
*/
static int cur_spec1 = -1;
static int cur_rate = -1;
static struct floppy_struct * floppy = floppy_type;
static unsigned char current_drive = 0;
static unsigned char sector = 0;
static unsigned char head = 0;
static unsigned char track = 0;
static unsigned char seek_track = 0;
static unsigned char current_track = 255;
static unsigned char command = 0;
unsigned char selected = 0;
struct task_struct * wait_on_floppy_select = NULL;
void floppy_deselect(unsigned int nr)
{
if (nr != (current_DOR & 3))
printk("floppy_deselect: drive not selected\n\r");
selected = 0;
wake_up(&wait_on_floppy_select);
}
/*
* floppy-change is never called from an interrupt, so we can relax a bit
* here, sleep etc. Note that floppy-on tries to set current_DOR to point
* to the desired drive, but it will probably not survive the sleep if
* several floppies are used at the same time: thus the loop.
*/
int floppy_change(unsigned int nr)
{
repeat:
floppy_on(nr);
while ((current_DOR & 3) != nr && selected)
sleep_on(&wait_on_floppy_select);
if ((current_DOR & 3) != nr)
goto repeat;
if (inb(FD_DIR) & 0x80) {
floppy_off(nr);
return 1;
}
floppy_off(nr);
return 0;
}
#define copy_buffer(from,to) \
__asm__("cld ; rep ; movsl" \
::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \
:"cx","di","si")
static void setup_DMA(void)
{
long addr = (long) CURRENT->buffer;
cli();
if (addr >= 0x100000) {
addr = (long) tmp_floppy_area;
if (command == FD_WRITE)
copy_buffer(CURRENT->buffer,tmp_floppy_area);
}
/* mask DMA 2 */
immoutb_p(4|2,10);
/* output command byte. I don't know why, but everyone (minix, */
/* sanches & canton) output this twice, first to 12 then to 11 */
__asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t"
"outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:"::
"a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE)));
/* 8 low bits of addr */
immoutb_p(addr,4);
addr >>= 8;
/* bits 8-15 of addr */
immoutb_p(addr,4);
addr >>= 8;
/* bits 16-19 of addr */
immoutb_p(addr,0x81);
/* low 8 bits of count-1 (1024-1=0x3ff) */
immoutb_p(0xff,5);
/* high 8 bits of count-1 */
immoutb_p(3,5);
/* activate DMA 2 */
immoutb_p(0|2,10);
sti();
}
static void output_byte(char byte)
{
int counter;
unsigned char status;
if (reset)
return;
for(counter = 0 ; counter < 10000 ; counter++) {
status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR);
if (status == STATUS_READY) {
outb(byte,FD_DATA);
return;
}
}
reset = 1;
printk("Unable to send byte to FDC\n\r");
}
static int result(void)
{
int i = 0, counter, status;
if (reset)
return -1;
for (counter = 0 ; counter < 10000 ; counter++) {
status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY);
if (status == STATUS_READY)
return i;
if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
if (i >= MAX_REPLIES)
break;
reply_buffer[i++] = inb_p(FD_DATA);
}
}
reset = 1;
printk("Getstatus times out\n\r");
return -1;
}
static void bad_flp_intr(void)
{
CURRENT->errors++;
if (CURRENT->errors > MAX_ERRORS) {
floppy_deselect(current_drive);
end_request(0);
}
if (CURRENT->errors > MAX_ERRORS/2)
reset = 1;
else
recalibrate = 1;
}
/*
* Ok, this interrupt is called after a DMA read/write has succeeded,
* so we check the results, and copy any buffers.
*/
static void rw_interrupt(void)
{
if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73)) {
if (ST1 & 0x02) {
printk("Drive %d is write protected\n\r",current_drive);
floppy_deselect(current_drive);
end_request(0);
} else
bad_flp_intr();
do_fd_request();
return;
}
if (command == FD_READ && (unsigned long)(CURRENT->buffer) >= 0x100000)
copy_buffer(tmp_floppy_area,CURRENT->buffer);
floppy_deselect(current_drive);
end_request(1);
do_fd_request();
}
inline void setup_rw_floppy(void)
{
setup_DMA();
do_floppy = rw_interrupt;
output_byte(command);
output_byte(head<<2 | current_drive);
output_byte(track);
output_byte(head);
output_byte(sector);
output_byte(2); /* sector size = 512 */
output_byte(floppy->sect);
output_byte(floppy->gap);
output_byte(0xFF); /* sector size (0xff when n!=0 ?) */
if (reset)
do_fd_request();
}
/*
* This is the routine called after every seek (or recalibrate) interrupt
* from the floppy controller. Note that the "unexpected interrupt" routine
* also does a recalibrate, but doesn't come here.
*/
static void seek_interrupt(void)
{
/* sense drive status */
output_byte(FD_SENSEI);
if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) {
bad_flp_intr();
do_fd_request();
return;
}
current_track = ST1;
setup_rw_floppy();
}
/*
* This routine is called when everything should be correctly set up
* for the transfer (ie floppy motor is on and the correct floppy is
* selected).
*/
static void transfer(void)
{
if (cur_spec1 != floppy->spec1) {
cur_spec1 = floppy->spec1;
output_byte(FD_SPECIFY);
output_byte(cur_spec1); /* hut etc */
output_byte(6); /* Head load time =6ms, DMA */
}
if (cur_rate != floppy->rate)
outb_p(cur_rate = floppy->rate,FD_DCR);
if (reset) {
do_fd_request();
return;
}
if (!seek) {
setup_rw_floppy();
return;
}
do_floppy = seek_interrupt;
if (seek_track) {
output_byte(FD_SEEK);
output_byte(head<<2 | current_drive);
output_byte(seek_track);
} else {
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
}
if (reset)
do_fd_request();
}
/*
* Special case - used after a unexpected interrupt (or reset)
*/
static void recal_interrupt(void)
{
output_byte(FD_SENSEI);
if (result()!=2 || (ST0 & 0xE0) == 0x60)
reset = 1;
else
recalibrate = 0;
do_fd_request();
}
void unexpected_floppy_interrupt(void)
{
output_byte(FD_SENSEI);
if (result()!=2 || (ST0 & 0xE0) == 0x60)
reset = 1;
else
recalibrate = 1;
}
static void recalibrate_floppy(void)
{
recalibrate = 0;
current_track = 0;
do_floppy = recal_interrupt;
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
if (reset)
do_fd_request();
}
static void reset_interrupt(void)
{
output_byte(FD_SENSEI);
(void) result();
output_byte(FD_SPECIFY);
output_byte(cur_spec1); /* hut etc */
output_byte(6); /* Head load time =6ms, DMA */
do_fd_request();
}
/*
* reset is done by pulling bit 2 of DOR low for a while.
*/
static void reset_floppy(void)
{
int i;
reset = 0;
cur_spec1 = -1;
cur_rate = -1;
recalibrate = 1;
printk("Reset-floppy called\n\r");
cli();
do_floppy = reset_interrupt;
outb_p(current_DOR & ~0x04,FD_DOR);
for (i=0 ; i<100 ; i++)
__asm__("nop");
outb(current_DOR,FD_DOR);
sti();
}
static void floppy_on_interrupt(void)
{
/* We cannot do a floppy-select, as that might sleep. We just force it */
selected = 1;
if (current_drive != (current_DOR & 3)) {
current_DOR &= 0xFC;
current_DOR |= current_drive;
outb(current_DOR,FD_DOR);
add_timer(2,&transfer);
} else
transfer();
}
void do_fd_request(void)
{
unsigned int block;
seek = 0;
if (reset) {
reset_floppy();
return;
}
if (recalibrate) {
recalibrate_floppy();
return;
}
INIT_REQUEST;
floppy = (MINOR(CURRENT->dev)>>2) + floppy_type;
if (current_drive != CURRENT_DEV)
seek = 1;
current_drive = CURRENT_DEV;
block = CURRENT->sector;
if (block+2 > floppy->size) {
end_request(0);
goto repeat;
}
sector = block % floppy->sect;
block /= floppy->sect;
head = block % floppy->head;
track = block / floppy->head;
seek_track = track << floppy->stretch;
if (seek_track != current_track)
seek = 1;
sector++;
if (CURRENT->cmd == READ)
command = FD_READ;
else if (CURRENT->cmd == WRITE)
command = FD_WRITE;
else
panic("do_fd_request: unknown command");
add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt);
}
static int floppy_sizes[] ={
0, 0, 0, 0,
360, 360 ,360, 360,
1200,1200,1200,1200,
360, 360, 360, 360,
720, 720, 720, 720,
360, 360, 360, 360,
720, 720, 720, 720,
1440,1440,1440,1440
};
void floppy_init(void)
{
blk_size[MAJOR_NR] = floppy_sizes;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
set_trap_gate(0x26,&floppy_interrupt);
outb(inb_p(0x21)&~0x40,0x21);
}

View File

@@ -0,0 +1,384 @@
/*
* linux/kernel/hd.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This is the low-level hd interrupt support. It traverses the
* request-list, using interrupts to jump between functions. As
* all the functions are called within interrupts, we may not
* sleep. Special care is recommended.
*
* modified by Drew Eckhardt to check nr of hd's from the CMOS.
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#define MAJOR_NR 3
#include "blk.h"
#define CMOS_READ(addr) ({ \
outb_p(0x80|addr,0x70); \
inb_p(0x71); \
})
/* Max read/write errors/sector */
#define MAX_ERRORS 7
#define MAX_HD 2
static void recal_intr(void);
static void bad_rw_intr(void);
static int recalibrate = 0;
static int reset = 0;
/*
* This struct defines the HD's and their types.
*/
struct hd_i_struct {
int head,sect,cyl,wpcom,lzone,ctl;
};
#ifdef HD_TYPE
struct hd_i_struct hd_info[] = { HD_TYPE };
#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))
#else
struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
static int NR_HD = 0;
#endif
static struct hd_struct {
long start_sect;
long nr_sects;
} hd[5*MAX_HD]={{0,0},};
static int hd_sizes[5*MAX_HD] = {0, };
#define port_read(port,buf,nr) \
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
#define port_write(port,buf,nr) \
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
extern void hd_interrupt(void);
extern void rd_load(void);
/* This may be used only once, enforced by 'static int callable' */
int sys_setup(void * BIOS)
{
static int callable = 1;
int i,drive;
unsigned char cmos_disks;
struct partition *p;
struct buffer_head * bh;
if (!callable)
return -1;
callable = 0;
#ifndef HD_TYPE
for (drive=0 ; drive<2 ; drive++) {
hd_info[drive].cyl = *(unsigned short *) BIOS;
hd_info[drive].head = *(unsigned char *) (2+BIOS);
hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
hd_info[drive].sect = *(unsigned char *) (14+BIOS);
BIOS += 16;
}
if (hd_info[1].cyl)
NR_HD=2;
else
NR_HD=1;
#endif
for (i=0 ; i<NR_HD ; i++) {
hd[i*5].start_sect = 0;
hd[i*5].nr_sects = hd_info[i].head*
hd_info[i].sect*hd_info[i].cyl;
}
/*
We querry CMOS about hard disks : it could be that
we have a SCSI/ESDI/etc controller that is BIOS
compatable with ST-506, and thus showing up in our
BIOS table, but not register compatable, and therefore
not present in CMOS.
Furthurmore, we will assume that our ST-506 drives
<if any> are the primary drives in the system, and
the ones reflected as drive 1 or 2.
The first drive is stored in the high nibble of CMOS
byte 0x12, the second in the low nibble. This will be
either a 4 bit drive type or 0xf indicating use byte 0x19
for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
Needless to say, a non-zero value means we have
an AT controller hard disk for that drive.
*/
if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
if (cmos_disks & 0x0f)
NR_HD = 2;
else
NR_HD = 1;
else
NR_HD = 0;
for (i = NR_HD ; i < 2 ; i++) {
hd[i*5].start_sect = 0;
hd[i*5].nr_sects = 0;
}
for (drive=0 ; drive<NR_HD ; drive++) {
if (!(bh = bread(0x300 + drive*5,0))) {
printk("Unable to read partition table of drive %d\n\r",
drive);
panic("");
}
if (bh->b_data[510] != 0x55 || (unsigned char)
bh->b_data[511] != 0xAA) {
printk("Bad partition table on drive %d\n\r",drive);
panic("");
}
p = 0x1BE + (void *)bh->b_data;
for (i=1;i<5;i++,p++) {
hd[i+5*drive].start_sect = p->start_sect;
hd[i+5*drive].nr_sects = p->nr_sects;
}
brelse(bh);
}
for (i=0 ; i<5*MAX_HD ; i++)
hd_sizes[i] = hd[i].nr_sects>>1 ;
blk_size[MAJOR_NR] = hd_sizes;
if (NR_HD)
printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
rd_load();
init_swapping();
mount_root();
return (0);
}
static int controller_ready(void)
{
int retries = 100000;
while (--retries && (inb_p(HD_STATUS)&0xc0)!=0x40);
return (retries);
}
static int win_result(void)
{
int i=inb_p(HD_STATUS);
if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
== (READY_STAT | SEEK_STAT))
return(0); /* ok */
if (i&1) i=inb(HD_ERROR);
return (1);
}
static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
unsigned int head,unsigned int cyl,unsigned int cmd,
void (*intr_addr)(void))
{
register int port asm("dx");
if (drive>1 || head>15)
panic("Trying to write bad sector");
if (!controller_ready())
panic("HD controller not ready");
SET_INTR(intr_addr);
outb_p(hd_info[drive].ctl,HD_CMD);
port=HD_DATA;
outb_p(hd_info[drive].wpcom>>2,++port);
outb_p(nsect,++port);
outb_p(sect,++port);
outb_p(cyl,++port);
outb_p(cyl>>8,++port);
outb_p(0xA0|(drive<<4)|head,++port);
outb(cmd,++port);
}
static int drive_busy(void)
{
unsigned int i;
unsigned char c;
for (i = 0; i < 50000; i++) {
c = inb_p(HD_STATUS);
c &= (BUSY_STAT | READY_STAT | SEEK_STAT);
if (c == (READY_STAT | SEEK_STAT))
return 0;
}
printk("HD controller times out\n\r");
return(1);
}
static void reset_controller(void)
{
int i;
outb(4,HD_CMD);
for(i = 0; i < 1000; i++) nop();
outb(hd_info[0].ctl & 0x0f ,HD_CMD);
if (drive_busy())
printk("HD-controller still busy\n\r");
if ((i = inb(HD_ERROR)) != 1)
printk("HD-controller reset failed: %02x\n\r",i);
}
static void reset_hd(void)
{
static int i;
repeat:
if (reset) {
reset = 0;
i = -1;
reset_controller();
} else if (win_result()) {
bad_rw_intr();
if (reset)
goto repeat;
}
i++;
if (i < NR_HD) {
hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
} else
do_hd_request();
}
void unexpected_hd_interrupt(void)
{
printk("Unexpected HD interrupt\n\r");
reset = 1;
do_hd_request();
}
static void bad_rw_intr(void)
{
if (++CURRENT->errors >= MAX_ERRORS)
end_request(0);
if (CURRENT->errors > MAX_ERRORS/2)
reset = 1;
}
static void read_intr(void)
{
if (win_result()) {
bad_rw_intr();
do_hd_request();
return;
}
port_read(HD_DATA,CURRENT->buffer,256);
CURRENT->errors = 0;
CURRENT->buffer += 512;
CURRENT->sector++;
if (--CURRENT->nr_sectors) {
SET_INTR(&read_intr);
return;
}
end_request(1);
do_hd_request();
}
static void write_intr(void)
{
if (win_result()) {
bad_rw_intr();
do_hd_request();
return;
}
if (--CURRENT->nr_sectors) {
CURRENT->sector++;
CURRENT->buffer += 512;
SET_INTR(&write_intr);
port_write(HD_DATA,CURRENT->buffer,256);
return;
}
end_request(1);
do_hd_request();
}
static void recal_intr(void)
{
if (win_result())
bad_rw_intr();
do_hd_request();
}
void hd_times_out(void)
{
if (!CURRENT)
return;
printk("HD timeout");
if (++CURRENT->errors >= MAX_ERRORS)
end_request(0);
SET_INTR(NULL);
reset = 1;
do_hd_request();
}
void do_hd_request(void)
{
int i,r;
unsigned int block,dev;
unsigned int sec,head,cyl;
unsigned int nsect;
INIT_REQUEST;
dev = MINOR(CURRENT->dev);
block = CURRENT->sector;
if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) {
end_request(0);
goto repeat;
}
block += hd[dev].start_sect;
dev /= 5;
__asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),
"r" (hd_info[dev].sect));
__asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
"r" (hd_info[dev].head));
sec++;
nsect = CURRENT->nr_sectors;
if (reset) {
recalibrate = 1;
reset_hd();
return;
}
if (recalibrate) {
recalibrate = 0;
hd_out(dev,hd_info[CURRENT_DEV].sect,0,0,0,
WIN_RESTORE,&recal_intr);
return;
}
if (CURRENT->cmd == WRITE) {
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
/* nothing */ ;
if (!r) {
bad_rw_intr();
goto repeat;
}
port_write(HD_DATA,CURRENT->buffer,256);
} else if (CURRENT->cmd == READ) {
hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
} else
panic("unknown hd-command");
}
void hd_init(void)
{
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
set_intr_gate(0x2E,&hd_interrupt);
outb_p(inb_p(0x21)&0xfb,0x21);
outb(inb_p(0xA1)&0xbf,0xA1);
}

View File

@@ -0,0 +1,218 @@
/*
* linux/kernel/blk_dev/ll_rw.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This handles all read/write requests to block devices
*/
#include <errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>
#include "blk.h"
/*
* The request-struct contains all necessary data
* to load a nr of sectors into memory
*/
struct request request[NR_REQUEST];
/*
* used to wait on when there are no free requests
*/
struct task_struct * wait_for_request = NULL;
/* blk_dev_struct is:
* do_request-address
* next-request
*/
struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
{ NULL, NULL }, /* no_dev */
{ NULL, NULL }, /* dev mem */
{ NULL, NULL }, /* dev fd */
{ NULL, NULL }, /* dev hd */
{ NULL, NULL }, /* dev ttyx */
{ NULL, NULL }, /* dev tty */
{ NULL, NULL } /* dev lp */
};
/*
* blk_size contains the size of all block-devices:
*
* blk_size[MAJOR][MINOR]
*
* if (!blk_size[MAJOR]) then no minor size checking is done.
*/
int * blk_size[NR_BLK_DEV] = { NULL, NULL, };
static inline void lock_buffer(struct buffer_head * bh)
{
cli();
while (bh->b_lock)
sleep_on(&bh->b_wait);
bh->b_lock=1;
sti();
}
static inline void unlock_buffer(struct buffer_head * bh)
{
if (!bh->b_lock)
printk("ll_rw_block.c: buffer not locked\n\r");
bh->b_lock = 0;
wake_up(&bh->b_wait);
}
/*
* add-request adds a request to the linked list.
* It disables interrupts so that it can muck with the
* request-lists in peace.
*
* Note that swapping requests always go before other requests,
* and are done in the order they appear.
*/
static void add_request(struct blk_dev_struct * dev, struct request * req)
{
struct request * tmp;
req->next = NULL;
cli();
if (req->bh)
req->bh->b_dirt = 0;
if (!(tmp = dev->current_request)) {
dev->current_request = req;
sti();
(dev->request_fn)();
return;
}
for ( ; tmp->next ; tmp=tmp->next) {
if (!req->bh)
if (tmp->next->bh)
break;
else
continue;
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
}
req->next=tmp->next;
tmp->next=req;
sti();
}
static void make_request(int major,int rw, struct buffer_head * bh)
{
struct request * req;
int rw_ahead;
/* WRITEA/READA is special case - it is not really needed, so if the */
/* buffer is locked, we just forget about it, else it's a normal read */
if (rw_ahead = (rw == READA || rw == WRITEA)) {
if (bh->b_lock)
return;
if (rw == READA)
rw = READ;
else
rw = WRITE;
}
if (rw!=READ && rw!=WRITE)
panic("Bad block dev command, must be R/W/RA/WA");
lock_buffer(bh);
if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
unlock_buffer(bh);
return;
}
repeat:
/* we don't allow the write-requests to fill up the queue completely:
* we want some room for reads: they take precedence. The last third
* of the requests are only for reads.
*/
if (rw == READ)
req = request+NR_REQUEST;
else
req = request+((NR_REQUEST*2)/3);
/* find an empty request */
while (--req >= request)
if (req->dev<0)
break;
/* if none found, sleep on new requests: check for rw_ahead */
if (req < request) {
if (rw_ahead) {
unlock_buffer(bh);
return;
}
sleep_on(&wait_for_request);
goto repeat;
}
/* fill up the request-info, and add it to the queue */
req->dev = bh->b_dev;
req->cmd = rw;
req->errors=0;
req->sector = bh->b_blocknr<<1;
req->nr_sectors = 2;
req->buffer = bh->b_data;
req->waiting = NULL;
req->bh = bh;
req->next = NULL;
add_request(major+blk_dev,req);
}
void ll_rw_page(int rw, int dev, int page, char * buffer)
{
struct request * req;
unsigned int major = MAJOR(dev);
if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) {
printk("Trying to read nonexistent block-device\n\r");
return;
}
if (rw!=READ && rw!=WRITE)
panic("Bad block dev command, must be R/W");
repeat:
req = request+NR_REQUEST;
while (--req >= request)
if (req->dev<0)
break;
if (req < request) {
sleep_on(&wait_for_request);
goto repeat;
}
/* fill up the request-info, and add it to the queue */
req->dev = dev;
req->cmd = rw;
req->errors = 0;
req->sector = page<<3;
req->nr_sectors = 8;
req->buffer = buffer;
req->waiting = current;
req->bh = NULL;
req->next = NULL;
current->state = TASK_UNINTERRUPTIBLE;
add_request(major+blk_dev,req);
schedule();
}
void ll_rw_block(int rw, struct buffer_head * bh)
{
unsigned int major;
if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV ||
!(blk_dev[major].request_fn)) {
printk("Trying to read nonexistent block-device\n\r");
return;
}
make_request(major,rw,bh);
}
void blk_dev_init(void)
{
int i;
for (i=0 ; i<NR_REQUEST ; i++) {
request[i].dev = -1;
request[i].next = NULL;
}
}

View File

@@ -0,0 +1,125 @@
/*
* linux/kernel/blk_drv/ramdisk.c
*
* Written by Theodore Ts'o, 12/2/91
*/
#include <string.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/memory.h>
#define MAJOR_NR 1
#include "blk.h"
char *rd_start;
int rd_length = 0;
void do_rd_request(void)
{
int len;
char *addr;
INIT_REQUEST;
addr = rd_start + (CURRENT->sector << 9);
len = CURRENT->nr_sectors << 9;
if ((MINOR(CURRENT->dev) != 1) || (addr+len > rd_start+rd_length)) {
end_request(0);
goto repeat;
}
if (CURRENT-> cmd == WRITE) {
(void ) memcpy(addr,
CURRENT->buffer,
len);
} else if (CURRENT->cmd == READ) {
(void) memcpy(CURRENT->buffer,
addr,
len);
} else
panic("unknown ramdisk-command");
end_request(1);
goto repeat;
}
/*
* Returns amount of memory which needs to be reserved.
*/
long rd_init(long mem_start, int length)
{
int i;
char *cp;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
rd_start = (char *) mem_start;
rd_length = length;
cp = rd_start;
for (i=0; i < length; i++)
*cp++ = '\0';
return(length);
}
/*
* If the root device is the ram disk, try to load it.
* In order to do this, the root device is originally set to the
* floppy, and we later change it to be ram disk.
*/
void rd_load(void)
{
struct buffer_head *bh;
struct super_block s;
int block = 256; /* Start at block 256 */
int i = 1;
int nblocks;
char *cp; /* Move pointer */
if (!rd_length)
return;
printk("Ram disk: %d bytes, starting at 0x%x\n", rd_length,
(int) rd_start);
if (MAJOR(ROOT_DEV) != 2)
return;
bh = breada(ROOT_DEV,block+1,block,block+2,-1);
if (!bh) {
printk("Disk error while looking for ramdisk!\n");
return;
}
*((struct d_super_block *) &s) = *((struct d_super_block *) bh->b_data);
brelse(bh);
if (s.s_magic != SUPER_MAGIC)
/* No ram disk image present, assume normal floppy boot */
return;
nblocks = s.s_nzones << s.s_log_zone_size;
if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
printk("Ram disk image too big! (%d blocks, %d avail)\n",
nblocks, rd_length >> BLOCK_SIZE_BITS);
return;
}
printk("Loading %d bytes into ram disk... 0000k",
nblocks << BLOCK_SIZE_BITS);
cp = rd_start;
while (nblocks) {
if (nblocks > 2)
bh = breada(ROOT_DEV, block, block+1, block+2, -1);
else
bh = bread(ROOT_DEV, block);
if (!bh) {
printk("I/O error on block %d, aborting load\n",
block);
return;
}
(void) memcpy(cp, bh->b_data, BLOCK_SIZE);
brelse(bh);
printk("\010\010\010\010\010%4dk",i);
cp += BLOCK_SIZE;
block++;
nblocks--;
i++;
}
printk("\010\010\010\010\010done \n");
ROOT_DEV=0x0101;
}

View File

@@ -0,0 +1,88 @@
#
# Makefile for the FREAX-kernel character device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
AR =gar
AS =gas
LD =gld
LDFLAGS =-s -x
CC =gcc
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \
-finline-functions -mstring-insns -nostdinc -I../../include
CPP =gcc -E -nostdinc -I../../include
.c.s:
$(CC) $(CFLAGS) \
-S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-c -o $*.o $<
OBJS = tty_io.o console.o keyboard.o serial.o rs_io.o \
tty_ioctl.o pty.o
chr_drv.a: $(OBJS)
$(AR) rcs chr_drv.a $(OBJS)
sync
keyboard.s: keyboard.S
$(CPP) -traditional keyboard.S -o keyboard.s
clean:
rm -f core *.o *.a tmp_make keyboard.s
for i in *.c;do rm -f `basename $$i .c`.s;done
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
$(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile
### Dependencies:
console.s console.o : console.c ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/tty.h \
../../include/termios.h ../../include/linux/config.h ../../include/asm/io.h \
../../include/asm/system.h ../../include/asm/segment.h \
../../include/string.h ../../include/errno.h
pty.s pty.o : pty.c ../../include/linux/tty.h ../../include/termios.h \
../../include/sys/types.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h \
../../include/time.h ../../include/sys/resource.h \
../../include/asm/system.h ../../include/asm/io.h
serial.s serial.o : serial.c ../../include/linux/tty.h ../../include/termios.h \
../../include/sys/types.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h \
../../include/time.h ../../include/sys/resource.h \
../../include/asm/system.h ../../include/asm/io.h
tty_io.s tty_io.o : tty_io.c ../../include/ctype.h ../../include/errno.h \
../../include/signal.h ../../include/sys/types.h ../../include/unistd.h \
../../include/sys/stat.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/times.h ../../include/sys/utsname.h \
../../include/sys/param.h ../../include/sys/resource.h \
../../include/utime.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/linux/tty.h ../../include/termios.h \
../../include/asm/segment.h ../../include/asm/system.h
tty_ioctl.s tty_ioctl.o : tty_ioctl.c ../../include/errno.h ../../include/termios.h \
../../include/sys/types.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h \
../../include/time.h ../../include/sys/resource.h ../../include/linux/tty.h \
../../include/asm/io.h ../../include/asm/segment.h \
../../include/asm/system.h

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,599 @@
/*
* linux/kernel/keyboard.S
*
* (C) 1991 Linus Torvalds
*/
/*
* Thanks to Alfred Leung for US keyboard patches
* Wolfgang Thiel for German keyboard patches
* Marc Corsini for the French keyboard
*/
/* KBD_FINNISH for Finnish keyboards
* KBD_US for US-type
* KBD_GR for German keyboards
* KBD_FR for Frech keyboard
*/
#define KBD_FINNISH
.text
.globl _keyboard_interrupt
/*
* these are for the keyboard read functions
*/
size = 1024 /* must be a power of two ! And MUST be the same
as in tty_io.c !!!! */
head = 4
tail = 8
proc_list = 12
buf = 16
mode: .byte 0 /* caps, alt, ctrl and shift mode */
leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */
e0: .byte 0
/*
* con_int is the real interrupt routine that reads the
* keyboard scan-code and converts it into the appropriate
* ascii character(s).
*/
_keyboard_interrupt:
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
push %ds
push %es
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl _blankinterval,%eax
movl %eax,_blankcount
xorl %eax,%eax /* %eax is scan code */
inb $0x60,%al
cmpb $0xe0,%al
je set_e0
cmpb $0xe1,%al
je set_e1
call key_table(,%eax,4)
movb $0,e0
e0_e1: inb $0x61,%al
jmp 1f
1: jmp 1f
1: orb $0x80,%al
jmp 1f
1: jmp 1f
1: outb %al,$0x61
jmp 1f
1: jmp 1f
1: andb $0x7F,%al
outb %al,$0x61
movb $0x20,%al
outb %al,$0x20
pushl $0
call _do_tty_interrupt
addl $4,%esp
pop %es
pop %ds
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
set_e0: movb $1,e0
jmp e0_e1
set_e1: movb $2,e0
jmp e0_e1
/*
* This routine fills the buffer with max 8 bytes, taken from
* %ebx:%eax. (%edx is high). The bytes are written in the
* order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.
*/
put_queue:
pushl %ecx
pushl %edx
movl _table_list,%edx # read-queue for console
movl head(%edx),%ecx
1: movb %al,buf(%edx,%ecx)
incl %ecx
andl $size-1,%ecx
cmpl tail(%edx),%ecx # buffer full - discard everything
je 3f
shrdl $8,%ebx,%eax
je 2f
shrl $8,%ebx
jmp 1b
2: movl %ecx,head(%edx)
movl proc_list(%edx),%ecx
testl %ecx,%ecx
je 3f
movl $0,(%ecx)
3: popl %edx
popl %ecx
ret
ctrl: movb $0x04,%al
jmp 1f
alt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: orb %al,mode
ret
unctrl: movb $0x04,%al
jmp 1f
unalt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: notb %al
andb %al,mode
ret
lshift:
orb $0x01,mode
ret
unlshift:
andb $0xfe,mode
ret
rshift:
orb $0x02,mode
ret
unrshift:
andb $0xfd,mode
ret
caps: testb $0x80,mode
jne 1f
xorb $4,leds
xorb $0x40,mode
orb $0x80,mode
set_leds:
call kb_wait
movb $0xed,%al /* set leds command */
outb %al,$0x60
call kb_wait
movb leds,%al
outb %al,$0x60
ret
uncaps: andb $0x7f,mode
ret
scroll:
testb $0x03,mode
je 1f
call _show_mem
jmp 2f
1: call _show_state
2: xorb $1,leds
jmp set_leds
num: xorb $2,leds
jmp set_leds
/*
* curosr-key/numeric keypad cursor keys are handled here.
* checking for numeric keypad etc.
*/
cursor:
subb $0x47,%al
jb 1f
cmpb $12,%al
ja 1f
jne cur2 /* check for ctrl-alt-del */
testb $0x0c,mode
je cur2
testb $0x30,mode
jne reboot
cur2: cmpb $0x01,e0 /* e0 forces cursor movement */
je cur
testb $0x02,leds /* not num-lock forces cursor */
je cur
testb $0x03,mode /* shift forces cursor */
jne cur
xorl %ebx,%ebx
movb num_table(%eax),%al
jmp put_queue
1: ret
cur: movb cur_table(%eax),%al
cmpb $'9,%al
ja ok_cur
movb $'~,%ah
ok_cur: shll $16,%eax
movw $0x5b1b,%ax
xorl %ebx,%ebx
jmp put_queue
#if defined(KBD_FR)
num_table:
.ascii "789 456 1230."
#else
num_table:
.ascii "789 456 1230,"
#endif
cur_table:
.ascii "HA5 DGC YB623"
/*
* this routine handles function keys
*/
func:
subb $0x3B,%al
jb end_func
cmpb $9,%al
jbe ok_func
subb $18,%al
cmpb $10,%al
jb end_func
cmpb $11,%al
ja end_func
ok_func:
testb $0x10,mode
jne alt_func
cmpl $4,%ecx /* check that there is enough room */
jl end_func
movl func_table(,%eax,4),%eax
xorl %ebx,%ebx
jmp put_queue
alt_func:
pushl %eax
call _change_console
popl %eax
end_func:
ret
/*
* function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.
*/
func_table:
.long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
.long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
.long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b
#if defined(KBD_FINNISH)
key_map:
.byte 0,27
.ascii "1234567890+'"
.byte 127,9
.ascii "qwertyuiop}"
.byte 0,13,0
.ascii "asdfghjkl|{"
.byte 0,0
.ascii "'zxcvbnm,.-"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!\"#$%&/()=?`"
.byte 127,9
.ascii "QWERTYUIOP]^"
.byte 13,0
.ascii "ASDFGHJKL\\["
.byte 0,0
.ascii "*ZXCVBNM;:_"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_US)
key_map:
.byte 0,27
.ascii "1234567890-="
.byte 127,9
.ascii "qwertyuiop[]"
.byte 13,0
.ascii "asdfghjkl;'"
.byte '`,0
.ascii "\\zxcvbnm,./"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!@#$%^&*()_+"
.byte 127,9
.ascii "QWERTYUIOP{}"
.byte 13,0
.ascii "ASDFGHJKL:\""
.byte '~,0
.ascii "|ZXCVBNM<>?"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_GR)
key_map:
.byte 0,27
.ascii "1234567890\\'"
.byte 127,9
.ascii "qwertzuiop@+"
.byte 13,0
.ascii "asdfghjkl[]^"
.byte 0,'#
.ascii "yxcvbnm,.-"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!\"#$%&/()=?`"
.byte 127,9
.ascii "QWERTZUIOP\\*"
.byte 13,0
.ascii "ASDFGHJKL{}~"
.byte 0,''
.ascii "YXCVBNM;:_"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte '@,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_FR)
key_map:
.byte 0,27
.ascii "&{\"'(-}_/@)="
.byte 127,9
.ascii "azertyuiop^$"
.byte 13,0
.ascii "qsdfghjklm|"
.byte '`,0,42 /* coin sup gauche, don't know, [*|mu] */
.ascii "wxcvbn,;:!"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "1234567890]+"
.byte 127,9
.ascii "AZERTYUIOP<>"
.byte 13,0
.ascii "QSDFGHJKLM%"
.byte '~,0,'#
.ascii "WXCVBN?./\\"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0~#{[|`\\^@]}"
.byte 0,0
.byte '@,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#else
#error "KBD-type not defined"
#endif
/*
* do_self handles "normal" keys, ie keys that don't change meaning
* and which have just one character returns.
*/
do_self:
lea alt_map,%ebx
testb $0x20,mode /* alt-gr */
jne 1f
lea shift_map,%ebx
testb $0x03,mode
jne 1f
lea key_map,%ebx
1: movb (%ebx,%eax),%al
orb %al,%al
je none
testb $0x4c,mode /* ctrl or caps */
je 2f
cmpb $'a,%al
jb 2f
cmpb $'},%al
ja 2f
subb $32,%al
2: testb $0x0c,mode /* ctrl */
je 3f
cmpb $64,%al
jb 3f
cmpb $64+32,%al
jae 3f
subb $64,%al
3: testb $0x10,mode /* left alt */
je 4f
orb $0x80,%al
4: andl $0xff,%eax
xorl %ebx,%ebx
call put_queue
none: ret
/*
* minus has a routine of it's own, as a 'E0h' before
* the scan code for minus means that the numeric keypad
* slash was pushed.
*/
minus: cmpb $1,e0
jne do_self
movl $'/,%eax
xorl %ebx,%ebx
jmp put_queue
/*
* This table decides which routine to call when a scan-code has been
* gotten. Most routines just call do_self, or none, depending if
* they are make or break.
*/
key_table:
.long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */
.long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */
.long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */
.long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */
.long do_self,do_self,do_self,do_self /* 10-13 q w e r */
.long do_self,do_self,do_self,do_self /* 14-17 t y u i */
.long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */
.long do_self,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */
.long do_self,do_self,do_self,do_self /* 20-23 d f g h */
.long do_self,do_self,do_self,do_self /* 24-27 j k l | */
.long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */
.long do_self,do_self,do_self,do_self /* 2C-2F z x c v */
.long do_self,do_self,do_self,do_self /* 30-33 b n m , */
.long do_self,minus,rshift,do_self /* 34-37 . - rshift * */
.long alt,do_self,caps,func /* 38-3B alt sp caps f1 */
.long func,func,func,func /* 3C-3F f2 f3 f4 f5 */
.long func,func,func,func /* 40-43 f6 f7 f8 f9 */
.long func,num,scroll,cursor /* 44-47 f10 num scr home */
.long cursor,cursor,do_self,cursor /* 48-4B up pgup - left */
.long cursor,cursor,do_self,cursor /* 4C-4F n5 right + end */
.long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */
.long none,none,do_self,func /* 54-57 sysreq ? < f11 */
.long func,none,none,none /* 58-5B f12 ? ? ? */
.long none,none,none,none /* 5C-5F ? ? ? ? */
.long none,none,none,none /* 60-63 ? ? ? ? */
.long none,none,none,none /* 64-67 ? ? ? ? */
.long none,none,none,none /* 68-6B ? ? ? ? */
.long none,none,none,none /* 6C-6F ? ? ? ? */
.long none,none,none,none /* 70-73 ? ? ? ? */
.long none,none,none,none /* 74-77 ? ? ? ? */
.long none,none,none,none /* 78-7B ? ? ? ? */
.long none,none,none,none /* 7C-7F ? ? ? ? */
.long none,none,none,none /* 80-83 ? br br br */
.long none,none,none,none /* 84-87 br br br br */
.long none,none,none,none /* 88-8B br br br br */
.long none,none,none,none /* 8C-8F br br br br */
.long none,none,none,none /* 90-93 br br br br */
.long none,none,none,none /* 94-97 br br br br */
.long none,none,none,none /* 98-9B br br br br */
.long none,unctrl,none,none /* 9C-9F br unctrl br br */
.long none,none,none,none /* A0-A3 br br br br */
.long none,none,none,none /* A4-A7 br br br br */
.long none,none,unlshift,none /* A8-AB br br unlshift br */
.long none,none,none,none /* AC-AF br br br br */
.long none,none,none,none /* B0-B3 br br br br */
.long none,none,unrshift,none /* B4-B7 br br unrshift br */
.long unalt,none,uncaps,none /* B8-BB unalt br uncaps br */
.long none,none,none,none /* BC-BF br br br br */
.long none,none,none,none /* C0-C3 br br br br */
.long none,none,none,none /* C4-C7 br br br br */
.long none,none,none,none /* C8-CB br br br br */
.long none,none,none,none /* CC-CF br br br br */
.long none,none,none,none /* D0-D3 br br br br */
.long none,none,none,none /* D4-D7 br br br br */
.long none,none,none,none /* D8-DB br ? ? ? */
.long none,none,none,none /* DC-DF ? ? ? ? */
.long none,none,none,none /* E0-E3 e0 e1 ? ? */
.long none,none,none,none /* E4-E7 ? ? ? ? */
.long none,none,none,none /* E8-EB ? ? ? ? */
.long none,none,none,none /* EC-EF ? ? ? ? */
.long none,none,none,none /* F0-F3 ? ? ? ? */
.long none,none,none,none /* F4-F7 ? ? ? ? */
.long none,none,none,none /* F8-FB ? ? ? ? */
.long none,none,none,none /* FC-FF ? ? ? ? */
/*
* kb_wait waits for the keyboard controller buffer to empty.
* there is no timeout - if the buffer doesn't empty, we hang.
*/
kb_wait:
pushl %eax
1: inb $0x64,%al
testb $0x02,%al
jne 1b
popl %eax
ret
/*
* This routine reboots the machine by asking the keyboard
* controller to pulse the reset-line low.
*/
reboot:
call kb_wait
movw $0x1234,0x472 /* don't do memory check */
movb $0xfc,%al /* pulse reset and A20 low */
outb %al,$0x64
die: jmp die

View File

@@ -0,0 +1,63 @@
/*
* linux/kernel/chr_drv/pty.c
*
* (C) 1991 Linus Torvalds
*/
/*
* pty.c
*
* This module implements the pty functions
* void mpty_write(struct tty_struct * queue);
* void spty_write(struct tty_struct * queue);
*/
#include <linux/tty.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/io.h>
static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
{
char c;
while (!from->stopped && !EMPTY(from->write_q)) {
if (FULL(to->read_q)) {
if (FULL(to->secondary))
break;
copy_to_cooked(to);
continue;
}
GETCH(from->write_q,c);
PUTCH(c,to->read_q);
if (current->signal & ~current->blocked)
break;
}
copy_to_cooked(to);
wake_up(&from->write_q->proc_list);
}
/*
* This routine gets called when tty_write has put something into
* the write_queue. It copies the input to the output-queue of it's
* slave.
*/
void mpty_write(struct tty_struct * tty)
{
int nr = tty - tty_table;
if ((nr >> 6) != 2)
printk("bad mpty\n\r");
else
pty_copy(tty,tty+64);
}
void spty_write(struct tty_struct * tty)
{
int nr = tty - tty_table;
if ((nr >> 6) != 3)
printk("bad spty\n\r");
else
pty_copy(tty,tty-64);
}

View File

@@ -0,0 +1,148 @@
/*
* linux/kernel/rs_io.s
*
* (C) 1991 Linus Torvalds
*/
/*
* rs_io.s
*
* This module implements the rs232 io interrupts.
*/
.text
.globl _rs1_interrupt,_rs2_interrupt
size = 1024 /* must be power of two !
and must match the value
in tty_io.c!!! */
/* these are the offsets into the read/write buffer structures */
rs_addr = 0
head = 4
tail = 8
proc_list = 12
buf = 16
startup = 256 /* chars left in write queue when we restart it */
/*
* These are the actual interrupt routines. They look where
* the interrupt is coming from, and take appropriate action.
*/
.align 2
_rs1_interrupt:
pushl $_table_list+8
jmp rs_int
.align 2
_rs2_interrupt:
pushl $_table_list+16
rs_int:
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
push %es
push %ds /* as this is an interrupt, we cannot */
pushl $0x10 /* know that bs is ok. Load it */
pop %ds
pushl $0x10
pop %es
movl 24(%esp),%edx
movl (%edx),%edx
movl rs_addr(%edx),%edx
addl $2,%edx /* interrupt ident. reg */
rep_int:
xorl %eax,%eax
inb %dx,%al
testb $1,%al
jne end
cmpb $6,%al /* this shouldn't happen, but ... */
ja end
movl 24(%esp),%ecx
pushl %edx
subl $2,%edx
call jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */
popl %edx
jmp rep_int
end: movb $0x20,%al
outb %al,$0x20 /* EOI */
pop %ds
pop %es
popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4,%esp # jump over _table_list entry
iret
jmp_table:
.long modem_status,write_char,read_char,line_status
.align 2
modem_status:
addl $6,%edx /* clear intr by reading modem status reg */
inb %dx,%al
ret
.align 2
line_status:
addl $5,%edx /* clear intr by reading line status reg. */
inb %dx,%al
ret
.align 2
read_char:
inb %dx,%al
movl %ecx,%edx
subl $_table_list,%edx
shrl $3,%edx
movl (%ecx),%ecx # read-queue
movl head(%ecx),%ebx
movb %al,buf(%ecx,%ebx)
incl %ebx
andl $size-1,%ebx
cmpl tail(%ecx),%ebx
je 1f
movl %ebx,head(%ecx)
1: addl $63,%edx
pushl %edx
call _do_tty_interrupt
addl $4,%esp
ret
.align 2
write_char:
movl 4(%ecx),%ecx # write-queue
movl head(%ecx),%ebx
subl tail(%ecx),%ebx
andl $size-1,%ebx # nr chars in queue
je write_buffer_empty
cmpl $startup,%ebx
ja 1f
movl proc_list(%ecx),%ebx # wake up sleeping process
testl %ebx,%ebx # is there any?
je 1f
movl $0,(%ebx)
1: movl tail(%ecx),%ebx
movb buf(%ecx,%ebx),%al
outb %al,%dx
incl %ebx
andl $size-1,%ebx
movl %ebx,tail(%ecx)
cmpl head(%ecx),%ebx
je write_buffer_empty
ret
.align 2
write_buffer_empty:
movl proc_list(%ecx),%ebx # wake up sleeping process
testl %ebx,%ebx # is there any?
je 1f
movl $0,(%ebx)
1: incl %edx
inb %dx,%al
jmp 1f
1: jmp 1f
1: andb $0xd,%al /* disable transmit interrupt */
outb %al,%dx
ret

View File

@@ -0,0 +1,59 @@
/*
* linux/kernel/serial.c
*
* (C) 1991 Linus Torvalds
*/
/*
* serial.c
*
* This module implements the rs232 io functions
* void rs_write(struct tty_struct * queue);
* void rs_init(void);
* and all interrupts pertaining to serial IO.
*/
#include <linux/tty.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/io.h>
#define WAKEUP_CHARS (TTY_BUF_SIZE/4)
extern void rs1_interrupt(void);
extern void rs2_interrupt(void);
static void init(int port)
{
outb_p(0x80,port+3); /* set DLAB of line control reg */
outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps */
outb_p(0x00,port+1); /* MS of divisor */
outb_p(0x03,port+3); /* reset DLAB */
outb_p(0x0b,port+4); /* set DTR,RTS, OUT_2 */
outb_p(0x0d,port+1); /* enable all intrs but writes */
(void)inb(port); /* read data port to reset things (?) */
}
void rs_init(void)
{
set_intr_gate(0x24,rs1_interrupt);
set_intr_gate(0x23,rs2_interrupt);
init(tty_table[64].read_q->data);
init(tty_table[65].read_q->data);
outb(inb_p(0x21)&0xE7,0x21);
}
/*
* This routine gets called when tty_write has put something into
* the write_queue. It must check wheter the queue is empty, and
* set the interrupt register accordingly
*
* void _rs_write(struct tty_struct * tty);
*/
void rs_write(struct tty_struct * tty)
{
cli();
if (!EMPTY(tty->write_q))
outb(inb_p(tty->write_q->data+1)|0x02,tty->write_q->data+1);
sti();
}

View File

@@ -0,0 +1,484 @@
/*
* linux/kernel/tty_io.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
* or rs-channels. It also implements echoing, cooked mode etc.
*
* Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
*/
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#define ALRMMASK (1<<(SIGALRM-1))
#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/segment.h>
#include <asm/system.h>
int kill_pg(int pgrp, int sig, int priv);
int is_orphaned_pgrp(int pgrp);
#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f)
#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f)
#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f)
#define L_CANON(tty) _L_FLAG((tty),ICANON)
#define L_ISIG(tty) _L_FLAG((tty),ISIG)
#define L_ECHO(tty) _L_FLAG((tty),ECHO)
#define L_ECHOE(tty) _L_FLAG((tty),ECHOE)
#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)
#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)
#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP)
#define I_UCLC(tty) _I_FLAG((tty),IUCLC)
#define I_NLCR(tty) _I_FLAG((tty),INLCR)
#define I_CRNL(tty) _I_FLAG((tty),ICRNL)
#define I_NOCR(tty) _I_FLAG((tty),IGNCR)
#define I_IXON(tty) _I_FLAG((tty),IXON)
#define O_POST(tty) _O_FLAG((tty),OPOST)
#define O_NLCR(tty) _O_FLAG((tty),ONLCR)
#define O_CRNL(tty) _O_FLAG((tty),OCRNL)
#define O_NLRET(tty) _O_FLAG((tty),ONLRET)
#define O_LCUC(tty) _O_FLAG((tty),OLCUC)
#define C_SPEED(tty) ((tty)->termios.c_cflag & CBAUD)
#define C_HUP(tty) (C_SPEED((tty)) == B0)
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#define QUEUES (3*(MAX_CONSOLES+NR_SERIALS+2*NR_PTYS))
static struct tty_queue tty_queues[QUEUES];
struct tty_struct tty_table[256];
#define con_queues tty_queues
#define rs_queues ((3*MAX_CONSOLES) + tty_queues)
#define mpty_queues ((3*(MAX_CONSOLES+NR_SERIALS)) + tty_queues)
#define spty_queues ((3*(MAX_CONSOLES+NR_SERIALS+NR_PTYS)) + tty_queues)
#define con_table tty_table
#define rs_table (64+tty_table)
#define mpty_table (128+tty_table)
#define spty_table (192+tty_table)
int fg_console = 0;
/*
* these are the tables used by the machine code handlers.
* you can implement virtual consoles.
*/
struct tty_queue * table_list[]={
con_queues + 0, con_queues + 1,
rs_queues + 0, rs_queues + 1,
rs_queues + 3, rs_queues + 4
};
void change_console(unsigned int new_console)
{
if (new_console == fg_console || new_console >= NR_CONSOLES)
return;
fg_console = new_console;
table_list[0] = con_queues + 0 + fg_console*3;
table_list[1] = con_queues + 1 + fg_console*3;
update_screen();
}
static void sleep_if_empty(struct tty_queue * queue)
{
cli();
while (!(current->signal & ~current->blocked) && EMPTY(queue))
interruptible_sleep_on(&queue->proc_list);
sti();
}
static void sleep_if_full(struct tty_queue * queue)
{
if (!FULL(queue))
return;
cli();
while (!(current->signal & ~current->blocked) && LEFT(queue)<128)
interruptible_sleep_on(&queue->proc_list);
sti();
}
void wait_for_keypress(void)
{
sleep_if_empty(tty_table[fg_console].secondary);
}
void copy_to_cooked(struct tty_struct * tty)
{
signed char c;
if (!(tty->read_q || tty->write_q || tty->secondary)) {
printk("copy_to_cooked: missing queues\n\r");
return;
}
while (1) {
if (EMPTY(tty->read_q))
break;
if (FULL(tty->secondary))
break;
GETCH(tty->read_q,c);
if (c==13) {
if (I_CRNL(tty))
c=10;
else if (I_NOCR(tty))
continue;
} else if (c==10 && I_NLCR(tty))
c=13;
if (I_UCLC(tty))
c=tolower(c);
if (L_CANON(tty)) {
if ((KILL_CHAR(tty) != _POSIX_VDISABLE) &&
(c==KILL_CHAR(tty))) {
/* deal with killing the input line */
while(!(EMPTY(tty->secondary) ||
(c=LAST(tty->secondary))==10 ||
((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
(c==EOF_CHAR(tty))))) {
if (L_ECHO(tty)) {
if (c<32)
PUTCH(127,tty->write_q);
PUTCH(127,tty->write_q);
tty->write(tty);
}
DEC(tty->secondary->head);
}
continue;
}
if ((ERASE_CHAR(tty) != _POSIX_VDISABLE) &&
(c==ERASE_CHAR(tty))) {
if (EMPTY(tty->secondary) ||
(c=LAST(tty->secondary))==10 ||
((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
(c==EOF_CHAR(tty))))
continue;
if (L_ECHO(tty)) {
if (c<32)
PUTCH(127,tty->write_q);
PUTCH(127,tty->write_q);
tty->write(tty);
}
DEC(tty->secondary->head);
continue;
}
}
if (I_IXON(tty)) {
if ((STOP_CHAR(tty) != _POSIX_VDISABLE) &&
(c==STOP_CHAR(tty))) {
tty->stopped=1;
tty->write(tty);
continue;
}
if ((START_CHAR(tty) != _POSIX_VDISABLE) &&
(c==START_CHAR(tty))) {
tty->stopped=0;
tty->write(tty);
continue;
}
}
if (L_ISIG(tty)) {
if ((INTR_CHAR(tty) != _POSIX_VDISABLE) &&
(c==INTR_CHAR(tty))) {
kill_pg(tty->pgrp, SIGINT, 1);
continue;
}
if ((QUIT_CHAR(tty) != _POSIX_VDISABLE) &&
(c==QUIT_CHAR(tty))) {
kill_pg(tty->pgrp, SIGQUIT, 1);
continue;
}
if ((SUSPEND_CHAR(tty) != _POSIX_VDISABLE) &&
(c==SUSPEND_CHAR(tty))) {
if (!is_orphaned_pgrp(tty->pgrp))
kill_pg(tty->pgrp, SIGTSTP, 1);
continue;
}
}
if (c==10 || (EOF_CHAR(tty) != _POSIX_VDISABLE &&
c==EOF_CHAR(tty)))
tty->secondary->data++;
if (L_ECHO(tty)) {
if (c==10) {
PUTCH(10,tty->write_q);
PUTCH(13,tty->write_q);
} else if (c<32) {
if (L_ECHOCTL(tty)) {
PUTCH('^',tty->write_q);
PUTCH(c+64,tty->write_q);
}
} else
PUTCH(c,tty->write_q);
tty->write(tty);
}
PUTCH(c,tty->secondary);
}
wake_up(&tty->secondary->proc_list);
}
/*
* Called when we need to send a SIGTTIN or SIGTTOU to our process
* group
*
* We only request that a system call be restarted if there was if the
* default signal handler is being used. The reason for this is that if
* a job is catching SIGTTIN or SIGTTOU, the signal handler may not want
* the system call to be restarted blindly. If there is no way to reset the
* terminal pgrp back to the current pgrp (perhaps because the controlling
* tty has been released on logout), we don't want to be in an infinite loop
* while restarting the system call, and have it always generate a SIGTTIN
* or SIGTTOU. The default signal handler will cause the process to stop
* thus avoiding the infinite loop problem. Presumably the job-control
* cognizant parent will fix things up before continuging its child process.
*/
int tty_signal(int sig, struct tty_struct *tty)
{
if (is_orphaned_pgrp(current->pgrp))
return -EIO; /* don't stop an orphaned pgrp */
(void) kill_pg(current->pgrp,sig,1);
if ((current->blocked & (1<<(sig-1))) ||
((int) current->sigaction[sig-1].sa_handler == 1))
return -EIO; /* Our signal will be ignored */
else if (current->sigaction[sig-1].sa_handler)
return -EINTR; /* We _will_ be interrupted :-) */
else
return -ERESTARTSYS; /* We _will_ be interrupted :-) */
/* (but restart after we continue) */
}
int tty_read(unsigned channel, char * buf, int nr)
{
struct tty_struct * tty;
struct tty_struct * other_tty = NULL;
char c, * b=buf;
int minimum,time;
if (channel > 255)
return -EIO;
tty = TTY_TABLE(channel);
if (!(tty->write_q || tty->read_q || tty->secondary))
return -EIO;
if ((current->tty == channel) && (tty->pgrp != current->pgrp))
return(tty_signal(SIGTTIN, tty));
if (channel & 0x80)
other_tty = tty_table + (channel ^ 0x40);
time = 10L*tty->termios.c_cc[VTIME];
minimum = tty->termios.c_cc[VMIN];
if (L_CANON(tty)) {
minimum = nr;
current->timeout = 0xffffffff;
time = 0;
} else if (minimum)
current->timeout = 0xffffffff;
else {
minimum = nr;
if (time)
current->timeout = time + jiffies;
time = 0;
}
if (minimum>nr)
minimum = nr;
while (nr>0) {
if (other_tty)
other_tty->write(other_tty);
cli();
if (EMPTY(tty->secondary) || (L_CANON(tty) &&
!FULL(tty->read_q) && !tty->secondary->data)) {
if (!current->timeout ||
(current->signal & ~current->blocked)) {
sti();
break;
}
if (IS_A_PTY_SLAVE(channel) && C_HUP(other_tty))
break;
interruptible_sleep_on(&tty->secondary->proc_list);
sti();
continue;
}
sti();
do {
GETCH(tty->secondary,c);
if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
c==EOF_CHAR(tty)) || c==10)
tty->secondary->data--;
if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
c==EOF_CHAR(tty)) && L_CANON(tty))
break;
else {
put_fs_byte(c,b++);
if (!--nr)
break;
}
if (c==10 && L_CANON(tty))
break;
} while (nr>0 && !EMPTY(tty->secondary));
wake_up(&tty->read_q->proc_list);
if (time)
current->timeout = time+jiffies;
if (L_CANON(tty) || b-buf >= minimum)
break;
}
current->timeout = 0;
if ((current->signal & ~current->blocked) && !(b-buf))
return -ERESTARTSYS;
return (b-buf);
}
int tty_write(unsigned channel, char * buf, int nr)
{
static cr_flag=0;
struct tty_struct * tty;
char c, *b=buf;
if (channel > 255)
return -EIO;
tty = TTY_TABLE(channel);
if (!(tty->write_q || tty->read_q || tty->secondary))
return -EIO;
if (L_TOSTOP(tty) &&
(current->tty == channel) && (tty->pgrp != current->pgrp))
return(tty_signal(SIGTTOU, tty));
while (nr>0) {
sleep_if_full(tty->write_q);
if (current->signal & ~current->blocked)
break;
while (nr>0 && !FULL(tty->write_q)) {
c=get_fs_byte(b);
if (O_POST(tty)) {
if (c=='\r' && O_CRNL(tty))
c='\n';
else if (c=='\n' && O_NLRET(tty))
c='\r';
if (c=='\n' && !cr_flag && O_NLCR(tty)) {
cr_flag = 1;
PUTCH(13,tty->write_q);
continue;
}
if (O_LCUC(tty))
c=toupper(c);
}
b++; nr--;
cr_flag = 0;
PUTCH(c,tty->write_q);
}
tty->write(tty);
if (nr>0)
schedule();
}
return (b-buf);
}
/*
* Jeh, sometimes I really like the 386.
* This routine is called from an interrupt,
* and there should be absolutely no problem
* with sleeping even in an interrupt (I hope).
* Of course, if somebody proves me wrong, I'll
* hate intel for all time :-). We'll have to
* be careful and see to reinstating the interrupt
* chips before calling this, though.
*
* I don't think we sleep here under normal circumstances
* anyway, which is good, as the task sleeping might be
* totally innocent.
*/
void do_tty_interrupt(int tty)
{
copy_to_cooked(TTY_TABLE(tty));
}
void chr_dev_init(void)
{
}
void tty_init(void)
{
int i;
for (i=0 ; i < QUEUES ; i++)
tty_queues[i] = (struct tty_queue) {0,0,0,0,""};
rs_queues[0] = (struct tty_queue) {0x3f8,0,0,0,""};
rs_queues[1] = (struct tty_queue) {0x3f8,0,0,0,""};
rs_queues[3] = (struct tty_queue) {0x2f8,0,0,0,""};
rs_queues[4] = (struct tty_queue) {0x2f8,0,0,0,""};
for (i=0 ; i<256 ; i++) {
tty_table[i] = (struct tty_struct) {
{0, 0, 0, 0, 0, INIT_C_CC},
0, 0, 0, NULL, NULL, NULL, NULL
};
}
con_init();
for (i = 0 ; i<NR_CONSOLES ; i++) {
con_table[i] = (struct tty_struct) {
{ICRNL, /* change incoming CR to NL */
OPOST|ONLCR, /* change outgoing NL to CRNL */
0,
IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
0, /* console termio */
INIT_C_CC},
0, /* initial pgrp */
0, /* initial session */
0, /* initial stopped */
con_write,
con_queues+0+i*3,con_queues+1+i*3,con_queues+2+i*3
};
}
for (i = 0 ; i<NR_SERIALS ; i++) {
rs_table[i] = (struct tty_struct) {
{0, /* no translation */
0, /* no translation */
B2400 | CS8,
0,
0,
INIT_C_CC},
0,
0,
0,
rs_write,
rs_queues+0+i*3,rs_queues+1+i*3,rs_queues+2+i*3
};
}
for (i = 0 ; i<NR_PTYS ; i++) {
mpty_table[i] = (struct tty_struct) {
{0, /* no translation */
0, /* no translation */
B9600 | CS8,
0,
0,
INIT_C_CC},
0,
0,
0,
mpty_write,
mpty_queues+0+i*3,mpty_queues+1+i*3,mpty_queues+2+i*3
};
spty_table[i] = (struct tty_struct) {
{0, /* no translation */
0, /* no translation */
B9600 | CS8,
IXON | ISIG | ICANON,
0,
INIT_C_CC},
0,
0,
0,
spty_write,
spty_queues+0+i*3,spty_queues+1+i*3,spty_queues+2+i*3
};
}
rs_init();
printk("%d virtual consoles\n\r",NR_CONSOLES);
printk("%d pty's\n\r",NR_PTYS);
}

View File

@@ -0,0 +1,251 @@
/*
* linux/kernel/chr_drv/tty_ioctl.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <termios.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
extern int session_of_pgrp(int pgrp);
extern int tty_signal(int sig, struct tty_struct *tty);
static unsigned short quotient[] = {
0, 2304, 1536, 1047, 857,
768, 576, 384, 192, 96,
64, 48, 24, 12, 6, 3
};
static void change_speed(struct tty_struct * tty)
{
unsigned short port,quot;
if (!(port = tty->read_q->data))
return;
quot = quotient[tty->termios.c_cflag & CBAUD];
cli();
outb_p(0x80,port+3); /* set DLAB */
outb_p(quot & 0xff,port); /* LS of divisor */
outb_p(quot >> 8,port+1); /* MS of divisor */
outb(0x03,port+3); /* reset DLAB */
sti();
}
static void flush(struct tty_queue * queue)
{
cli();
queue->head = queue->tail;
sti();
}
static void wait_until_sent(struct tty_struct * tty)
{
/* do nothing - not implemented */
}
static void send_break(struct tty_struct * tty)
{
/* do nothing - not implemented */
}
static int get_termios(struct tty_struct * tty, struct termios * termios)
{
int i;
verify_area(termios, sizeof (*termios));
for (i=0 ; i< (sizeof (*termios)) ; i++)
put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios );
return 0;
}
static int set_termios(struct tty_struct * tty, struct termios * termios,
int channel)
{
int i, retsig;
/* If we try to set the state of terminal and we're not in the
foreground, send a SIGTTOU. If the signal is blocked or
ignored, go ahead and perform the operation. POSIX 7.2) */
if ((current->tty == channel) && (tty->pgrp != current->pgrp)) {
retsig = tty_signal(SIGTTOU, tty);
if (retsig == -ERESTARTSYS || retsig == -EINTR)
return retsig;
}
for (i=0 ; i< (sizeof (*termios)) ; i++)
((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios);
change_speed(tty);
return 0;
}
static int get_termio(struct tty_struct * tty, struct termio * termio)
{
int i;
struct termio tmp_termio;
verify_area(termio, sizeof (*termio));
tmp_termio.c_iflag = tty->termios.c_iflag;
tmp_termio.c_oflag = tty->termios.c_oflag;
tmp_termio.c_cflag = tty->termios.c_cflag;
tmp_termio.c_lflag = tty->termios.c_lflag;
tmp_termio.c_line = tty->termios.c_line;
for(i=0 ; i < NCC ; i++)
tmp_termio.c_cc[i] = tty->termios.c_cc[i];
for (i=0 ; i< (sizeof (*termio)) ; i++)
put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio );
return 0;
}
/*
* This only works as the 386 is low-byt-first
*/
static int set_termio(struct tty_struct * tty, struct termio * termio,
int channel)
{
int i, retsig;
struct termio tmp_termio;
if ((current->tty == channel) && (tty->pgrp != current->pgrp)) {
retsig = tty_signal(SIGTTOU, tty);
if (retsig == -ERESTARTSYS || retsig == -EINTR)
return retsig;
}
for (i=0 ; i< (sizeof (*termio)) ; i++)
((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
*(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag;
*(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag;
*(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag;
*(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag;
tty->termios.c_line = tmp_termio.c_line;
for(i=0 ; i < NCC ; i++)
tty->termios.c_cc[i] = tmp_termio.c_cc[i];
change_speed(tty);
return 0;
}
int tty_ioctl(int dev, int cmd, int arg)
{
struct tty_struct * tty;
int pgrp;
if (MAJOR(dev) == 5) {
dev=current->tty;
if (dev<0)
panic("tty_ioctl: dev<0");
} else
dev=MINOR(dev);
tty = tty_table + (dev ? ((dev < 64)? dev-1:dev) : fg_console);
switch (cmd) {
case TCGETS:
return get_termios(tty,(struct termios *) arg);
case TCSETSF:
flush(tty->read_q); /* fallthrough */
case TCSETSW:
wait_until_sent(tty); /* fallthrough */
case TCSETS:
return set_termios(tty,(struct termios *) arg, dev);
case TCGETA:
return get_termio(tty,(struct termio *) arg);
case TCSETAF:
flush(tty->read_q); /* fallthrough */
case TCSETAW:
wait_until_sent(tty); /* fallthrough */
case TCSETA:
return set_termio(tty,(struct termio *) arg, dev);
case TCSBRK:
if (!arg) {
wait_until_sent(tty);
send_break(tty);
}
return 0;
case TCXONC:
switch (arg) {
case TCOOFF:
tty->stopped = 1;
tty->write(tty);
return 0;
case TCOON:
tty->stopped = 0;
tty->write(tty);
return 0;
case TCIOFF:
if (STOP_CHAR(tty))
PUTCH(STOP_CHAR(tty),tty->write_q);
return 0;
case TCION:
if (START_CHAR(tty))
PUTCH(START_CHAR(tty),tty->write_q);
return 0;
}
return -EINVAL; /* not implemented */
case TCFLSH:
if (arg==0)
flush(tty->read_q);
else if (arg==1)
flush(tty->write_q);
else if (arg==2) {
flush(tty->read_q);
flush(tty->write_q);
} else
return -EINVAL;
return 0;
case TIOCEXCL:
return -EINVAL; /* not implemented */
case TIOCNXCL:
return -EINVAL; /* not implemented */
case TIOCSCTTY:
return -EINVAL; /* set controlling term NI */
case TIOCGPGRP:
verify_area((void *) arg,4);
put_fs_long(tty->pgrp,(unsigned long *) arg);
return 0;
case TIOCSPGRP:
if ((current->tty < 0) ||
(current->tty != dev) ||
(tty->session != current->session))
return -ENOTTY;
pgrp=get_fs_long((unsigned long *) arg);
if (pgrp < 0)
return -EINVAL;
if (session_of_pgrp(pgrp) != current->session)
return -EPERM;
tty->pgrp = pgrp;
return 0;
case TIOCOUTQ:
verify_area((void *) arg,4);
put_fs_long(CHARS(tty->write_q),(unsigned long *) arg);
return 0;
case TIOCINQ:
verify_area((void *) arg,4);
put_fs_long(CHARS(tty->secondary),
(unsigned long *) arg);
return 0;
case TIOCSTI:
return -EINVAL; /* not implemented */
case TIOCGWINSZ:
return -EINVAL; /* not implemented */
case TIOCSWINSZ:
return -EINVAL; /* not implemented */
case TIOCMGET:
return -EINVAL; /* not implemented */
case TIOCMBIS:
return -EINVAL; /* not implemented */
case TIOCMBIC:
return -EINVAL; /* not implemented */
case TIOCMSET:
return -EINVAL; /* not implemented */
case TIOCGSOFTCAR:
return -EINVAL; /* not implemented */
case TIOCSSOFTCAR:
return -EINVAL; /* not implemented */
default:
return -EINVAL;
}
}

View File

@@ -0,0 +1,430 @@
/*
* linux/kernel/exit.c
*
* (C) 1991 Linus Torvalds
*/
#define DEBUG_PROC_TREE
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <asm/segment.h>
int sys_pause(void);
int sys_close(int fd);
void release(struct task_struct * p)
{
int i;
if (!p)
return;
if (p == current) {
printk("task releasing itself\n\r");
return;
}
for (i=1 ; i<NR_TASKS ; i++)
if (task[i]==p) {
task[i]=NULL;
/* Update links */
if (p->p_osptr)
p->p_osptr->p_ysptr = p->p_ysptr;
if (p->p_ysptr)
p->p_ysptr->p_osptr = p->p_osptr;
else
p->p_pptr->p_cptr = p->p_osptr;
free_page((long)p);
schedule();
return;
}
panic("trying to release non-existent task");
}
#ifdef DEBUG_PROC_TREE
/*
* Check to see if a task_struct pointer is present in the task[] array
* Return 0 if found, and 1 if not found.
*/
int bad_task_ptr(struct task_struct *p)
{
int i;
if (!p)
return 0;
for (i=0 ; i<NR_TASKS ; i++)
if (task[i] == p)
return 0;
return 1;
}
/*
* This routine scans the pid tree and make sure the rep invarient still
* holds. Used for debugging only, since it's very slow....
*
* It looks a lot scarier than it really is.... we're doing <20>nothing more
* than verifying the doubly-linked list found<6E>in p_ysptr and p_osptr,
* and checking it corresponds with the process tree defined by p_cptr and
* p_pptr;
*/
void audit_ptree()
{
int i;
for (i=1 ; i<NR_TASKS ; i++) {
if (!task[i])
continue;
if (bad_task_ptr(task[i]->p_pptr))
printk("Warning, pid %d's parent link is bad\n",
task[i]->pid);
if (bad_task_ptr(task[i]->p_cptr))
printk("Warning, pid %d's child link is bad\n",
task[i]->pid);
if (bad_task_ptr(task[i]->p_ysptr))
printk("Warning, pid %d's ys link is bad\n",
task[i]->pid);
if (bad_task_ptr(task[i]->p_osptr))
printk("Warning, pid %d's os link is bad\n",
task[i]->pid);
if (task[i]->p_pptr == task[i])
printk("Warning, pid %d parent link points to self\n");
if (task[i]->p_cptr == task[i])
printk("Warning, pid %d child link points to self\n");
if (task[i]->p_ysptr == task[i])
printk("Warning, pid %d ys link points to self\n");
if (task[i]->p_osptr == task[i])
printk("Warning, pid %d os link points to self\n");
if (task[i]->p_osptr) {
if (task[i]->p_pptr != task[i]->p_osptr->p_pptr)
printk(
"Warning, pid %d older sibling %d parent is %d\n",
task[i]->pid, task[i]->p_osptr->pid,
task[i]->p_osptr->p_pptr->pid);
if (task[i]->p_osptr->p_ysptr != task[i])
printk(
"Warning, pid %d older sibling %d has mismatched ys link\n",
task[i]->pid, task[i]->p_osptr->pid);
}
if (task[i]->p_ysptr) {
if (task[i]->p_pptr != task[i]->p_ysptr->p_pptr)
printk(
"Warning, pid %d younger sibling %d parent is %d\n",
task[i]->pid, task[i]->p_osptr->pid,
task[i]->p_osptr->p_pptr->pid);
if (task[i]->p_ysptr->p_osptr != task[i])
printk(
"Warning, pid %d younger sibling %d has mismatched os link\n",
task[i]->pid, task[i]->p_ysptr->pid);
}
if (task[i]->p_cptr) {
if (task[i]->p_cptr->p_pptr != task[i])
printk(
"Warning, pid %d youngest child %d has mismatched parent link\n",
task[i]->pid, task[i]->p_cptr->pid);
if (task[i]->p_cptr->p_ysptr)
printk(
"Warning, pid %d youngest child %d has non-NULL ys link\n",
task[i]->pid, task[i]->p_cptr->pid);
}
}
}
#endif /* DEBUG_PROC_TREE */
static inline int send_sig(long sig,struct task_struct * p,int priv)
{
if (!p)
return -EINVAL;
if (!priv && (current->euid!=p->euid) && !suser())
return -EPERM;
if ((sig == SIGKILL) || (sig == SIGCONT)) {
if (p->state == TASK_STOPPED)
p->state = TASK_RUNNING;
p->exit_code = 0;
p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) |
(1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) );
}
/* If the signal will be ignored, don't even post it */
if ((int) p->sigaction[sig-1].sa_handler == 1)
return 0;
/* Depends on order SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */
if ((sig >= SIGSTOP) && (sig <= SIGTTOU))
p->signal &= ~(1<<(SIGCONT-1));
/* Actually deliver the signal */
p->signal |= (1<<(sig-1));
return 0;
}
int session_of_pgrp(int pgrp)
{
struct task_struct **p;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if ((*p)->pgrp == pgrp)
return((*p)->session);
return -1;
}
int kill_pg(int pgrp, int sig, int priv)
{
struct task_struct **p;
int err,retval = -ESRCH;
int found = 0;
if (sig<1 || sig>32 || pgrp<=0)
return -EINVAL;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if ((*p)->pgrp == pgrp) {
if (sig && (err = send_sig(sig,*p,priv)))
retval = err;
else
found++;
}
return(found ? 0 : retval);
}
int kill_proc(int pid, int sig, int priv)
{
struct task_struct **p;
if (sig<1 || sig>32)
return -EINVAL;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if ((*p)->pid == pid)
return(sig ? send_sig(sig,*p,priv) : 0);
return(-ESRCH);
}
/*
* POSIX specifies that kill(-1,sig) is unspecified, but what we have
* is probably wrong. Should make it like BSD or SYSV.
*/
int sys_kill(int pid,int sig)
{
struct task_struct **p = NR_TASKS + task;
int err, retval = 0;
if (!pid)
return(kill_pg(current->pid,sig,0));
if (pid == -1) {
while (--p > &FIRST_TASK)
if (err = send_sig(sig,*p,0))
retval = err;
return(retval);
}
if (pid < 0)
return(kill_pg(-pid,sig,0));
/* Normal kill */
return(kill_proc(pid,sig,0));
}
/*
* Determine if a process group is "orphaned", according to the POSIX
* definition in 2.2.2.52. Orphaned process groups are not to be affected
* by terminal-generated stop signals. Newly orphaned process groups are
* to receive a SIGHUP and a SIGCONT.
*
* "I ask you, have you ever known what it is to be an orphan?"
*/
int is_orphaned_pgrp(int pgrp)
{
struct task_struct **p;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if (!(*p) ||
((*p)->pgrp != pgrp) ||
((*p)->state == TASK_ZOMBIE) ||
((*p)->p_pptr->pid == 1))
continue;
if (((*p)->p_pptr->pgrp != pgrp) &&
((*p)->p_pptr->session == (*p)->session))
return 0;
}
return(1); /* (sighing) "Often!" */
}
static int has_stopped_jobs(int pgrp)
{
struct task_struct ** p;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if ((*p)->pgrp != pgrp)
continue;
if ((*p)->state == TASK_STOPPED)
return(1);
}
return(0);
}
volatile void do_exit(long code)
{
struct task_struct *p;
int i;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
for (i=0 ; i<NR_OPEN ; i++)
if (current->filp[i])
sys_close(i);
iput(current->pwd);
current->pwd = NULL;
iput(current->root);
current->root = NULL;
iput(current->executable);
current->executable = NULL;
iput(current->library);
current->library = NULL;
current->state = TASK_ZOMBIE;
current->exit_code = code;
/*
* Check to see if any process groups have become orphaned
* as a result of our exiting, and if they have any stopped
* jobs, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2)
*
* Case i: Our father is in a different pgrp than we are
* and we were the only connection outside, so our pgrp
* is about to become orphaned.
*/
if ((current->p_pptr->pgrp != current->pgrp) &&
(current->p_pptr->session == current->session) &&
is_orphaned_pgrp(current->pgrp) &&
has_stopped_jobs(current->pgrp)) {
kill_pg(current->pgrp,SIGHUP,1);
kill_pg(current->pgrp,SIGCONT,1);
}
/* Let father know we died */
current->p_pptr->signal |= (1<<(SIGCHLD-1));
/*
* This loop does two things:
*
* A. Make init inherit all the child processes
* B. Check to see if any process groups have become orphaned
* as a result of our exiting, and if they have any stopped
* jons, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2)
*/
if (p = current->p_cptr) {
while (1) {
p->p_pptr = task[1];
if (p->state == TASK_ZOMBIE)
task[1]->signal |= (1<<(SIGCHLD-1));
/*
* process group orphan check
* Case ii: Our child is in a different pgrp
* than we are, and it was the only connection
* outside, so the child pgrp is now orphaned.
*/
if ((p->pgrp != current->pgrp) &&
(p->session == current->session) &&
is_orphaned_pgrp(p->pgrp) &&
has_stopped_jobs(p->pgrp)) {
kill_pg(p->pgrp,SIGHUP,1);
kill_pg(p->pgrp,SIGCONT,1);
}
if (p->p_osptr) {
p = p->p_osptr;
continue;
}
/*
* This is it; link everything into init's children
* and leave
*/
p->p_osptr = task[1]->p_cptr;
task[1]->p_cptr->p_ysptr = p;
task[1]->p_cptr = current->p_cptr;
current->p_cptr = 0;
break;
}
}
if (current->leader) {
struct task_struct **p;
struct tty_struct *tty;
if (current->tty >= 0) {
tty = TTY_TABLE(current->tty);
if (tty->pgrp>0)
kill_pg(tty->pgrp, SIGHUP, 1);
tty->pgrp = 0;
tty->session = 0;
}
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if ((*p)->session == current->session)
(*p)->tty = -1;
}
if (last_task_used_math == current)
last_task_used_math = NULL;
#ifdef DEBUG_PROC_TREE
audit_ptree();
#endif
schedule();
}
int sys_exit(int error_code)
{
do_exit((error_code&0xff)<<8);
}
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
int flag;
struct task_struct *p;
unsigned long oldblocked;
verify_area(stat_addr,4);
repeat:
flag=0;
for (p = current->p_cptr ; p ; p = p->p_osptr) {
if (pid>0) {
if (p->pid != pid)
continue;
} else if (!pid) {
if (p->pgrp != current->pgrp)
continue;
} else if (pid != -1) {
if (p->pgrp != -pid)
continue;
}
switch (p->state) {
case TASK_STOPPED:
if (!(options & WUNTRACED) ||
!p->exit_code)
continue;
put_fs_long((p->exit_code << 8) | 0x7f,
stat_addr);
p->exit_code = 0;
return p->pid;
case TASK_ZOMBIE:
current->cutime += p->utime;
current->cstime += p->stime;
flag = p->pid;
put_fs_long(p->exit_code, stat_addr);
release(p);
#ifdef DEBUG_PROC_TREE
audit_ptree();
#endif
return flag;
default:
flag=1;
continue;
}
}
if (flag) {
if (options & WNOHANG)
return 0;
current->state=TASK_INTERRUPTIBLE;
oldblocked = current->blocked;
current->blocked &= ~(1<<(SIGCHLD-1));
schedule();
current->blocked = oldblocked;
if (current->signal & ~(current->blocked | (1<<(SIGCHLD-1))))
return -ERESTARTSYS;
else
goto repeat;
}
return -ECHILD;
}

View File

@@ -0,0 +1,157 @@
/*
* linux/kernel/fork.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'fork.c' contains the help-routines for the 'fork' system call
* (see also system_call.s), and some misc functions ('verify_area').
* Fork is rather simple, once you get the hang of it, but the memory
* management can be a bitch. See 'mm/mm.c': 'copy_page_tables()'
*/
#include <errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <asm/system.h>
extern void write_verify(unsigned long address);
long last_pid=0;
void verify_area(void * addr,int size)
{
unsigned long start;
start = (unsigned long) addr;
size += start & 0xfff;
start &= 0xfffff000;
start += get_base(current->ldt[2]);
while (size>0) {
size -= 4096;
write_verify(start);
start += 4096;
}
}
int copy_mem(int nr,struct task_struct * p)
{
unsigned long old_data_base,new_data_base,data_limit;
unsigned long old_code_base,new_code_base,code_limit;
code_limit=get_limit(0x0f);
data_limit=get_limit(0x17);
old_code_base = get_base(current->ldt[1]);
old_data_base = get_base(current->ldt[2]);
if (old_data_base != old_code_base)
panic("We don't support separate I&D");
if (data_limit < code_limit)
panic("Bad data_limit");
new_data_base = new_code_base = nr * TASK_SIZE;
p->start_code = new_code_base;
set_base(p->ldt[1],new_code_base);
set_base(p->ldt[2],new_data_base);
if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
free_page_tables(new_data_base,data_limit);
return -ENOMEM;
}
return 0;
}
/*
* Ok, this is the main fork-routine. It copies the system process
* information (task[nr]) and sets up the necessary registers. It
* also copies the data segment in it's entirety.
*/
int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
long ebx,long ecx,long edx, long orig_eax,
long fs,long es,long ds,
long eip,long cs,long eflags,long esp,long ss)
{
struct task_struct *p;
int i;
struct file *f;
p = (struct task_struct *) get_free_page();
if (!p)
return -EAGAIN;
task[nr] = p;
*p = *current; /* NOTE! this doesn't copy the supervisor stack */
p->state = TASK_UNINTERRUPTIBLE;
p->pid = last_pid;
p->counter = p->priority;
p->signal = 0;
p->alarm = 0;
p->leader = 0; /* process leadership doesn't inherit */
p->utime = p->stime = 0;
p->cutime = p->cstime = 0;
p->start_time = jiffies;
p->tss.back_link = 0;
p->tss.esp0 = PAGE_SIZE + (long) p;
p->tss.ss0 = 0x10;
p->tss.eip = eip;
p->tss.eflags = eflags;
p->tss.eax = 0;
p->tss.ecx = ecx;
p->tss.edx = edx;
p->tss.ebx = ebx;
p->tss.esp = esp;
p->tss.ebp = ebp;
p->tss.esi = esi;
p->tss.edi = edi;
p->tss.es = es & 0xffff;
p->tss.cs = cs & 0xffff;
p->tss.ss = ss & 0xffff;
p->tss.ds = ds & 0xffff;
p->tss.fs = fs & 0xffff;
p->tss.gs = gs & 0xffff;
p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = 0x80000000;
if (last_task_used_math == current)
__asm__("clts ; fnsave %0 ; frstor %0"::"m" (p->tss.i387));
if (copy_mem(nr,p)) {
task[nr] = NULL;
free_page((long) p);
return -EAGAIN;
}
for (i=0; i<NR_OPEN;i++)
if (f=p->filp[i])
f->f_count++;
if (current->pwd)
current->pwd->i_count++;
if (current->root)
current->root->i_count++;
if (current->executable)
current->executable->i_count++;
if (current->library)
current->library->i_count++;
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->p_pptr = current;
p->p_cptr = 0;
p->p_ysptr = 0;
p->p_osptr = current->p_cptr;
if (p->p_osptr)
p->p_osptr->p_ysptr = p;
current->p_cptr = p;
p->state = TASK_RUNNING; /* do this last, just in case */
return last_pid;
}
int find_empty_process(void)
{
int i;
repeat:
if ((++last_pid)<0) last_pid=1;
for(i=0 ; i<NR_TASKS ; i++)
if (task[i] && ((task[i]->pid == last_pid) ||
(task[i]->pgrp == last_pid)))
goto repeat;
for(i=1 ; i<NR_TASKS ; i++)
if (!task[i])
return i;
return -EAGAIN;
}

View File

@@ -0,0 +1,83 @@
#
# Makefile for the FREAX-kernel character device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
AR =gar
AS =gas
LD =gld
LDFLAGS =-s -x
CC =gcc
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \
-finline-functions -mstring-insns -nostdinc -I../../include
CPP =gcc -E -nostdinc -I../../include
.c.s:
$(CC) $(CFLAGS) \
-S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-c -o $*.o $<
OBJS = math_emulate.o error.o convert.o ea.o get_put.o \
add.o mul.o div.o compare.o
math.a: $(OBJS)
$(AR) rcs math.a $(OBJS)
sync
clean:
rm -f core *.o *.a tmp_make
for i in *.c;do rm -f `basename $$i .c`.s;done
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
$(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile
### Dependencies:
add.s add.o : add.c ../../include/linux/math_emu.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h
compare.s compare.o : compare.c ../../include/linux/math_emu.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h
convert.s convert.o : convert.c ../../include/linux/math_emu.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h
div.s div.o : div.c ../../include/linux/math_emu.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h
ea.s ea.o : ea.c ../../include/stddef.h ../../include/linux/math_emu.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/asm/segment.h
error.s error.o : error.c ../../include/signal.h ../../include/sys/types.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/linux/mm.h \
../../include/linux/kernel.h
get_put.s get_put.o : get_put.c ../../include/signal.h ../../include/sys/types.h \
../../include/linux/math_emu.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/asm/segment.h
math_emulate.s math_emulate.o : math_emulate.c ../../include/signal.h \
../../include/sys/types.h ../../include/linux/math_emu.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/asm/segment.h
mul.s mul.o : mul.c ../../include/linux/math_emu.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h

View File

@@ -0,0 +1,92 @@
/*
* linux/kernel/math/add.c
*
* (C) 1991 Linus Torvalds
*/
/*
* temporary real addition routine.
*
* NOTE! These aren't exact: they are only 62 bits wide, and don't do
* correct rounding. Fast hack. The reason is that we shift right the
* values by two, in order not to have overflow (1 bit), and to be able
* to move the sign into the mantissa (1 bit). Much simpler algorithms,
* and 62 bits (61 really - no rounding) accuracy is usually enough. The
* only time you should notice anything weird is when adding 64-bit
* integers together. When using doubles (52 bits accuracy), the
* 61-bit accuracy never shows at all.
*/
#include <linux/math_emu.h>
#define NEGINT(a) \
__asm__("notl %0 ; notl %1 ; addl $1,%0 ; adcl $0,%1" \
:"=r" (a->a),"=r" (a->b) \
:"0" (a->a),"1" (a->b))
static void signify(temp_real * a)
{
a->exponent += 2;
__asm__("shrdl $2,%1,%0 ; shrl $2,%1"
:"=r" (a->a),"=r" (a->b)
:"0" (a->a),"1" (a->b));
if (a->exponent < 0)
NEGINT(a);
a->exponent &= 0x7fff;
}
static void unsignify(temp_real * a)
{
if (!(a->a || a->b)) {
a->exponent = 0;
return;
}
a->exponent &= 0x7fff;
if (a->b < 0) {
NEGINT(a);
a->exponent |= 0x8000;
}
while (a->b >= 0) {
a->exponent--;
__asm__("addl %0,%0 ; adcl %1,%1"
:"=r" (a->a),"=r" (a->b)
:"0" (a->a),"1" (a->b));
}
}
void fadd(const temp_real * src1, const temp_real * src2, temp_real * result)
{
temp_real a,b;
int x1,x2,shift;
x1 = src1->exponent & 0x7fff;
x2 = src2->exponent & 0x7fff;
if (x1 > x2) {
a = *src1;
b = *src2;
shift = x1-x2;
} else {
a = *src2;
b = *src1;
shift = x2-x1;
}
if (shift >= 64) {
*result = a;
return;
}
if (shift >= 32) {
b.a = b.b;
b.b = 0;
shift -= 32;
}
__asm__("shrdl %4,%1,%0 ; shrl %4,%1"
:"=r" (b.a),"=r" (b.b)
:"0" (b.a),"1" (b.b),"c" ((char) shift));
signify(&a);
signify(&b);
__asm__("addl %4,%0 ; adcl %5,%1"
:"=r" (a.a),"=r" (a.b)
:"0" (a.a),"1" (a.b),"g" (b.a),"g" (b.b));
unsignify(&a);
*result = a;
}

View File

@@ -0,0 +1,60 @@
/*
* linux/kernel/math/compare.c
*
* (C) 1991 Linus Torvalds
*/
/*
* temporary real comparison routines
*/
#include <linux/math_emu.h>
#define clear_Cx() (I387.swd &= ~0x4500)
static void normalize(temp_real * a)
{
int i = a->exponent & 0x7fff;
int sign = a->exponent & 0x8000;
if (!(a->a || a->b)) {
a->exponent = 0;
return;
}
while (i && a->b >= 0) {
i--;
__asm__("addl %0,%0 ; adcl %1,%1"
:"=r" (a->a),"=r" (a->b)
:"0" (a->a),"1" (a->b));
}
a->exponent = i | sign;
}
void ftst(const temp_real * a)
{
temp_real b;
clear_Cx();
b = *a;
normalize(&b);
if (b.a || b.b || b.exponent) {
if (b.exponent < 0)
set_C0();
} else
set_C3();
}
void fcom(const temp_real * src1, const temp_real * src2)
{
temp_real a;
a = *src1;
a.exponent ^= 0x8000;
fadd(&a,src2,&a);
ftst(&a);
}
void fucom(const temp_real * src1, const temp_real * src2)
{
fcom(src1,src2);
}

View File

@@ -0,0 +1,185 @@
/*
* linux/kernel/math/convert.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/math_emu.h>
/*
* NOTE!!! There is some "non-obvious" optimisations in the temp_to_long
* and temp_to_short conversion routines: don't touch them if you don't
* know what's going on. They are the adding of one in the rounding: the
* overflow bit is also used for adding one into the exponent. Thus it
* looks like the overflow would be incorrectly handled, but due to the
* way the IEEE numbers work, things are correct.
*
* There is no checking for total overflow in the conversions, though (ie
* if the temp-real number simply won't fit in a short- or long-real.)
*/
void short_to_temp(const short_real * a, temp_real * b)
{
if (!(*a & 0x7fffffff)) {
b->a = b->b = 0;
if (*a)
b->exponent = 0x8000;
else
b->exponent = 0;
return;
}
b->exponent = ((*a>>23) & 0xff)-127+16383;
if (*a<0)
b->exponent |= 0x8000;
b->b = (*a<<8) | 0x80000000;
b->a = 0;
}
void long_to_temp(const long_real * a, temp_real * b)
{
if (!a->a && !(a->b & 0x7fffffff)) {
b->a = b->b = 0;
if (a->b)
b->exponent = 0x8000;
else
b->exponent = 0;
return;
}
b->exponent = ((a->b >> 20) & 0x7ff)-1023+16383;
if (a->b<0)
b->exponent |= 0x8000;
b->b = 0x80000000 | (a->b<<11) | (((unsigned long)a->a)>>21);
b->a = a->a<<11;
}
void temp_to_short(const temp_real * a, short_real * b)
{
if (!(a->exponent & 0x7fff)) {
*b = (a->exponent)?0x80000000:0;
return;
}
*b = ((((long) a->exponent)-16383+127) << 23) & 0x7f800000;
if (a->exponent < 0)
*b |= 0x80000000;
*b |= (a->b >> 8) & 0x007fffff;
switch (ROUNDING) {
case ROUND_NEAREST:
if ((a->b & 0xff) > 0x80)
++*b;
break;
case ROUND_DOWN:
if ((a->exponent & 0x8000) && (a->b & 0xff))
++*b;
break;
case ROUND_UP:
if (!(a->exponent & 0x8000) && (a->b & 0xff))
++*b;
break;
}
}
void temp_to_long(const temp_real * a, long_real * b)
{
if (!(a->exponent & 0x7fff)) {
b->a = 0;
b->b = (a->exponent)?0x80000000:0;
return;
}
b->b = (((0x7fff & (long) a->exponent)-16383+1023) << 20) & 0x7ff00000;
if (a->exponent < 0)
b->b |= 0x80000000;
b->b |= (a->b >> 11) & 0x000fffff;
b->a = a->b << 21;
b->a |= (a->a >> 11) & 0x001fffff;
switch (ROUNDING) {
case ROUND_NEAREST:
if ((a->a & 0x7ff) > 0x400)
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
case ROUND_DOWN:
if ((a->exponent & 0x8000) && (a->b & 0xff))
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
case ROUND_UP:
if (!(a->exponent & 0x8000) && (a->b & 0xff))
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
}
}
void real_to_int(const temp_real * a, temp_int * b)
{
int shift = 16383 + 63 - (a->exponent & 0x7fff);
unsigned long underflow;
b->a = b->b = underflow = 0;
b->sign = (a->exponent < 0);
if (shift < 0) {
set_OE();
return;
}
if (shift < 32) {
b->b = a->b; b->a = a->a;
} else if (shift < 64) {
b->a = a->b; underflow = a->a;
shift -= 32;
} else if (shift < 96) {
underflow = a->b;
shift -= 64;
} else
return;
__asm__("shrdl %2,%1,%0"
:"=r" (underflow),"=r" (b->a)
:"c" ((char) shift),"0" (underflow),"1" (b->a));
__asm__("shrdl %2,%1,%0"
:"=r" (b->a),"=r" (b->b)
:"c" ((char) shift),"0" (b->a),"1" (b->b));
__asm__("shrl %1,%0"
:"=r" (b->b)
:"c" ((char) shift),"0" (b->b));
switch (ROUNDING) {
case ROUND_NEAREST:
__asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b)
,"r" (0x7fffffff + (b->a & 1))
,"m" (*&underflow));
break;
case ROUND_UP:
if (!b->sign && underflow)
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
case ROUND_DOWN:
if (b->sign && underflow)
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
}
}
void int_to_real(const temp_int * a, temp_real * b)
{
b->a = a->a;
b->b = a->b;
if (b->a || b->b)
b->exponent = 16383 + 63 + (a->sign? 0x8000:0);
else {
b->exponent = 0;
return;
}
while (b->b >= 0) {
b->exponent--;
__asm__("addl %0,%0 ; adcl %1,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
}
}

View File

@@ -0,0 +1,109 @@
/*
* linux/kernel/math/div.c
*
* (C) 1991 Linus Torvalds
*/
/*
* temporary real division routine.
*/
#include <linux/math_emu.h>
static void shift_left(int * c)
{
__asm__ __volatile__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
"movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
"movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
"movl 12(%0),%%eax ; adcl %%eax,12(%0)"
::"r" ((long) c):"ax");
}
static void shift_right(int * c)
{
__asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)"
::"r" ((long) c));
}
static int try_sub(int * a, int * b)
{
char ok;
__asm__ __volatile__("movl (%1),%%eax ; subl %%eax,(%2)\n\t"
"movl 4(%1),%%eax ; sbbl %%eax,4(%2)\n\t"
"movl 8(%1),%%eax ; sbbl %%eax,8(%2)\n\t"
"movl 12(%1),%%eax ; sbbl %%eax,12(%2)\n\t"
"setae %%al":"=a" (ok):"c" ((long) a),"d" ((long) b));
return ok;
}
static void div64(int * a, int * b, int * c)
{
int tmp[4];
int i;
unsigned int mask = 0;
c += 4;
for (i = 0 ; i<64 ; i++) {
if (!(mask >>= 1)) {
c--;
mask = 0x80000000;
}
tmp[0] = a[0]; tmp[1] = a[1];
tmp[2] = a[2]; tmp[3] = a[3];
if (try_sub(b,tmp)) {
*c |= mask;
a[0] = tmp[0]; a[1] = tmp[1];
a[2] = tmp[2]; a[3] = tmp[3];
}
shift_right(b);
}
}
void fdiv(const temp_real * src1, const temp_real * src2, temp_real * result)
{
int i,sign;
int a[4],b[4],tmp[4] = {0,0,0,0};
sign = (src1->exponent ^ src2->exponent) & 0x8000;
if (!(src2->a || src2->b)) {
set_ZE();
return;
}
i = (src1->exponent & 0x7fff) - (src2->exponent & 0x7fff) + 16383;
if (i<0) {
set_UE();
result->exponent = sign;
result->a = result->b = 0;
return;
}
a[0] = a[1] = 0;
a[2] = src1->a;
a[3] = src1->b;
b[0] = b[1] = 0;
b[2] = src2->a;
b[3] = src2->b;
while (b[3] >= 0) {
i++;
shift_left(b);
}
div64(a,b,tmp);
if (tmp[0] || tmp[1] || tmp[2] || tmp[3]) {
while (i && tmp[3] >= 0) {
i--;
shift_left(tmp);
}
if (tmp[3] >= 0)
set_DE();
} else
i = 0;
if (i>0x7fff) {
set_OE();
return;
}
if (tmp[0] || tmp[1])
set_PE();
result->exponent = i | sign;
result->a = tmp[2];
result->b = tmp[3];
}

View File

@@ -0,0 +1,92 @@
/*
* linux/kernel/math/ea.c
*
* (C) 1991 Linus Torvalds
*/
/*
* Calculate the effective address.
*/
#include <stddef.h>
#include <linux/math_emu.h>
#include <asm/segment.h>
static int __regoffset[] = {
offsetof(struct info,___eax),
offsetof(struct info,___ecx),
offsetof(struct info,___edx),
offsetof(struct info,___ebx),
offsetof(struct info,___esp),
offsetof(struct info,___ebp),
offsetof(struct info,___esi),
offsetof(struct info,___edi)
};
#define REG(x) (*(long *)(__regoffset[(x)]+(char *) info))
static char * sib(struct info * info, int mod)
{
unsigned char ss,index,base;
long offset = 0;
base = get_fs_byte((char *) EIP);
EIP++;
ss = base >> 6;
index = (base >> 3) & 7;
base &= 7;
if (index == 4)
offset = 0;
else
offset = REG(index);
offset <<= ss;
if (mod || base != 5)
offset += REG(base);
if (mod == 1) {
offset += (signed char) get_fs_byte((char *) EIP);
EIP++;
} else if (mod == 2 || base == 5) {
offset += (signed) get_fs_long((unsigned long *) EIP);
EIP += 4;
}
I387.foo = offset;
I387.fos = 0x17;
return (char *) offset;
}
char * ea(struct info * info, unsigned short code)
{
unsigned char mod,rm;
long * tmp = &EAX;
int offset = 0;
mod = (code >> 6) & 3;
rm = code & 7;
if (rm == 4 && mod != 3)
return sib(info,mod);
if (rm == 5 && !mod) {
offset = get_fs_long((unsigned long *) EIP);
EIP += 4;
I387.foo = offset;
I387.fos = 0x17;
return (char *) offset;
}
tmp = & REG(rm);
switch (mod) {
case 0: offset = 0; break;
case 1:
offset = (signed char) get_fs_byte((char *) EIP);
EIP++;
break;
case 2:
offset = (signed) get_fs_long((unsigned long *) EIP);
EIP += 4;
break;
case 3:
math_abort(info,1<<(SIGILL-1));
}
I387.foo = offset;
I387.fos = 0x17;
return offset + (char *) *tmp;
}

View File

@@ -0,0 +1,16 @@
/*
* linux/kernel/math/error.c
*
* (C) 1991 Linus Torvalds
*/
#include <signal.h>
#include <linux/sched.h>
void math_error(void)
{
__asm__("fnclex");
if (last_task_used_math)
last_task_used_math->signal |= 1<<(SIGFPE-1);
}

View File

@@ -0,0 +1,240 @@
/*
* linux/kernel/math/get_put.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This file handles all accesses to user memory: getting and putting
* ints/reals/BCD etc. This is the only part that concerns itself with
* other than temporary real format. All other cals are strictly temp_real.
*/
#include <signal.h>
#include <linux/math_emu.h>
#include <linux/kernel.h>
#include <asm/segment.h>
void get_short_real(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
short_real sr;
addr = ea(info,code);
sr = get_fs_long((unsigned long *) addr);
short_to_temp(&sr,tmp);
}
void get_long_real(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
long_real lr;
addr = ea(info,code);
lr.a = get_fs_long((unsigned long *) addr);
lr.b = get_fs_long(1 + (unsigned long *) addr);
long_to_temp(&lr,tmp);
}
void get_temp_real(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
addr = ea(info,code);
tmp->a = get_fs_long((unsigned long *) addr);
tmp->b = get_fs_long(1 + (unsigned long *) addr);
tmp->exponent = get_fs_word(4 + (unsigned short *) addr);
}
void get_short_int(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
ti.a = (signed short) get_fs_word((unsigned short *) addr);
ti.b = 0;
if (ti.sign = (ti.a < 0))
ti.a = - ti.a;
int_to_real(&ti,tmp);
}
void get_long_int(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
ti.a = get_fs_long((unsigned long *) addr);
ti.b = 0;
if (ti.sign = (ti.a < 0))
ti.a = - ti.a;
int_to_real(&ti,tmp);
}
void get_longlong_int(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
ti.a = get_fs_long((unsigned long *) addr);
ti.b = get_fs_long(1 + (unsigned long *) addr);
if (ti.sign = (ti.b < 0))
__asm__("notl %0 ; notl %1\n\t"
"addl $1,%0 ; adcl $0,%1"
:"=r" (ti.a),"=r" (ti.b)
:"0" (ti.a),"1" (ti.b));
int_to_real(&ti,tmp);
}
#define MUL10(low,high) \
__asm__("addl %0,%0 ; adcl %1,%1\n\t" \
"movl %0,%%ecx ; movl %1,%%ebx\n\t" \
"addl %0,%0 ; adcl %1,%1\n\t" \
"addl %0,%0 ; adcl %1,%1\n\t" \
"addl %%ecx,%0 ; adcl %%ebx,%1" \
:"=a" (low),"=d" (high) \
:"0" (low),"1" (high):"cx","bx")
#define ADD64(val,low,high) \
__asm__("addl %4,%0 ; adcl $0,%1":"=r" (low),"=r" (high) \
:"0" (low),"1" (high),"r" ((unsigned long) (val)))
void get_BCD(temp_real * tmp, struct info * info, unsigned short code)
{
int k;
char * addr;
temp_int i;
unsigned char c;
addr = ea(info,code);
addr += 9;
i.sign = 0x80 & get_fs_byte(addr--);
i.a = i.b = 0;
for (k = 0; k < 9; k++) {
c = get_fs_byte(addr--);
MUL10(i.a, i.b);
ADD64((c>>4), i.a, i.b);
MUL10(i.a, i.b);
ADD64((c&0xf), i.a, i.b);
}
int_to_real(&i,tmp);
}
void put_short_real(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
short_real sr;
addr = ea(info,code);
verify_area(addr,4);
temp_to_short(tmp,&sr);
put_fs_long(sr,(unsigned long *) addr);
}
void put_long_real(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
long_real lr;
addr = ea(info,code);
verify_area(addr,8);
temp_to_long(tmp,&lr);
put_fs_long(lr.a, (unsigned long *) addr);
put_fs_long(lr.b, 1 + (unsigned long *) addr);
}
void put_temp_real(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
addr = ea(info,code);
verify_area(addr,10);
put_fs_long(tmp->a, (unsigned long *) addr);
put_fs_long(tmp->b, 1 + (unsigned long *) addr);
put_fs_word(tmp->exponent, 4 + (short *) addr);
}
void put_short_int(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
real_to_int(tmp,&ti);
verify_area(addr,2);
if (ti.sign)
ti.a = -ti.a;
put_fs_word(ti.a,(short *) addr);
}
void put_long_int(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
real_to_int(tmp,&ti);
verify_area(addr,4);
if (ti.sign)
ti.a = -ti.a;
put_fs_long(ti.a,(unsigned long *) addr);
}
void put_longlong_int(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
real_to_int(tmp,&ti);
verify_area(addr,8);
if (ti.sign)
__asm__("notl %0 ; notl %1\n\t"
"addl $1,%0 ; adcl $0,%1"
:"=r" (ti.a),"=r" (ti.b)
:"0" (ti.a),"1" (ti.b));
put_fs_long(ti.a,(unsigned long *) addr);
put_fs_long(ti.b,1 + (unsigned long *) addr);
}
#define DIV10(low,high,rem) \
__asm__("divl %6 ; xchgl %1,%2 ; divl %6" \
:"=d" (rem),"=a" (low),"=b" (high) \
:"0" (0),"1" (high),"2" (low),"c" (10))
void put_BCD(const temp_real * tmp,struct info * info, unsigned short code)
{
int k,rem;
char * addr;
temp_int i;
unsigned char c;
addr = ea(info,code);
verify_area(addr,10);
real_to_int(tmp,&i);
if (i.sign)
put_fs_byte(0x80, addr+9);
else
put_fs_byte(0, addr+9);
for (k = 0; k < 9; k++) {
DIV10(i.a,i.b,rem);
c = rem;
DIV10(i.a,i.b,rem);
c += rem<<4;
put_fs_byte(c,addr++);
}
}

View File

@@ -0,0 +1,529 @@
/*
* linux/kernel/math/math_emulate.c
*
* (C) 1991 Linus Torvalds
*/
/*
* Limited emulation 27.12.91 - mostly loads/stores, which gcc wants
* even for soft-float, unless you use bruce evans' patches. The patches
* are great, but they have to be re-applied for every version, and the
* library is different for soft-float and 80387. So emulation is more
* practical, even though it's slower.
*
* 28.12.91 - loads/stores work, even BCD. I'll have to start thinking
* about add/sub/mul/div. Urgel. I should find some good source, but I'll
* just fake up something.
*
* 30.12.91 - add/sub/mul/div/com seem to work mostly. I should really
* test every possible combination.
*/
/*
* This file is full of ugly macros etc: one problem was that gcc simply
* didn't want to make the structures as they should be: it has to try to
* align them. Sickening code, but at least I've hidden the ugly things
* in this one file: the other files don't need to know about these things.
*
* The other files also don't care about ST(x) etc - they just get addresses
* to 80-bit temporary reals, and do with them as they please. I wanted to
* hide most of the 387-specific things here.
*/
#include <signal.h>
#define __ALIGNED_TEMP_REAL 1
#include <linux/math_emu.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
#define ST(x) (*__st((x)))
#define PST(x) ((const temp_real *) __st((x)))
/*
* We don't want these inlined - it gets too messy in the machine-code.
*/
static void fpop(void);
static void fpush(void);
static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b);
static temp_real_unaligned * __st(int i);
static void do_emu(struct info * info)
{
unsigned short code;
temp_real tmp;
char * address;
if (I387.cwd & I387.swd & 0x3f)
I387.swd |= 0x8000;
else
I387.swd &= 0x7fff;
ORIG_EIP = EIP;
/* 0x0007 means user code space */
if (CS != 0x000F) {
printk("math_emulate: %04x:%08x\n\r",CS,EIP);
panic("Math emulation needed in kernel");
}
code = get_fs_word((unsigned short *) EIP);
bswapw(code);
code &= 0x7ff;
I387.fip = EIP;
*(unsigned short *) &I387.fcs = CS;
*(1+(unsigned short *) &I387.fcs) = code;
EIP += 2;
switch (code) {
case 0x1d0: /* fnop */
return;
case 0x1d1: case 0x1d2: case 0x1d3:
case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
math_abort(info,1<<(SIGILL-1));
case 0x1e0:
ST(0).exponent ^= 0x8000;
return;
case 0x1e1:
ST(0).exponent &= 0x7fff;
return;
case 0x1e2: case 0x1e3:
math_abort(info,1<<(SIGILL-1));
case 0x1e4:
ftst(PST(0));
return;
case 0x1e5:
printk("fxam not implemented\n\r");
math_abort(info,1<<(SIGILL-1));
case 0x1e6: case 0x1e7:
math_abort(info,1<<(SIGILL-1));
case 0x1e8:
fpush();
ST(0) = CONST1;
return;
case 0x1e9:
fpush();
ST(0) = CONSTL2T;
return;
case 0x1ea:
fpush();
ST(0) = CONSTL2E;
return;
case 0x1eb:
fpush();
ST(0) = CONSTPI;
return;
case 0x1ec:
fpush();
ST(0) = CONSTLG2;
return;
case 0x1ed:
fpush();
ST(0) = CONSTLN2;
return;
case 0x1ee:
fpush();
ST(0) = CONSTZ;
return;
case 0x1ef:
math_abort(info,1<<(SIGILL-1));
case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3:
case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7:
case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb:
case 0x1fc: case 0x1fd: case 0x1fe: case 0x1ff:
printk("%04x fxxx not implemented\n\r",code + 0xc800);
math_abort(info,1<<(SIGILL-1));
case 0x2e9:
fucom(PST(1),PST(0));
fpop(); fpop();
return;
case 0x3d0: case 0x3d1:
return;
case 0x3e2:
I387.swd &= 0x7f00;
return;
case 0x3e3:
I387.cwd = 0x037f;
I387.swd = 0x0000;
I387.twd = 0x0000;
return;
case 0x3e4:
return;
case 0x6d9:
fcom(PST(1),PST(0));
fpop(); fpop();
return;
case 0x7e0:
*(short *) &EAX = I387.swd;
return;
}
switch (code >> 3) {
case 0x18:
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x19:
fmul(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1a:
fcom(PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1b:
fcom(PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
fpop();
return;
case 0x1c:
real_to_real(&ST(code & 7),&tmp);
tmp.exponent ^= 0x8000;
fadd(PST(0),&tmp,&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1d:
ST(0).exponent ^= 0x8000;
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1e:
fdiv(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1f:
fdiv(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x38:
fpush();
ST(0) = ST((code & 7)+1);
return;
case 0x39:
fxchg(&ST(0),&ST(code & 7));
return;
case 0x3b:
ST(code & 7) = ST(0);
fpop();
return;
case 0x98:
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x99:
fmul(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x9a:
fcom(PST(code & 7),PST(0));
return;
case 0x9b:
fcom(PST(code & 7),PST(0));
fpop();
return;
case 0x9c:
ST(code & 7).exponent ^= 0x8000;
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x9d:
real_to_real(&ST(0),&tmp);
tmp.exponent ^= 0x8000;
fadd(PST(code & 7),&tmp,&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x9e:
fdiv(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x9f:
fdiv(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0xb8:
printk("ffree not implemented\n\r");
math_abort(info,1<<(SIGILL-1));
case 0xb9:
fxchg(&ST(0),&ST(code & 7));
return;
case 0xba:
ST(code & 7) = ST(0);
return;
case 0xbb:
ST(code & 7) = ST(0);
fpop();
return;
case 0xbc:
fucom(PST(code & 7),PST(0));
return;
case 0xbd:
fucom(PST(code & 7),PST(0));
fpop();
return;
case 0xd8:
fadd(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xd9:
fmul(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xda:
fcom(PST(code & 7),PST(0));
fpop();
return;
case 0xdc:
ST(code & 7).exponent ^= 0x8000;
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xdd:
real_to_real(&ST(0),&tmp);
tmp.exponent ^= 0x8000;
fadd(PST(code & 7),&tmp,&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xde:
fdiv(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xdf:
fdiv(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xf8:
printk("ffree not implemented\n\r");
math_abort(info,1<<(SIGILL-1));
fpop();
return;
case 0xf9:
fxchg(&ST(0),&ST(code & 7));
return;
case 0xfa:
case 0xfb:
ST(code & 7) = ST(0);
fpop();
return;
}
switch ((code>>3) & 0xe7) {
case 0x22:
put_short_real(PST(0),info,code);
return;
case 0x23:
put_short_real(PST(0),info,code);
fpop();
return;
case 0x24:
address = ea(info,code);
for (code = 0 ; code < 7 ; code++) {
((long *) & I387)[code] =
get_fs_long((unsigned long *) address);
address += 4;
}
return;
case 0x25:
address = ea(info,code);
*(unsigned short *) &I387.cwd =
get_fs_word((unsigned short *) address);
return;
case 0x26:
address = ea(info,code);
verify_area(address,28);
for (code = 0 ; code < 7 ; code++) {
put_fs_long( ((long *) & I387)[code],
(unsigned long *) address);
address += 4;
}
return;
case 0x27:
address = ea(info,code);
verify_area(address,2);
put_fs_word(I387.cwd,(short *) address);
return;
case 0x62:
put_long_int(PST(0),info,code);
return;
case 0x63:
put_long_int(PST(0),info,code);
fpop();
return;
case 0x65:
fpush();
get_temp_real(&tmp,info,code);
real_to_real(&tmp,&ST(0));
return;
case 0x67:
put_temp_real(PST(0),info,code);
fpop();
return;
case 0xa2:
put_long_real(PST(0),info,code);
return;
case 0xa3:
put_long_real(PST(0),info,code);
fpop();
return;
case 0xa4:
address = ea(info,code);
for (code = 0 ; code < 27 ; code++) {
((long *) & I387)[code] =
get_fs_long((unsigned long *) address);
address += 4;
}
return;
case 0xa6:
address = ea(info,code);
verify_area(address,108);
for (code = 0 ; code < 27 ; code++) {
put_fs_long( ((long *) & I387)[code],
(unsigned long *) address);
address += 4;
}
I387.cwd = 0x037f;
I387.swd = 0x0000;
I387.twd = 0x0000;
return;
case 0xa7:
address = ea(info,code);
verify_area(address,2);
put_fs_word(I387.swd,(short *) address);
return;
case 0xe2:
put_short_int(PST(0),info,code);
return;
case 0xe3:
put_short_int(PST(0),info,code);
fpop();
return;
case 0xe4:
fpush();
get_BCD(&tmp,info,code);
real_to_real(&tmp,&ST(0));
return;
case 0xe5:
fpush();
get_longlong_int(&tmp,info,code);
real_to_real(&tmp,&ST(0));
return;
case 0xe6:
put_BCD(PST(0),info,code);
fpop();
return;
case 0xe7:
put_longlong_int(PST(0),info,code);
fpop();
return;
}
switch (code >> 9) {
case 0:
get_short_real(&tmp,info,code);
break;
case 1:
get_long_int(&tmp,info,code);
break;
case 2:
get_long_real(&tmp,info,code);
break;
case 4:
get_short_int(&tmp,info,code);
}
switch ((code>>3) & 0x27) {
case 0:
fadd(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 1:
fmul(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 2:
fcom(&tmp,PST(0));
return;
case 3:
fcom(&tmp,PST(0));
fpop();
return;
case 4:
tmp.exponent ^= 0x8000;
fadd(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 5:
ST(0).exponent ^= 0x8000;
fadd(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 6:
fdiv(PST(0),&tmp,&tmp);
real_to_real(&tmp,&ST(0));
return;
case 7:
fdiv(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
}
if ((code & 0x138) == 0x100) {
fpush();
real_to_real(&tmp,&ST(0));
return;
}
printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code);
math_abort(info,1<<(SIGFPE-1));
}
void math_emulate(long ___false)
{
if (!current->used_math) {
current->used_math = 1;
I387.cwd = 0x037f;
I387.swd = 0x0000;
I387.twd = 0x0000;
}
/* &___false points to info->___orig_eip, so subtract 1 to get info */
do_emu((struct info *) ((&___false) - 1));
}
void __math_abort(struct info * info, unsigned int signal)
{
EIP = ORIG_EIP;
current->signal |= signal;
__asm__("movl %0,%%esp ; ret"::"g" ((long) info));
}
static void fpop(void)
{
unsigned long tmp;
tmp = I387.swd & 0xffffc7ff;
I387.swd += 0x00000800;
I387.swd &= 0x00003800;
I387.swd |= tmp;
}
static void fpush(void)
{
unsigned long tmp;
tmp = I387.swd & 0xffffc7ff;
I387.swd += 0x00003800;
I387.swd &= 0x00003800;
I387.swd |= tmp;
}
static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b)
{
temp_real_unaligned c;
c = *a;
*a = *b;
*b = c;
}
static temp_real_unaligned * __st(int i)
{
i += I387.swd >> 11;
i &= 7;
return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space));
}

View File

@@ -0,0 +1,73 @@
/*
* linux/kernel/math/mul.c
*
* (C) 1991 Linus Torvalds
*/
/*
* temporary real multiplication routine.
*/
#include <linux/math_emu.h>
static void shift(int * c)
{
__asm__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
"movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
"movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
"movl 12(%0),%%eax ; adcl %%eax,12(%0)"
::"r" ((long) c):"ax");
}
static void mul64(const temp_real * a, const temp_real * b, int * c)
{
__asm__("movl (%0),%%eax\n\t"
"mull (%1)\n\t"
"movl %%eax,(%2)\n\t"
"movl %%edx,4(%2)\n\t"
"movl 4(%0),%%eax\n\t"
"mull 4(%1)\n\t"
"movl %%eax,8(%2)\n\t"
"movl %%edx,12(%2)\n\t"
"movl (%0),%%eax\n\t"
"mull 4(%1)\n\t"
"addl %%eax,4(%2)\n\t"
"adcl %%edx,8(%2)\n\t"
"adcl $0,12(%2)\n\t"
"movl 4(%0),%%eax\n\t"
"mull (%1)\n\t"
"addl %%eax,4(%2)\n\t"
"adcl %%edx,8(%2)\n\t"
"adcl $0,12(%2)"
::"b" ((long) a),"c" ((long) b),"D" ((long) c)
:"ax","dx");
}
void fmul(const temp_real * src1, const temp_real * src2, temp_real * result)
{
int i,sign;
int tmp[4] = {0,0,0,0};
sign = (src1->exponent ^ src2->exponent) & 0x8000;
i = (src1->exponent & 0x7fff) + (src2->exponent & 0x7fff) - 16383 + 1;
if (i<0) {
result->exponent = sign;
result->a = result->b = 0;
return;
}
if (i>0x7fff) {
set_OE();
return;
}
mul64(src1,src2,tmp);
if (tmp[0] || tmp[1] || tmp[2] || tmp[3])
while (i && tmp[3] >= 0) {
i--;
shift(tmp);
}
else
i = 0;
result->exponent = i | sign;
result->a = tmp[2];
result->b = tmp[3];
}

View File

@@ -0,0 +1,58 @@
/*
* linux/kernel/mktime.c
*
* (C) 1991 Linus Torvalds
*/
#include <time.h>
/*
* This isn't the library routine, it is only used in the kernel.
* as such, we don't care about years<1970 etc, but assume everything
* is ok. Similarly, TZ etc is happily ignored. We just do everything
* as easily as possible. Let's find something public for the library
* routines (although I think minix times is public).
*/
/*
* PS. I hate whoever though up the year 1970 - couldn't they have gotten
* a leap-year instead? I also hate Gregorius, pope or no. I'm grumpy.
*/
#define MINUTE 60
#define HOUR (60*MINUTE)
#define DAY (24*HOUR)
#define YEAR (365*DAY)
/* interestingly, we assume leap-years */
static int month[12] = {
0,
DAY*(31),
DAY*(31+29),
DAY*(31+29+31),
DAY*(31+29+31+30),
DAY*(31+29+31+30+31),
DAY*(31+29+31+30+31+30),
DAY*(31+29+31+30+31+30+31),
DAY*(31+29+31+30+31+30+31+31),
DAY*(31+29+31+30+31+30+31+31+30),
DAY*(31+29+31+30+31+30+31+31+30+31),
DAY*(31+29+31+30+31+30+31+31+30+31+30)
};
long kernel_mktime(struct tm * tm)
{
long res;
int year;
year = tm->tm_year - 70;
/* magic offsets (y+1) needed to get leapyears right.*/
res = YEAR*year + DAY*((year+1)/4);
res += month[tm->tm_mon];
/* and (y+2) here. If it wasn't a leap-year, we have to adjust */
if (tm->tm_mon>1 && ((year+2)%4))
res -= DAY;
res += DAY*(tm->tm_mday-1);
res += HOUR*tm->tm_hour;
res += MINUTE*tm->tm_min;
res += tm->tm_sec;
return res;
}

View File

@@ -0,0 +1,24 @@
/*
* linux/kernel/panic.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This function is used through-out the kernel (includeinh mm and fs)
* to indicate a major problem.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
void sys_sync(void); /* it's really int */
volatile void panic(const char * s)
{
printk("Kernel panic: %s\n\r",s);
if (current == task[0])
printk("In swapper task - not syncing\n\r");
else
sys_sync();
for(;;);
}

View File

@@ -0,0 +1,31 @@
/*
* linux/kernel/printk.c
*
* (C) 1991 Linus Torvalds
*/
/*
* When in kernel-mode, we cannot use printf, as fs is liable to
* point to 'interesting' things. Make a printf with fs-saving, and
* all is well.
*/
#include <stdarg.h>
#include <stddef.h>
#include <linux/kernel.h>
static char buf[1024];
extern int vsprintf(char * buf, const char * fmt, va_list args);
int printk(const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
console_print(buf);
return i;
}

View File

@@ -0,0 +1,444 @@
/*
* linux/kernel/sched.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'sched.c' is the main kernel file. It contains scheduling primitives
* (sleep_on, wakeup, schedule etc) as well as a number of simple system
* call functions (type getpid(), which just extracts a field from
* current-task
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/sys.h>
#include <linux/fdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <signal.h>
#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
void show_task(int nr,struct task_struct * p)
{
int i,j = 4096-sizeof(struct task_struct);
printk("%d: pid=%d, state=%d, father=%d, child=%d, ",nr,p->pid,
p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1);
i=0;
while (i<j && !((char *)(p+1))[i])
i++;
printk("%d/%d chars free in kstack\n\r",i,j);
printk(" PC=%08X.", *(1019 + (unsigned long *) p));
if (p->p_ysptr || p->p_osptr)
printk(" Younger sib=%d, older sib=%d\n\r",
p->p_ysptr ? p->p_ysptr->pid : -1,
p->p_osptr ? p->p_osptr->pid : -1);
else
printk("\n\r");
}
void show_state(void)
{
int i;
printk("\rTask-info:\n\r");
for (i=0;i<NR_TASKS;i++)
if (task[i])
show_task(i,task[i]);
}
#define LATCH (1193180/HZ)
extern void mem_use(void);
extern int timer_interrupt(void);
extern int system_call(void);
union task_union {
struct task_struct task;
char stack[PAGE_SIZE];
};
static union task_union init_task = {INIT_TASK,};
unsigned long volatile jiffies=0;
unsigned long startup_time=0;
int jiffies_offset = 0; /* # clock ticks to add to get "true
time". Should always be less than
1 second's worth. For time fanatics
who like to syncronize their machines
to WWV :-) */
struct task_struct *current = &(init_task.task);
struct task_struct *last_task_used_math = NULL;
struct task_struct * task[NR_TASKS] = {&(init_task.task), };
long user_stack [ PAGE_SIZE>>2 ] ;
struct {
long * a;
short b;
} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*/
void math_state_restore()
{
if (last_task_used_math == current)
return;
__asm__("fwait");
if (last_task_used_math) {
__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
}
last_task_used_math=current;
if (current->used_math) {
__asm__("frstor %0"::"m" (current->tss.i387));
} else {
__asm__("fninit"::);
current->used_math=1;
}
}
/*
* 'schedule()' is the scheduler function. This is GOOD CODE! There
* probably won't be any reason to change this, as it should work well
* in all circumstances (ie gives IO-bound processes good response etc).
* The one thing you might take a look at is the signal-handler code here.
*
* NOTE!! Task 0 is the 'idle' task, which gets called when no other
* tasks can run. It can not be killed, and it cannot sleep. The 'state'
* information in task[0] is never used.
*/
void schedule(void)
{
int i,next,c;
struct task_struct ** p;
/* check alarm, wake up any interruptible tasks that have got a signal */
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p) {
if ((*p)->timeout && (*p)->timeout < jiffies) {
(*p)->timeout = 0;
if ((*p)->state == TASK_INTERRUPTIBLE)
(*p)->state = TASK_RUNNING;
}
if ((*p)->alarm && (*p)->alarm < jiffies) {
(*p)->signal |= (1<<(SIGALRM-1));
(*p)->alarm = 0;
}
if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING;
}
/* this is the scheduler proper: */
while (1) {
c = -1;
next = 0;
i = NR_TASKS;
p = &task[NR_TASKS];
while (--i) {
if (!*--p)
continue;
if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
c = (*p)->counter, next = i;
}
if (c) break;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p)
(*p)->counter = ((*p)->counter >> 1) +
(*p)->priority;
}
switch_to(next);
}
int sys_pause(void)
{
current->state = TASK_INTERRUPTIBLE;
schedule();
return 0;
}
static inline void __sleep_on(struct task_struct **p, int state)
{
struct task_struct *tmp;
if (!p)
return;
if (current == &(init_task.task))
panic("task[0] trying to sleep");
tmp = *p;
*p = current;
current->state = state;
repeat: schedule();
if (*p && *p != current) {
(**p).state = 0;
current->state = TASK_UNINTERRUPTIBLE;
goto repeat;
}
if (!*p)
printk("Warning: *P = NULL\n\r");
if (*p = tmp)
tmp->state=0;
}
void interruptible_sleep_on(struct task_struct **p)
{
__sleep_on(p,TASK_INTERRUPTIBLE);
}
void sleep_on(struct task_struct **p)
{
__sleep_on(p,TASK_UNINTERRUPTIBLE);
}
void wake_up(struct task_struct **p)
{
if (p && *p) {
if ((**p).state == TASK_STOPPED)
printk("wake_up: TASK_STOPPED");
if ((**p).state == TASK_ZOMBIE)
printk("wake_up: TASK_ZOMBIE");
(**p).state=0;
}
}
/*
* OK, here are some floppy things that shouldn't be in the kernel
* proper. They are here because the floppy needs a timer, and this
* was the easiest way of doing it.
*/
static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL};
static int mon_timer[4]={0,0,0,0};
static int moff_timer[4]={0,0,0,0};
unsigned char current_DOR = 0x0C;
int ticks_to_floppy_on(unsigned int nr)
{
extern unsigned char selected;
unsigned char mask = 0x10 << nr;
if (nr>3)
panic("floppy_on: nr>3");
moff_timer[nr]=10000; /* 100 s = very big :-) */
cli(); /* use floppy_off to turn it off */
mask |= current_DOR;
if (!selected) {
mask &= 0xFC;
mask |= nr;
}
if (mask != current_DOR) {
outb(mask,FD_DOR);
if ((mask ^ current_DOR) & 0xf0)
mon_timer[nr] = HZ/2;
else if (mon_timer[nr] < 2)
mon_timer[nr] = 2;
current_DOR = mask;
}
sti();
return mon_timer[nr];
}
void floppy_on(unsigned int nr)
{
cli();
while (ticks_to_floppy_on(nr))
sleep_on(nr+wait_motor);
sti();
}
void floppy_off(unsigned int nr)
{
moff_timer[nr]=3*HZ;
}
void do_floppy_timer(void)
{
int i;
unsigned char mask = 0x10;
for (i=0 ; i<4 ; i++,mask <<= 1) {
if (!(mask & current_DOR))
continue;
if (mon_timer[i]) {
if (!--mon_timer[i])
wake_up(i+wait_motor);
} else if (!moff_timer[i]) {
current_DOR &= ~mask;
outb(current_DOR,FD_DOR);
} else
moff_timer[i]--;
}
}
#define TIME_REQUESTS 64
static struct timer_list {
long jiffies;
void (*fn)();
struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;
void add_timer(long jiffies, void (*fn)(void))
{
struct timer_list * p;
if (!fn)
return;
cli();
if (jiffies <= 0)
(fn)();
else {
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
if (!p->fn)
break;
if (p >= timer_list + TIME_REQUESTS)
panic("No more time requests free");
p->fn = fn;
p->jiffies = jiffies;
p->next = next_timer;
next_timer = p;
while (p->next && p->next->jiffies < p->jiffies) {
p->jiffies -= p->next->jiffies;
fn = p->fn;
p->fn = p->next->fn;
p->next->fn = fn;
jiffies = p->jiffies;
p->jiffies = p->next->jiffies;
p->next->jiffies = jiffies;
p = p->next;
}
}
sti();
}
void do_timer(long cpl)
{
static int blanked = 0;
if (blankcount || !blankinterval) {
if (blanked)
unblank_screen();
if (blankcount)
blankcount--;
blanked = 0;
} else if (!blanked) {
blank_screen();
blanked = 1;
}
if (hd_timeout)
if (!--hd_timeout)
hd_times_out();
if (beepcount)
if (!--beepcount)
sysbeepstop();
if (cpl)
current->utime++;
else
current->stime++;
if (next_timer) {
next_timer->jiffies--;
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();
}
}
if (current_DOR & 0xf0)
do_floppy_timer();
if ((--current->counter)>0) return;
current->counter=0;
if (!cpl) return;
schedule();
}
int sys_alarm(long seconds)
{
int old = current->alarm;
if (old)
old = (old - jiffies) / HZ;
current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
return (old);
}
int sys_getpid(void)
{
return current->pid;
}
int sys_getppid(void)
{
return current->p_pptr->pid;
}
int sys_getuid(void)
{
return current->uid;
}
int sys_geteuid(void)
{
return current->euid;
}
int sys_getgid(void)
{
return current->gid;
}
int sys_getegid(void)
{
return current->egid;
}
int sys_nice(long increment)
{
if (current->priority-increment>0)
current->priority -= increment;
return 0;
}
void sched_init(void)
{
int i;
struct desc_struct * p;
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
p = gdt+2+FIRST_TSS_ENTRY;
for(i=1;i<NR_TASKS;i++) {
task[i] = NULL;
p->a=p->b=0;
p++;
p->a=p->b=0;
p++;
}
/* Clear NT, so that we won't have troubles with that later on */
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
ltr(0);
lldt(0);
outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
set_intr_gate(0x20,&timer_interrupt);
outb(inb_p(0x21)&~0x01,0x21);
set_system_gate(0x80,&system_call);
}

View File

@@ -0,0 +1,209 @@
/*
* linux/kernel/signal.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <signal.h>
#include <errno.h>
int sys_sgetmask()
{
return current->blocked;
}
int sys_ssetmask(int newmask)
{
int old=current->blocked;
current->blocked = newmask & ~(1<<(SIGKILL-1)) & ~(1<<(SIGSTOP-1));
return old;
}
int sys_sigpending(sigset_t *set)
{
/* fill in "set" with signals pending but blocked. */
verify_area(set,4);
put_fs_long(current->blocked & current->signal, (unsigned long *)set);
return 0;
}
/* atomically swap in the new signal mask, and wait for a signal.
*
* we need to play some games with syscall restarting. We get help
* from the syscall library interface. Note that we need to coordinate
* the calling convention with the libc routine.
*
* "set" is just the sigmask as described in 1003.1-1988, 3.3.7.
* It is assumed that sigset_t can be passed as a 32 bit quantity.
*
* "restart" holds a restart indication. If it's non-zero, then we
* install the old mask, and return normally. If it's zero, we store
* the current mask in old_mask and block until a signal comes in.
*/
int sys_sigsuspend(int restart, unsigned long old_mask, unsigned long set)
{
extern int sys_pause(void);
if (restart) {
/* we're restarting */
current->blocked = old_mask;
return -EINTR;
}
/* we're not restarting. do the work */
*(&restart) = 1;
*(&old_mask) = current->blocked;
current->blocked = set;
(void) sys_pause(); /* return after a signal arrives */
return -ERESTARTNOINTR; /* handle the signal, and come back */
}
static inline void save_old(char * from,char * to)
{
int i;
verify_area(to, sizeof(struct sigaction));
for (i=0 ; i< sizeof(struct sigaction) ; i++) {
put_fs_byte(*from,to);
from++;
to++;
}
}
static inline void get_new(char * from,char * to)
{
int i;
for (i=0 ; i< sizeof(struct sigaction) ; i++)
*(to++) = get_fs_byte(from++);
}
int sys_signal(int signum, long handler, long restorer)
{
struct sigaction tmp;
if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
return -EINVAL;
tmp.sa_handler = (void (*)(int)) handler;
tmp.sa_mask = 0;
tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
tmp.sa_restorer = (void (*)(void)) restorer;
handler = (long) current->sigaction[signum-1].sa_handler;
current->sigaction[signum-1] = tmp;
return handler;
}
int sys_sigaction(int signum, const struct sigaction * action,
struct sigaction * oldaction)
{
struct sigaction tmp;
if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
return -EINVAL;
tmp = current->sigaction[signum-1];
get_new((char *) action,
(char *) (signum-1+current->sigaction));
if (oldaction)
save_old((char *) &tmp,(char *) oldaction);
if (current->sigaction[signum-1].sa_flags & SA_NOMASK)
current->sigaction[signum-1].sa_mask = 0;
else
current->sigaction[signum-1].sa_mask |= (1<<(signum-1));
return 0;
}
/*
* Routine writes a core dump image in the current directory.
* Currently not implemented.
*/
int core_dump(long signr)
{
return(0); /* We didn't do a dump */
}
int do_signal(long signr,long eax,long ebx, long ecx, long edx, long orig_eax,
long fs, long es, long ds,
long eip, long cs, long eflags,
unsigned long * esp, long ss)
{
unsigned long sa_handler;
long old_eip=eip;
struct sigaction * sa = current->sigaction + signr - 1;
int longs;
unsigned long * tmp_esp;
#ifdef notdef
printk("pid: %d, signr: %x, eax=%d, oeax = %d, int=%d\n",
current->pid, signr, eax, orig_eax,
sa->sa_flags & SA_INTERRUPT);
#endif
if ((orig_eax != -1) &&
((eax == -ERESTARTSYS) || (eax == -ERESTARTNOINTR))) {
if ((eax == -ERESTARTSYS) && ((sa->sa_flags & SA_INTERRUPT) ||
signr < SIGCONT || signr > SIGTTOU))
*(&eax) = -EINTR;
else {
*(&eax) = orig_eax;
*(&eip) = old_eip -= 2;
}
}
sa_handler = (unsigned long) sa->sa_handler;
if (sa_handler==1)
return(1); /* Ignore, see if there are more signals... */
if (!sa_handler) {
switch (signr) {
case SIGCONT:
case SIGCHLD:
return(1); /* Ignore, ... */
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
current->state = TASK_STOPPED;
current->exit_code = signr;
if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags &
SA_NOCLDSTOP))
current->p_pptr->signal |= (1<<(SIGCHLD-1));
return(1); /* Reschedule another event */
case SIGQUIT:
case SIGILL:
case SIGTRAP:
case SIGIOT:
case SIGFPE:
case SIGSEGV:
if (core_dump(signr))
do_exit(signr|0x80);
/* fall through */
default:
do_exit(signr);
}
}
/*
* OK, we're invoking a handler
*/
if (sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
*(&eip) = sa_handler;
longs = (sa->sa_flags & SA_NOMASK)?7:8;
*(&esp) -= longs;
verify_area(esp,longs*4);
tmp_esp=esp;
put_fs_long((long) sa->sa_restorer,tmp_esp++);
put_fs_long(signr,tmp_esp++);
if (!(sa->sa_flags & SA_NOMASK))
put_fs_long(current->blocked,tmp_esp++);
put_fs_long(eax,tmp_esp++);
put_fs_long(ecx,tmp_esp++);
put_fs_long(edx,tmp_esp++);
put_fs_long(eflags,tmp_esp++);
put_fs_long(old_eip,tmp_esp++);
current->blocked |= sa->sa_mask;
return(0); /* Continue, execute handler */
}

View File

@@ -0,0 +1,522 @@
/*
* linux/kernel/sys.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kernel.h>
#include <linux/config.h>
#include <asm/segment.h>
#include <sys/times.h>
#include <sys/utsname.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <string.h>
/*
* The timezone where the local system is located. Used as a default by some
* programs who obtain this value by using gettimeofday.
*/
struct timezone sys_tz = { 0, 0};
extern int session_of_pgrp(int pgrp);
int sys_ftime()
{
return -ENOSYS;
}
int sys_break()
{
return -ENOSYS;
}
int sys_ptrace()
{
return -ENOSYS;
}
int sys_stty()
{
return -ENOSYS;
}
int sys_gtty()
{
return -ENOSYS;
}
int sys_rename()
{
return -ENOSYS;
}
int sys_prof()
{
return -ENOSYS;
}
/*
* This is done BSD-style, with no consideration of the saved gid, except
* that if you set the effective gid, it sets the saved gid too. This
* makes it possible for a setgid program to completely drop its privileges,
* which is often a useful assertion to make when you are doing a security
* audit over a program.
*
* The general idea is that a program which uses just setregid() will be
* 100% compatible with BSD. A program which uses just setgid() will be
* 100% compatible with POSIX w/ Saved ID's.
*/
int sys_setregid(int rgid, int egid)
{
if (rgid>0) {
if ((current->gid == rgid) ||
suser())
current->gid = rgid;
else
return(-EPERM);
}
if (egid>0) {
if ((current->gid == egid) ||
(current->egid == egid) ||
suser()) {
current->egid = egid;
current->sgid = egid;
} else
return(-EPERM);
}
return 0;
}
/*
* setgid() is implemeneted like SysV w/ SAVED_IDS
*/
int sys_setgid(int gid)
{
if (suser())
current->gid = current->egid = current->sgid = gid;
else if ((gid == current->gid) || (gid == current->sgid))
current->egid = gid;
else
return -EPERM;
return 0;
}
int sys_acct()
{
return -ENOSYS;
}
int sys_phys()
{
return -ENOSYS;
}
int sys_lock()
{
return -ENOSYS;
}
int sys_mpx()
{
return -ENOSYS;
}
int sys_ulimit()
{
return -ENOSYS;
}
int sys_time(long * tloc)
{
int i;
i = CURRENT_TIME;
if (tloc) {
verify_area(tloc,4);
put_fs_long(i,(unsigned long *)tloc);
}
return i;
}
/*
* Unprivileged users may change the real user id to the effective uid
* or vice versa. (BSD-style)
*
* When you set the effective uid, it sets the saved uid too. This
* makes it possible for a setuid program to completely drop its privileges,
* which is often a useful assertion to make when you are doing a security
* audit over a program.
*
* The general idea is that a program which uses just setreuid() will be
* 100% compatible with BSD. A program which uses just setuid() will be
* 100% compatible with POSIX w/ Saved ID's.
*/
int sys_setreuid(int ruid, int euid)
{
int old_ruid = current->uid;
if (ruid>0) {
if ((current->euid==ruid) ||
(old_ruid == ruid) ||
suser())
current->uid = ruid;
else
return(-EPERM);
}
if (euid>0) {
if ((old_ruid == euid) ||
(current->euid == euid) ||
suser()) {
current->euid = euid;
current->suid = euid;
} else {
current->uid = old_ruid;
return(-EPERM);
}
}
return 0;
}
/*
* setuid() is implemeneted like SysV w/ SAVED_IDS
*
* Note that SAVED_ID's is deficient in that a setuid root program
* like sendmail, for example, cannot set its uid to be a normal
* user and then switch back, because if you're root, setuid() sets
* the saved uid too. If you don't like this, blame the bright people
* in the POSIX commmittee and/or USG. Note that the BSD-style setreuid()
* will allow a root program to temporarily drop privileges and be able to
* regain them by swapping the real and effective uid.
*/
int sys_setuid(int uid)
{
if (suser())
current->uid = current->euid = current->suid = uid;
else if ((uid == current->uid) || (uid == current->suid))
current->euid = uid;
else
return -EPERM;
return(0);
}
int sys_stime(long * tptr)
{
if (!suser())
return -EPERM;
startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ;
jiffies_offset = 0;
return 0;
}
int sys_times(struct tms * tbuf)
{
if (tbuf) {
verify_area(tbuf,sizeof *tbuf);
put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime);
put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime);
put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime);
put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime);
}
return jiffies;
}
int sys_brk(unsigned long end_data_seg)
{
if (end_data_seg >= current->end_code &&
end_data_seg < current->start_stack - 16384)
current->brk = end_data_seg;
return current->brk;
}
/*
* This needs some heave checking ...
* I just haven't get the stomach for it. I also don't fully
* understand sessions/pgrp etc. Let somebody who does explain it.
*
* OK, I think I have the protection semantics right.... this is really
* only important on a multi-user system anyway, to make sure one user
* can't send a signal to a process owned by another. -TYT, 12/12/91
*/
int sys_setpgid(int pid, int pgid)
{
int i;
if (!pid)
pid = current->pid;
if (!pgid)
pgid = current->pid;
if (pgid < 0)
return -EINVAL;
for (i=0 ; i<NR_TASKS ; i++)
if (task[i] && (task[i]->pid == pid) &&
((task[i]->p_pptr == current) ||
(task[i] == current))) {
if (task[i]->leader)
return -EPERM;
if ((task[i]->session != current->session) ||
((pgid != pid) &&
(session_of_pgrp(pgid) != current->session)))
return -EPERM;
task[i]->pgrp = pgid;
return 0;
}
return -ESRCH;
}
int sys_getpgrp(void)
{
return current->pgrp;
}
int sys_setsid(void)
{
if (current->leader && !suser())
return -EPERM;
current->leader = 1;
current->session = current->pgrp = current->pid;
current->tty = -1;
return current->pgrp;
}
/*
* Supplementary group ID's
*/
int sys_getgroups(int gidsetsize, gid_t *grouplist)
{
int i;
if (gidsetsize)
verify_area(grouplist, sizeof(gid_t) * gidsetsize);
for (i = 0; (i < NGROUPS) && (current->groups[i] != NOGROUP);
i++, grouplist++) {
if (gidsetsize) {
if (i >= gidsetsize)
return -EINVAL;
put_fs_word(current->groups[i], (short *) grouplist);
}
}
return(i);
}
int sys_setgroups(int gidsetsize, gid_t *grouplist)
{
int i;
if (!suser())
return -EPERM;
if (gidsetsize > NGROUPS)
return -EINVAL;
for (i = 0; i < gidsetsize; i++, grouplist++) {
current->groups[i] = get_fs_word((unsigned short *) grouplist);
}
if (i < NGROUPS)
current->groups[i] = NOGROUP;
return 0;
}
int in_group_p(gid_t grp)
{
int i;
if (grp == current->egid)
return 1;
for (i = 0; i < NGROUPS; i++) {
if (current->groups[i] == NOGROUP)
break;
if (current->groups[i] == grp)
return 1;
}
return 0;
}
static struct utsname thisname = {
UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION, UTS_MACHINE
};
int sys_uname(struct utsname * name)
{
int i;
if (!name) return -ERROR;
verify_area(name,sizeof *name);
for(i=0;i<sizeof *name;i++)
put_fs_byte(((char *) &thisname)[i],i+(char *) name);
return 0;
}
/*
* Only sethostname; gethostname can be implemented by calling uname()
*/
int sys_sethostname(char *name, int len)
{
int i;
if (!suser())
return -EPERM;
if (len > MAXHOSTNAMELEN)
return -EINVAL;
for (i=0; i < len; i++) {
if ((thisname.nodename[i] = get_fs_byte(name+i)) == 0)
break;
}
if (thisname.nodename[i]) {
thisname.nodename[i>MAXHOSTNAMELEN ? MAXHOSTNAMELEN : i] = 0;
}
return 0;
}
int sys_getrlimit(int resource, struct rlimit *rlim)
{
if (resource >= RLIM_NLIMITS)
return -EINVAL;
verify_area(rlim,sizeof *rlim);
put_fs_long(current->rlim[resource].rlim_cur,
(unsigned long *) rlim);
put_fs_long(current->rlim[resource].rlim_max,
((unsigned long *) rlim)+1);
return 0;
}
int sys_setrlimit(int resource, struct rlimit *rlim)
{
struct rlimit new, *old;
if (resource >= RLIM_NLIMITS)
return -EINVAL;
old = current->rlim + resource;
new.rlim_cur = get_fs_long((unsigned long *) rlim);
new.rlim_max = get_fs_long(((unsigned long *) rlim)+1);
if (((new.rlim_cur > old->rlim_max) ||
(new.rlim_max > old->rlim_max)) &&
!suser())
return -EPERM;
*old = new;
return 0;
}
/*
* It would make sense to put struct rusuage in the task_struct,
* except that would make the task_struct be *really big*. After
* task_struct gets moved into malloc'ed memory, it would
* make sense to do this. It will make moving the rest of the information
* a lot simpler! (Which we're not doing right now because we're not
* measuring them yet).
*/
int sys_getrusage(int who, struct rusage *ru)
{
struct rusage r;
unsigned long *lp, *lpend, *dest;
if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN)
return -EINVAL;
verify_area(ru, sizeof *ru);
memset((char *) &r, 0, sizeof(r));
if (who == RUSAGE_SELF) {
r.ru_utime.tv_sec = CT_TO_SECS(current->utime);
r.ru_utime.tv_usec = CT_TO_USECS(current->utime);
r.ru_stime.tv_sec = CT_TO_SECS(current->stime);
r.ru_stime.tv_usec = CT_TO_USECS(current->stime);
} else {
r.ru_utime.tv_sec = CT_TO_SECS(current->cutime);
r.ru_utime.tv_usec = CT_TO_USECS(current->cutime);
r.ru_stime.tv_sec = CT_TO_SECS(current->cstime);
r.ru_stime.tv_usec = CT_TO_USECS(current->cstime);
}
lp = (unsigned long *) &r;
lpend = (unsigned long *) (&r+1);
dest = (unsigned long *) ru;
for (; lp < lpend; lp++, dest++)
put_fs_long(*lp, dest);
return(0);
}
int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
{
if (tv) {
verify_area(tv, sizeof *tv);
put_fs_long(startup_time + CT_TO_SECS(jiffies+jiffies_offset),
(unsigned long *) tv);
put_fs_long(CT_TO_USECS(jiffies+jiffies_offset),
((unsigned long *) tv)+1);
}
if (tz) {
verify_area(tz, sizeof *tz);
put_fs_long(sys_tz.tz_minuteswest, (unsigned long *) tz);
put_fs_long(sys_tz.tz_dsttime, ((unsigned long *) tz)+1);
}
return 0;
}
/*
* The first time we set the timezone, we will warp the clock so that
* it is ticking GMT time instead of local time. Presumably,
* if someone is setting the timezone then we are running in an
* environment where the programs understand about timezones.
* This should be done at boot time in the /etc/rc script, as
* soon as possible, so that the clock can be set right. Otherwise,
* various programs will get confused when the clock gets warped.
*/
int sys_settimeofday(struct timeval *tv, struct timezone *tz)
{
static int firsttime = 1;
void adjust_clock();
if (!suser())
return -EPERM;
if (tz) {
sys_tz.tz_minuteswest = get_fs_long((unsigned long *) tz);
sys_tz.tz_dsttime = get_fs_long(((unsigned long *) tz)+1);
if (firsttime) {
firsttime = 0;
if (!tv)
adjust_clock();
}
}
if (tv) {
int sec, usec;
sec = get_fs_long((unsigned long *)tv);
usec = get_fs_long(((unsigned long *)tv)+1);
startup_time = sec - jiffies/HZ;
jiffies_offset = usec * HZ / 1000000 - jiffies%HZ;
}
return 0;
}
/*
* Adjust the time obtained from the CMOS to be GMT time instead of
* local time.
*
* This is ugly, but preferable to the alternatives. Otherwise we
* would either need to write a program to do it in /etc/rc (and risk
* confusion if the program gets run more than once; it would also be
* hard to make the program warp the clock precisely n hours) or
* compile in the timezone information into the kernel. Bad, bad....
*
* XXX Currently does not adjust for daylight savings time. May not
* need to do anything, depending on how smart (dumb?) the BIOS
* is. Blast it all.... the best thing to do not depend on the CMOS
* clock at all, but get the time via NTP or timed if you're on a
* network.... - TYT, 1/1/92
*/
void adjust_clock()
{
startup_time += sys_tz.tz_minuteswest*60;
}
int sys_umask(int mask)
{
int old = current->umask;
current->umask = mask & 0777;
return (old);
}

View File

@@ -0,0 +1,300 @@
/*
* linux/kernel/system_call.s
*
* (C) 1991 Linus Torvalds
*/
/*
* system_call.s contains the system-call low-level handling routines.
* This also contains the timer-interrupt handler, as some of the code is
* the same. The hd- and flopppy-interrupts are also here.
*
* NOTE: This code handles signal-recognition, which happens every time
* after a timer-interrupt and after each system call. Ordinary interrupts
* don't handle signal-recognition, as that would clutter them up totally
* unnecessarily.
*
* Stack layout in 'ret_from_system_call':
*
* 0(%esp) - %eax
* 4(%esp) - %ebx
* 8(%esp) - %ecx
* C(%esp) - %edx
* 10(%esp) - original %eax (-1 if not system call)
* 14(%esp) - %fs
* 18(%esp) - %es
* 1C(%esp) - %ds
* 20(%esp) - %eip
* 24(%esp) - %cs
* 28(%esp) - %eflags
* 2C(%esp) - %oldesp
* 30(%esp) - %oldss
*/
SIG_CHLD = 17
EAX = 0x00
EBX = 0x04
ECX = 0x08
EDX = 0x0C
ORIG_EAX = 0x10
FS = 0x14
ES = 0x18
DS = 0x1C
EIP = 0x20
CS = 0x24
EFLAGS = 0x28
OLDESP = 0x2C
OLDSS = 0x30
state = 0 # these are offsets into the task-struct.
counter = 4
priority = 8
signal = 12
sigaction = 16 # MUST be 16 (=len of sigaction)
blocked = (33*16)
# offsets within sigaction
sa_handler = 0
sa_mask = 4
sa_flags = 8
sa_restorer = 12
nr_system_calls = 82
ENOSYS = 38
/*
* Ok, I get parallel printer interrupts while using the floppy for some
* strange reason. Urgel. Now I just ignore them.
*/
.globl _system_call,_sys_fork,_timer_interrupt,_sys_execve
.globl _hd_interrupt,_floppy_interrupt,_parallel_interrupt
.globl _device_not_available, _coprocessor_error
.align 2
bad_sys_call:
pushl $-ENOSYS
jmp ret_from_sys_call
.align 2
reschedule:
pushl $ret_from_sys_call
jmp _schedule
.align 2
_system_call:
push %ds
push %es
push %fs
pushl %eax # save the orig_eax
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
movl $0x10,%edx # set up ds,es to kernel space
mov %dx,%ds
mov %dx,%es
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
cmpl _NR_syscalls,%eax
jae bad_sys_call
call _sys_call_table(,%eax,4)
pushl %eax
2:
movl _current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
ret_from_sys_call:
movl _current,%eax
cmpl _task,%eax # task[0] cannot have signals
je 3f
cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
jne 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
jne 3f
movl signal(%eax),%ebx
movl blocked(%eax),%ecx
notl %ecx
andl %ebx,%ecx
bsfl %ecx,%ecx
je 3f
btrl %ecx,%ebx
movl %ebx,signal(%eax)
incl %ecx
pushl %ecx
call _do_signal
popl %ecx
testl %eax, %eax
jne 2b # see if we need to switch tasks, or do more signals
3: popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4, %esp # skip orig_eax
pop %fs
pop %es
pop %ds
iret
.align 2
_coprocessor_error:
push %ds
push %es
push %fs
pushl $-1 # fill in -1 for orig_eax
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
pushl $ret_from_sys_call
jmp _math_error
.align 2
_device_not_available:
push %ds
push %es
push %fs
pushl $-1 # fill in -1 for orig_eax
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
pushl $ret_from_sys_call
clts # clear TS so that we can use math
movl %cr0,%eax
testl $0x4,%eax # EM (math emulation bit)
je _math_state_restore
pushl %ebp
pushl %esi
pushl %edi
pushl $0 # temporary storage for ORIG_EIP
call _math_emulate
addl $4,%esp
popl %edi
popl %esi
popl %ebp
ret
.align 2
_timer_interrupt:
push %ds # save ds,es and put kernel data space
push %es # into them. %fs is used by _system_call
push %fs
pushl $-1 # fill in -1 for orig_eax
pushl %edx # we save %eax,%ecx,%edx as gcc doesn't
pushl %ecx # save those across function calls. %ebx
pushl %ebx # is saved as we use that in ret_sys_call
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
incl _jiffies
movb $0x20,%al # EOI to interrupt controller #1
outb %al,$0x20
movl CS(%esp),%eax
andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor)
pushl %eax
call _do_timer # 'do_timer(long CPL)' does everything from
addl $4,%esp # task switching to accounting ...
jmp ret_from_sys_call
.align 2
_sys_execve:
lea EIP(%esp),%eax
pushl %eax
call _do_execve
addl $4,%esp
ret
.align 2
_sys_fork:
call _find_empty_process
testl %eax,%eax
js 1f
push %gs
pushl %esi
pushl %edi
pushl %ebp
pushl %eax
call _copy_process
addl $20,%esp
1: ret
_hd_interrupt:
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
movb $0x20,%al
outb %al,$0xA0 # EOI to interrupt controller #1
jmp 1f # give port chance to breathe
1: jmp 1f
1: xorl %edx,%edx
movl %edx,_hd_timeout
xchgl _do_hd,%edx
testl %edx,%edx
jne 1f
movl $_unexpected_hd_interrupt,%edx
1: outb %al,$0x20
call *%edx # "interesting" way of handling intr.
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
_floppy_interrupt:
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
movb $0x20,%al
outb %al,$0x20 # EOI to interrupt controller #1
xorl %eax,%eax
xchgl _do_floppy,%eax
testl %eax,%eax
jne 1f
movl $_unexpected_floppy_interrupt,%eax
1: call *%eax # "interesting" way of handling intr.
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
_parallel_interrupt:
pushl %eax
movb $0x20,%al
outb %al,$0x20
popl %eax
iret

View File

@@ -0,0 +1,213 @@
/*
* linux/kernel/traps.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'Traps.c' handles hardware traps and faults after we have saved some
* state in 'asm.s'. Currently mostly a debugging-aid, will be extended
* to mainly kill the offending process (probably by giving it a signal,
* but possibly by killing it outright if necessary).
*/
#include <string.h>
#include <linux/head.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/io.h>
#define get_seg_byte(seg,addr) ({ \
register char __res; \
__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \
:"=a" (__res):"0" (seg),"m" (*(addr))); \
__res;})
#define get_seg_long(seg,addr) ({ \
register unsigned long __res; \
__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \
:"=a" (__res):"0" (seg),"m" (*(addr))); \
__res;})
#define _fs() ({ \
register unsigned short __res; \
__asm__("mov %%fs,%%ax":"=a" (__res):); \
__res;})
void page_exception(void);
void divide_error(void);
void debug(void);
void nmi(void);
void int3(void);
void overflow(void);
void bounds(void);
void invalid_op(void);
void device_not_available(void);
void double_fault(void);
void coprocessor_segment_overrun(void);
void invalid_TSS(void);
void segment_not_present(void);
void stack_segment(void);
void general_protection(void);
void page_fault(void);
void coprocessor_error(void);
void reserved(void);
void parallel_interrupt(void);
void irq13(void);
void alignment_check(void);
static void die(char * str,long esp_ptr,long nr)
{
long * esp = (long *) esp_ptr;
int i;
printk("%s: %04x\n\r",str,nr&0xffff);
printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n",
esp[1],esp[0],esp[2],esp[4],esp[3]);
printk("fs: %04x\n",_fs());
printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17));
if (esp[4] == 0x17) {
printk("Stack: ");
for (i=0;i<4;i++)
printk("%p ",get_seg_long(0x17,i+(long *)esp[3]));
printk("\n");
}
str(i);
printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i);
for(i=0;i<10;i++)
printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0])));
printk("\n\r");
do_exit(11); /* play segment exception */
}
void do_double_fault(long esp, long error_code)
{
die("double fault",esp,error_code);
}
void do_general_protection(long esp, long error_code)
{
die("general protection",esp,error_code);
}
void do_alignment_check(long esp, long error_code)
{
die("alignment check",esp,error_code);
}
void do_divide_error(long esp, long error_code)
{
die("divide error",esp,error_code);
}
void do_int3(long * esp, long error_code,
long fs,long es,long ds,
long ebp,long esi,long edi,
long edx,long ecx,long ebx,long eax)
{
int tr;
__asm__("str %%ax":"=a" (tr):"0" (0));
printk("eax\t\tebx\t\tecx\t\tedx\n\r%8x\t%8x\t%8x\t%8x\n\r",
eax,ebx,ecx,edx);
printk("esi\t\tedi\t\tebp\t\tesp\n\r%8x\t%8x\t%8x\t%8x\n\r",
esi,edi,ebp,(long) esp);
printk("\n\rds\tes\tfs\ttr\n\r%4x\t%4x\t%4x\t%4x\n\r",
ds,es,fs,tr);
printk("EIP: %8x CS: %4x EFLAGS: %8x\n\r",esp[0],esp[1],esp[2]);
}
void do_nmi(long esp, long error_code)
{
die("nmi",esp,error_code);
}
void do_debug(long esp, long error_code)
{
die("debug",esp,error_code);
}
void do_overflow(long esp, long error_code)
{
die("overflow",esp,error_code);
}
void do_bounds(long esp, long error_code)
{
die("bounds",esp,error_code);
}
void do_invalid_op(long esp, long error_code)
{
die("invalid operand",esp,error_code);
}
void do_device_not_available(long esp, long error_code)
{
die("device not available",esp,error_code);
}
void do_coprocessor_segment_overrun(long esp, long error_code)
{
die("coprocessor segment overrun",esp,error_code);
}
void do_invalid_TSS(long esp,long error_code)
{
die("invalid TSS",esp,error_code);
}
void do_segment_not_present(long esp,long error_code)
{
die("segment not present",esp,error_code);
}
void do_stack_segment(long esp,long error_code)
{
die("stack segment",esp,error_code);
}
void do_coprocessor_error(long esp, long error_code)
{
if (last_task_used_math != current)
return;
die("coprocessor error",esp,error_code);
}
void do_reserved(long esp, long error_code)
{
die("reserved (15,17-47) error",esp,error_code);
}
void trap_init(void)
{
int i;
set_trap_gate(0,&divide_error);
set_trap_gate(1,&debug);
set_trap_gate(2,&nmi);
set_system_gate(3,&int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_trap_gate(8,&double_fault);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_trap_gate(14,&page_fault);
set_trap_gate(15,&reserved);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
for (i=18;i<48;i++)
set_trap_gate(i,&reserved);
set_trap_gate(45,&irq13);
outb_p(inb_p(0x21)&0xfb,0x21);
outb(inb_p(0xA1)&0xdf,0xA1);
set_trap_gate(39,&parallel_interrupt);
}

View File

@@ -0,0 +1,233 @@
/*
* linux/kernel/vsprintf.c
*
* (C) 1991 Linus Torvalds
*/
/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/*
* Wirzenius wrote this portably, Torvalds fucked it up :-)
*/
#include <stdarg.h>
#include <string.h>
/* we use this so that we can do without the ctype library */
#define is_digit(c) ((c) >= '0' && (c) <= '9')
static int skip_atoi(const char **s)
{
int i=0;
while (is_digit(**s))
i = i*10 + *((*s)++) - '0';
return i;
}
#define ZEROPAD 1 /* pad with zero */
#define SIGN 2 /* unsigned/signed long */
#define PLUS 4 /* show plus */
#define SPACE 8 /* space if plus */
#define LEFT 16 /* left justified */
#define SPECIAL 32 /* 0x */
#define SMALL 64 /* use 'abcdef' instead of 'ABCDEF' */
#define do_div(n,base) ({ \
int __res; \
__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
__res; })
static char * number(char * str, int num, int base, int size, int precision
,int type)
{
char c,sign,tmp[36];
const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i;
if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz";
if (type&LEFT) type &= ~ZEROPAD;
if (base<2 || base>36)
return 0;
c = (type & ZEROPAD) ? '0' : ' ' ;
if (type&SIGN && num<0) {
sign='-';
num = -num;
} else
sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);
if (sign) size--;
if (type&SPECIAL)
if (base==16) size -= 2;
else if (base==8) size--;
i=0;
if (num==0)
tmp[i++]='0';
else while (num!=0)
tmp[i++]=digits[do_div(num,base)];
if (i>precision) precision=i;
size -= precision;
if (!(type&(ZEROPAD+LEFT)))
while(size-->0)
*str++ = ' ';
if (sign)
*str++ = sign;
if (type&SPECIAL)
if (base==8)
*str++ = '0';
else if (base==16) {
*str++ = '0';
*str++ = digits[33];
}
if (!(type&LEFT))
while(size-->0)
*str++ = c;
while(i<precision--)
*str++ = '0';
while(i-->0)
*str++ = tmp[i];
while(size-->0)
*str++ = ' ';
return str;
}
int vsprintf(char *buf, const char *fmt, va_list args)
{
int len;
int i;
char * str;
char *s;
int *ip;
int flags; /* flags to number() */
int field_width; /* width of output field */
int precision; /* min. # of digits for integers; max
number of chars for from string */
int qualifier; /* 'h', 'l', or 'L' for integer fields */
for (str=buf ; *fmt ; ++fmt) {
if (*fmt != '%') {
*str++ = *fmt;
continue;
}
/* process flags */
flags = 0;
repeat:
++fmt; /* this also skips first '%' */
switch (*fmt) {
case '-': flags |= LEFT; goto repeat;
case '+': flags |= PLUS; goto repeat;
case ' ': flags |= SPACE; goto repeat;
case '#': flags |= SPECIAL; goto repeat;
case '0': flags |= ZEROPAD; goto repeat;
}
/* get field width */
field_width = -1;
if (is_digit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == '.') {
++fmt;
if (is_digit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
/* get the conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
qualifier = *fmt;
++fmt;
}
switch (*fmt) {
case 'c':
if (!(flags & LEFT))
while (--field_width > 0)
*str++ = ' ';
*str++ = (unsigned char) va_arg(args, int);
while (--field_width > 0)
*str++ = ' ';
break;
case 's':
s = va_arg(args, char *);
len = strlen(s);
if (precision < 0)
precision = len;
else if (len > precision)
len = precision;
if (!(flags & LEFT))
while (len < field_width--)
*str++ = ' ';
for (i = 0; i < len; ++i)
*str++ = *s++;
while (len < field_width--)
*str++ = ' ';
break;
case 'o':
str = number(str, va_arg(args, unsigned long), 8,
field_width, precision, flags);
break;
case 'p':
if (field_width == -1) {
field_width = 8;
flags |= ZEROPAD;
}
str = number(str,
(unsigned long) va_arg(args, void *), 16,
field_width, precision, flags);
break;
case 'x':
flags |= SMALL;
case 'X':
str = number(str, va_arg(args, unsigned long), 16,
field_width, precision, flags);
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
str = number(str, va_arg(args, unsigned long), 10,
field_width, precision, flags);
break;
case 'n':
ip = va_arg(args, int *);
*ip = (str - buf);
break;
default:
if (*fmt != '%')
*str++ = '%';
if (*fmt)
*str++ = *fmt;
else
--fmt;
break;
}
}
*str = '\0';
return str-buf;
}