diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /arch/i386 | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'arch/i386')
79 files changed, 8144 insertions, 5629 deletions
diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 0b296da44..429b2b2b4 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -13,39 +13,65 @@ # Copyright (C) 1994 by Linus Torvalds # +AS86 =$(CROSS_COMPILE)as86 -0 -a +AS386 =$(CROSS_COMPILE)as86 -3 +LD86 =$(CROSS_COMPILE)ld86 -0 + # -# Set these to indicate how to link it.. -# -# -zmagic: +# ZIMAGE_OFFSET is the load offset of the compression loader +# BZIMAGE_OFFSET is the load offset of the high loaded compression loader # -# ZLINKFLAGS = -Ttext 0x1000 -# LINKFLAGS = -Ttext 0x100000 +ZIMAGE_OFFSET=0x1000 +BZIMAGE_OFFSET=0x100000 + # -# -qmagic (we need to remove the 32 byte header for bootup purposes) +# IMAGE_OFFSET is the load offset of the _real_ kernel, soon +# to be offset by another 0xC0000000... # -ZLINKFLAGS =-qmagic -Ttext 0xfe0 -LINKFLAGS =-qmagic -Ttext 0xfffe0 +IMAGE_OFFSET=0xC0100000 + +# This is used for ELF - it needs to migrate or be moved. +LD_RFLAG = -m elf_i386 + +LD=$(CROSS_COMPILE)ld -m elf_i386 +CPP=$(CC) -E -D__ELF__ +OBJDUMP=$(CROSS_COMPILE)objdump +OBJDUMP_FLAGS=-k -q +ENCAPS=$(CROSS_COMPILE)encaps +OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -R .stab -R .stabstr +ZLDFLAGS=-e startup_32 +LDFLAGS=-e stext +ZLINKFLAGS =-Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS) +BZLINKFLAGS =-Ttext $(BZIMAGE_OFFSET) $(ZLDFLAGS) +LINKFLAGS =-Ttext $(IMAGE_OFFSET) $(LDFLAGS) + CFLAGS := $(CFLAGS) -pipe +ifdef CONFIG_M386 +CFLAGS := $(CFLAGS) -m386 -DCPU=386 +endif + ifdef CONFIG_M486 -CFLAGS := $(CFLAGS) -m486 -else +CFLAGS := $(CFLAGS) -m486 -DCPU=486 +endif + ifdef CONFIG_M586 -CFLAGS := $(CFLAGS) -mpentium -else -CFLAGS := $(CFLAGS) -m386 +CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586 endif + +ifdef CONFIG_M686 +CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686 +endif + +ifdef SMP +CFLAGS := $(CFLAGS) -D__SMP__ endif HEAD := arch/i386/kernel/head.o -SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm +SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm arch/i386/lib ARCHIVES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(ARCHIVES) - -ifdef CONFIG_IBCS -SUBDIRS := $(SUBDIRS) arch/i386/ibcs -DRIVERS := $(DRIVERS) arch/i386/ibcs/ibcs.o -endif +LIBS := $(TOPDIR)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a ifdef CONFIG_MATH_EMULATION SUBDIRS := $(SUBDIRS) arch/i386/math-emu @@ -63,19 +89,29 @@ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot zImage: vmlinux @$(MAKEBOOT) zImage +bzImage: vmlinux + @$(MAKEBOOT) bzImage + compressed: zImage zlilo: vmlinux - @$(MAKEBOOT) zlilo + @$(MAKEBOOT) BOOTIMAGE=zImage zlilo + +bzlilo: vmlinux + @$(MAKEBOOT) BOOTIMAGE=bzImage zlilo zdisk: vmlinux - @$(MAKEBOOT) zdisk + @$(MAKEBOOT) BOOTIMAGE=zImage zdisk + +bzdisk: vmlinux + @$(MAKEBOOT) BOOTIMAGE=bzImage zdisk install: vmlinux @$(MAKEBOOT) install archclean: @$(MAKEBOOT) clean + $(MAKE) -C arch/$(ARCH)/kernel clean archdep: @$(MAKEBOOT) dep diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile index b6a5f1c15..503b0cce6 100644 --- a/arch/i386/boot/Makefile +++ b/arch/i386/boot/Makefile @@ -8,23 +8,43 @@ # Copyright (C) 1994 by Linus Torvalds # -AS86 =as86 -0 -a -LD86 =ld86 -0 +HOSTCFLAGS := $(HOSTCFLAGS) -D__BFD__ + +ifdef SMP +HOSTCFLAGS := $(HOSTCFLAGS) -D__SMP__ +endif zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build - tools/build bootsect setup compressed/vmlinux $(ROOT_DEV) > zImage + if hash $(ENCAPS) 2> /dev/null; then \ + $(OBJDUMP) $(OBJDUMP_FLAGS) -o $(ZIMAGE_OFFSET) compressed/vmlinux > compressed/vmlinux.out; \ + else \ + $(OBJCOPY) compressed/vmlinux compressed/vmlinux.out; \ + fi + tools/build bootsect setup compressed/vmlinux.out $(ROOT_DEV) > zImage + sync + +bzImage: $(CONFIGURE) bbootsect setup compressed/bvmlinux tools/bbuild + if hash $(ENCAPS) 2> /dev/null; then \ + $(OBJDUMP) $(OBJDUMP_FLAGS) -o $(BZIMAGE_OFFSET) compressed/bvmlinux > compressed/bvmlinux.out; \ + else \ + $(OBJCOPY) compressed/bvmlinux compressed/bvmlinux.out; \ + fi + tools/bbuild bbootsect setup compressed/bvmlinux.out $(ROOT_DEV) > bzImage sync compressed/vmlinux: $(TOPDIR)/vmlinux @$(MAKE) -C compressed vmlinux -zdisk: zImage - dd bs=8192 if=zImage of=/dev/fd0 +compressed/bvmlinux: $(TOPDIR)/vmlinux + @$(MAKE) -C compressed bvmlinux -zlilo: $(CONFIGURE) zImage +zdisk: $(BOOTIMAGE) + dd bs=8192 if=$(BOOTIMAGE) of=/dev/fd0 + +zlilo: $(CONFIGURE) $(BOOTIMAGE) if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi - cat zImage > $(INSTALL_PATH)/vmlinuz + cat $(BOOTIMAGE) > $(INSTALL_PATH)/vmlinuz cp $(TOPDIR)/System.map $(INSTALL_PATH)/ if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi @@ -32,7 +52,10 @@ install: $(CONFIGURE) zImage sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)" tools/build: tools/build.c - $(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< -I$(TOPDIR)/include + +tools/bbuild: tools/build.c + $(HOSTCC) $(HOSTCFLAGS) -D__BIG_KERNEL__ -o $@ $< -I$(TOPDIR)/include setup: setup.o $(LD86) -s -o $@ $< @@ -40,7 +63,7 @@ setup: setup.o setup.o: setup.s $(AS86) -o $@ $< -setup.s: setup.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile +setup.s: setup.S video.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ bootsect: bootsect.o @@ -52,9 +75,20 @@ bootsect.o: bootsect.s bootsect.s: bootsect.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ +bbootsect: bbootsect.o + $(LD86) -s -o $@ $< + +bbootsect.o: bbootsect.s + $(AS86) -o $@ $< + +bbootsect.s: bootsect.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile + $(CPP) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + dep: clean: rm -f bootsect setup - rm -f zImage tools/build + rm -f bbootsect + rm -f zImage tools/build compressed/vmlinux.out + rm -f bzImage tools/bbuild compressed/bvmlinux.out @$(MAKE) -C compressed clean diff --git a/arch/i386/boot/bootsect.S b/arch/i386/boot/bootsect.S index f6a0d3158..88a04190d 100644 --- a/arch/i386/boot/bootsect.S +++ b/arch/i386/boot/bootsect.S @@ -47,7 +47,7 @@ SWAP_DEV = 0 #define RAMDISK 0 #endif #ifndef CONFIG_ROOT_RDONLY -#define CONFIG_ROOT_RDONLY 0 +#define CONFIG_ROOT_RDONLY 1 #endif ! ld86 requires an entry symbol. This may as well be the usual one. @@ -268,8 +268,14 @@ read_it: die: jne die ! es must be at 64kB boundary xor bx,bx ! bx is starting address within segment rp_read: +#ifdef __BIG_KERNEL__ +#define CALL_HIGHLOAD_KLUDGE .word 0x1eff,0x220 ! call far * bootsect_kludge + ! NOTE: as86 can't assemble this + CALL_HIGHLOAD_KLUDGE ! this is within setup.S +#else mov ax,es sub ax,#SYSSEG +#endif cmp ax,syssize ! have we loaded all yet? jbe ok1_read ret diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile index b4e3f8482..d7f093e3f 100644 --- a/arch/i386/boot/compressed/Makefile +++ b/arch/i386/boot/compressed/Makefile @@ -7,35 +7,57 @@ HEAD = head.o SYSTEM = $(TOPDIR)/vmlinux -OBJECTS = $(HEAD) inflate.o unzip.o misc.o +OBJECTS = $(HEAD) misc.o CFLAGS = -O2 -DSTDC_HEADERS -.c.s: - $(CC) $(CFLAGS) -S $< -.s.o: - $(AS) -o $*.o $< -.c.o: - $(CC) $(CFLAGS) -c $< +ifdef SMP +CFLAGS := $(CFLAGS) -D__SMP__ +endif + +TARGET=--target elf32-i386 +INPUT_DATA=input_data +INPUT_LEN=input_len all: vmlinux -vmlinux: piggy.o $(OBJECTS) - $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o +vmlinux: piggy.o $(OBJECTS) + $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o + +bvmlinux: piggy.o $(OBJECTS) + $(LD) $(BZLINKFLAGS) -o bvmlinux $(OBJECTS) piggy.o + +ifdef SMP + +head.o: head.S $(TOPDIR)/include/linux/tasks.h + $(CC) -D__SMP__ -traditional -c head.S -head.o: head.s +else -head.s: head.S $(TOPDIR)/include/linux/tasks.h - $(CPP) -traditional head.S -o head.s +head.o: head.S $(TOPDIR)/include/linux/tasks.h + $(CC) -traditional -c head.S -piggy.o: $(SYSTEM) xtract piggyback - ./xtract $(SYSTEM) | gzip -9 | ./piggyback > piggy.o +endif -xtract: xtract.c - $(HOSTCC) $(CFLAGS) -o xtract xtract.c -piggyback: piggyback.c - $(HOSTCC) $(CFLAGS) -o piggyback piggyback.c +# You cannot compress a file and have the kernel uncompress it, it must +# be stdin +piggy.o: $(SYSTEM) + tmppiggy=/tmp/$$$$piggy; \ + rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ + if hash $(ENCAPS) 2> /dev/null; then \ + $(OBJDUMP) $(OBJDUMP_FLAGS) -o $(IMAGE_OFFSET) $(SYSTEM) > $$tmppiggy; \ + else \ + $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ + fi; \ + gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ + if hash $(ENCAPS) 2> /dev/null; then \ + $(ENCAPS) $(TARGET) piggy.o $$tmppiggy.gz $(INPUT_DATA) $(INPUT_LEN); \ + else \ + echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ + $(LD) -m elf_i386 -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ + fi; \ + rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk clean: - rm -f xtract piggyback vmlinux + rm -f xtract piggyback vmlinux bvmlinux diff --git a/arch/i386/boot/compressed/Makefile.debug b/arch/i386/boot/compressed/Makefile.debug new file mode 100644 index 000000000..83714b505 --- /dev/null +++ b/arch/i386/boot/compressed/Makefile.debug @@ -0,0 +1,21 @@ +# +# linux/arch/i386/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +OBJECTS = misc.o + +CFLAGS = -g -O2 -DSTDC_HEADERS -DSTANDALONE_DEBUG -Wall + +test-gzip: piggy.o $(OBJECTS) + $(CC) -g -o test-gzip $(OBJECTS) piggy.o + +clean: + $(RM) inflate.o misc.o test-gzip + +inflate.o: inflate.c gzip.h + +misc.o: misc.c gzip.h + + diff --git a/arch/i386/boot/compressed/crypt.h b/arch/i386/boot/compressed/crypt.h deleted file mode 100644 index 2a4c203ca..000000000 --- a/arch/i386/boot/compressed/crypt.h +++ /dev/null @@ -1,12 +0,0 @@ -/* crypt.h (dummy version) -- do not perform encryption - * Hardly worth copyrighting :-) - */ - -#ifdef CRYPT -# undef CRYPT /* dummy version */ -#endif - -#define RAND_HEAD_LEN 12 /* length of encryption random header */ - -#define zencode -#define zdecode diff --git a/arch/i386/boot/compressed/gzip.h b/arch/i386/boot/compressed/gzip.h deleted file mode 100644 index 2f738b945..000000000 --- a/arch/i386/boot/compressed/gzip.h +++ /dev/null @@ -1,284 +0,0 @@ -/* gzip.h -- common declarations for all gzip modules - * Copyright (C) 1992-1993 Jean-loup Gailly. - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License, see the file COPYING. - */ - -#if defined(__STDC__) || defined(PROTO) -# define OF(args) args -#else -# define OF(args) () -#endif - -#ifdef __STDC__ - typedef void *voidp; -#else - typedef char *voidp; -#endif - -/* I don't like nested includes, but the string functions are used too often */ -#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) -# include <string.h> -# define memzero(s, n) memset ((s), 0, (n)) -#else -# include <strings.h> -# define strchr index -# define strrchr rindex -# define memcpy(d, s, n) bcopy((s), (d), (n)) -# define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) -# define memzero(s, n) bzero((s), (n)) -#endif - -#if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) -# include <memory.h> -#endif - -#ifndef RETSIGTYPE -# define RETSIGTYPE void -#endif - -#define local static - -typedef unsigned char uch; -typedef unsigned short ush; -typedef unsigned long ulg; - -/* Return codes from gzip */ -#define OK 0 -#define ERROR 1 -#define WARNING 2 - -/* Compression methods (see algorithm.doc) */ -#define STORED 0 -#define COMPRESSED 1 -#define PACKED 2 -/* methods 3 to 7 reserved */ -#define DEFLATED 8 -extern int method; /* compression method */ - -/* To save memory for 16 bit systems, some arrays are overlayed between - * the various modules: - * deflate: prev+head window d_buf l_buf outbuf - * unlzw: tab_prefix tab_suffix stack inbuf outbuf - * inflate: window inbuf - * unpack: window inbuf - * For compression, input is done in window[]. For decompression, output - * is done in window except for unlzw. - */ - -#ifndef INBUFSIZ -# define INBUFSIZ 0x8000 /* input buffer size */ -#endif -#define INBUF_EXTRA 64 /* required by unlzw() */ - -#ifndef OUTBUFSIZ -# define OUTBUFSIZ 16384 /* output buffer size */ -#endif -#define OUTBUF_EXTRA 2048 /* required by unlzw() */ - -#define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ - -#ifdef DYN_ALLOC -# define EXTERN(type, array) extern type * near array -# define DECLARE(type, array, size) type * near array -# define ALLOC(type, array, size) { \ - array = (type*)fcalloc((unsigned)(((size)+1L)/2), 2*sizeof(type)); \ - if (array == NULL) error("insufficient memory"); \ - } -# define FREE(array) {if (array != NULL) fcfree(array), array=NULL;} -#else -# define EXTERN(type, array) extern type array[] -# define DECLARE(type, array, size) type array[size] -# define ALLOC(type, array, size) -# define FREE(array) -#endif - -EXTERN(uch, inbuf); /* input buffer */ -EXTERN(uch, outbuf); /* output buffer */ -EXTERN(ush, d_buf); /* buffer for distances, see trees.c */ -EXTERN(uch, window); /* Sliding window and suffix table (unlzw) */ -#define tab_suffix window -#ifndef MAXSEG_64K -# define tab_prefix prev /* hash link (see deflate.c) */ -# define head (prev+WSIZE) /* hash head (see deflate.c) */ - EXTERN(ush, tab_prefix); /* prefix code (see unlzw.c) */ -#else -# define tab_prefix0 prev -# define head tab_prefix1 - EXTERN(ush, tab_prefix0); /* prefix for even codes */ - EXTERN(ush, tab_prefix1); /* prefix for odd codes */ -#endif - -extern unsigned insize; /* valid bytes in inbuf */ -extern unsigned inptr; /* index of next byte to be processed in inbuf */ -extern unsigned outcnt; /* bytes in output buffer */ - -extern long bytes_in; /* number of input bytes */ -extern long bytes_out; /* number of output bytes */ -extern long overhead; /* number of bytes in gzip header */ - -#define isize bytes_in -/* for compatibility with old zip sources (to be cleaned) */ - -extern int ifd; /* input file descriptor */ -extern int ofd; /* output file descriptor */ -extern char ifname[]; /* input filename or "stdin" */ -extern char ofname[]; /* output filename or "stdout" */ - -extern ulg time_stamp; /* original time stamp (modification time) */ -extern long ifile_size; /* input file size, -1 for devices (debug only) */ - -extern int exit_code; /* program exit code */ - -typedef int file_t; /* Do not use stdio */ -#define NO_FILE (-1) /* in memory compression */ - - -#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ -#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */ -#define PKZIP_MAGIC "PK\003\004" /* Magic header for pkzip files */ -#define PACK_MAGIC "\037\036" /* Magic header for packed files */ - -/* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ -#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ -#define RESERVED 0xC0 /* bit 6,7: reserved */ - -/* internal file attribute */ -#define UNKNOWN (-1) -#define BINARY 0 -#define ASCII 1 - -#ifndef WSIZE -# define WSIZE 0x8000 /* window size--must be a power of two, and */ -#endif /* at least 32K for zip's deflate method */ - -#define MIN_MATCH 3 -#define MAX_MATCH 258 -/* The minimum and maximum match lengths */ - -#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) -/* Minimum amount of lookahead, except at the end of the input file. - * See deflate.c for comments about the MIN_MATCH+1. - */ - -#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) -/* In order to simplify the code, particularly on 16 bit machines, match - * distances are limited to MAX_DIST instead of WSIZE. - */ - -extern int decrypt; /* flag to turn on decryption */ -extern int save_orig_name; /* set if original name must be saved */ -extern int verbose; /* be verbose (-v) */ -extern int level; /* compression level */ -extern int test; /* check .z file integrity */ -extern int to_stdout; /* output to stdout (-c) */ - -#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) - -/* put_byte is used for the compressed output, put_char for the - * uncompressed output. However unlzw() uses window for its - * suffix table instead of its output buffer, so it does not use put_char. - * (to be cleaned up). - */ -#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\ - flush_outbuf();} -#define put_char(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\ - flush_window();} - -/* Output a 16 bit value, lsb first */ -#define put_short(w) \ -{ if (outcnt < OUTBUFSIZ-2) { \ - outbuf[outcnt++] = (uch) ((w) & 0xff); \ - outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \ - } else { \ - put_byte((uch)((w) & 0xff)); \ - put_byte((uch)((ush)(w) >> 8)); \ - } \ -} - -/* Output a 32 bit value to the bit stream, lsb first */ -#define put_long(n) { \ - put_short((n) & 0xffff); \ - put_short(((ulg)(n)) >> 16); \ -} - -#define seekable() 0 /* force sequential output */ -#define translate_eol 0 /* no option -a yet */ - -#define tolow(c) (isupper(c) ? (c)-'A'+'a' : (c)) /* force to lower case */ - -/* Macros for getting two-byte and four-byte header values */ -#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)) -#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)) - -/* Diagnostic functions */ -#ifdef DEBUG -# define Assert(cond,msg) {if(!(cond)) error(msg);} -# define Trace(x) fprintf x -# define Tracev(x) {if (verbose) fprintf x ;} -# define Tracevv(x) {if (verbose>1) fprintf x ;} -# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} -# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} -#else -# define Assert(cond,msg) -# define Trace(x) -# define Tracev(x) -# define Tracevv(x) -# define Tracec(c,x) -# define Tracecv(c,x) -#endif - - /* in zip.c: */ -extern void zip OF((int in, int out)); -extern int file_read OF((char *buf, unsigned size)); - - /* in unzip.c */ -extern void unzip OF((int in, int out)); -extern int check_zipfile OF((int in)); - - /* in unpack.c */ -extern void unpack OF((int in, int out)); - - /* in gzip.c */ -RETSIGTYPE abort_gzip OF((void)); - - /* in deflate.c */ -void lm_init OF((int pack_level, ush *flags)); -ulg deflate OF((void)); - - /* in trees.c */ -void ct_init OF((ush *attr, int *method)); -int ct_tally OF((int dist, int lc)); -ulg flush_block OF((char *buf, ulg stored_len, int eof)); - - /* in bits.c */ -void bi_init OF((file_t zipfile)); -void send_bits OF((int value, int length)); -unsigned bi_reverse OF((unsigned value, int length)); -void bi_windup OF((void)); -void copy_block OF((char *buf, unsigned len, int header)); -extern int (*read_buf) OF((char *buf, unsigned size)); - - /* in util.c: */ -extern ulg updcrc OF((uch *s, unsigned n)); -extern void clear_bufs OF((void)); -extern int fill_inbuf OF((void)); -extern void flush_outbuf OF((void)); -extern void flush_window OF((void)); -extern char *strlwr OF((char *s)); -extern char *basename OF((char *fname)); -extern char *add_envopt OF((int *argcp, char ***argvp, char *env)); -extern void error OF((char *m)); -extern void warn OF((char *a, char *b)); -extern void read_error OF((void)); -extern void write_error OF((void)); -extern void display_ratio OF((long num, long den)); -extern voidp xmalloc OF((unsigned int size)); - - /* in inflate.c */ -extern int inflate OF((void)); diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S index 6035cea85..6f68a92d4 100644 --- a/arch/i386/boot/compressed/head.S +++ b/arch/i386/boot/compressed/head.S @@ -9,18 +9,31 @@ * * NOTE!!! Startup happens at absolute address 0x00001000, which is also where * the page directory will exist. The startup code will be overwritten by - * the page directory. + * the page directory. [According to comments etc elsewhere on a compressed + * kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC] + * + * In SMP mode we keep this page safe. Really we ought to shuffle things and + * put the trampoline here. - AC. An SMP trampoline enters with %cx holding + * the stack base. * * Page 0 is deliberately kept safe, since System Management Mode code in * laptops may need to access the BIOS data stored there. This is also * useful for future device drivers that either access the BIOS via VM86 * mode. */ + +/* + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + */ .text #define __ASSEMBLY__ +#include <linux/config.h> +#include <linux/linkage.h> #include <asm/segment.h> + .globl startup_32 + startup_32: cld cli @@ -29,7 +42,25 @@ startup_32: mov %ax,%es mov %ax,%fs mov %ax,%gs - lss _stack_start,%esp +#ifdef __SMP__ + orw %bx,%bx # What state are we in BX=1 for SMP + # 0 for boot + jz 2f # Initial boot + +/* + * We are trampolining an SMP processor + */ + mov %ax,%ss + xorl %eax,%eax # Back to 0 + mov %cx,%ax # SP low 16 bits + movl %eax,%esp + pushl 0 # Clear NT + popfl + ljmp $(KERNEL_CS), $0x100000 # Into C and sanity + +2: +#endif + lss SYMBOL_NAME(stack_start),%esp xorl %eax,%eax 1: incl %eax # check that A20 really IS enabled movl %eax,0x000000 # loop forever if it isn't @@ -46,8 +77,8 @@ startup_32: * Clear BSS */ xorl %eax,%eax - movl $__edata,%edi - movl $__end,%ecx + movl $ SYMBOL_NAME(_edata),%edi + movl $ SYMBOL_NAME(_end),%ecx subl %edi,%ecx cld rep @@ -55,5 +86,55 @@ startup_32: /* * Do the decompression, and jump to the new kernel.. */ - call _decompress_kernel + subl $16,%esp # place for structure on the stack + pushl %esp # address of structure as first arg + call SYMBOL_NAME(decompress_kernel) + orl %eax,%eax + jnz 3f + xorl %ebx,%ebx + ljmp $(KERNEL_CS), $0x100000 + +/* + * We come here, if we were loaded high. + * We need to move the move-in-place routine down to 0x1000 + * and then start it with the buffer addresses in registers, + * which we got from the stack. + */ +3: + movl $move_routine_start,%esi + movl $0x1000,%edi + movl $move_routine_end,%ecx + subl %esi,%ecx + cld + rep + movsb + + popl %esi # discard the address + popl %esi # low_buffer_start + popl %ecx # lcount + popl %edx # high_buffer_start + popl %eax # hcount + movl $0x100000,%edi + cli # make sure we don't get interrupted + ljmp $(KERNEL_CS), $0x1000 # and jump to the move routine + +/* + * Routine (template) for moving the decompressed kernel in place, + * if we were high loaded. This _must_ PIC-code ! + */ +move_routine_start: + rep + movsb + movl %edx,%esi + movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0 + rep + movsb + xorl %ebx,%ebx +/* + * Well, the kernel relies on %esp pointing into low mem, + * with the decompressor loaded high this is no longer true, + * so we set esp here. + */ + mov $0x90000,%esp ljmp $(KERNEL_CS), $0x100000 +move_routine_end: diff --git a/arch/i386/boot/compressed/inflate.c b/arch/i386/boot/compressed/inflate.c deleted file mode 100644 index 848fef6ae..000000000 --- a/arch/i386/boot/compressed/inflate.c +++ /dev/null @@ -1,810 +0,0 @@ -#define DEBG(x) -#define DEBG1(x) -/* inflate.c -- Not copyrighted 1992 by Mark Adler - version c10p1, 10 January 1993 */ - -/* - * Adapted for booting Linux by Hannu Savolainen 1993 - * based on gzip-1.0.3 - */ - -#ifndef lint -static char rcsid[] = "$Id: inflate.c,v 0.10 1993/02/04 13:21:06 jloup Exp $"; -#endif - -#include "gzip.h" -#define slide window - -#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H) -# include <sys/types.h> -# include <stdlib.h> -#endif - -struct huft { - uch e; /* number of extra bits or operation */ - uch b; /* number of bits in this code or subcode */ - union { - ush n; /* literal, length base, or distance base */ - struct huft *t; /* pointer to next level of table */ - } v; -}; - - -/* Function prototypes */ -int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *, - struct huft **, int *)); -int huft_free OF((struct huft *)); -int inflate_codes OF((struct huft *, struct huft *, int, int)); -int inflate_stored OF((void)); -int inflate_fixed OF((void)); -int inflate_dynamic OF((void)); -int inflate_block OF((int *)); -int inflate OF((void)); - - -#define wp outcnt -#define flush_output(w) (wp=(w),flush_window()) - -/* Tables for deflate from PKZIP's appnote.txt. */ -static unsigned border[] = { /* Order of the bit length code lengths */ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; -static ush cplens[] = { /* Copy lengths for literal codes 257..285 */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; - /* note: see note #13 above about the 258 in this list. */ -static ush cplext[] = { /* Extra bits for literal codes 257..285 */ - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */ -static ush cpdist[] = { /* Copy offsets for distance codes 0..29 */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577}; -static ush cpdext[] = { /* Extra bits for distance codes */ - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, - 12, 12, 13, 13}; - - -ulg bb; /* bit buffer */ -unsigned bk; /* bits in bit buffer */ - -ush mask_bits[] = { - 0x0000, - 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, - 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff -}; - -#ifdef CRYPT - uch cc; -# define NEXTBYTE() \ - (decrypt ? (cc = get_byte(), zdecode(cc), cc) : get_byte()) -#else -# define NEXTBYTE() (uch)get_byte() -#endif -#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}} -#define DUMPBITS(n) {b>>=(n);k-=(n);} - -int lbits = 9; /* bits in base literal/length lookup table */ -int dbits = 6; /* bits in base distance lookup table */ - - -/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ -#define BMAX 16 /* maximum bit length of any code (16 for explode) */ -#define N_MAX 288 /* maximum number of codes in any set */ - - -unsigned hufts; /* track memory usage */ - - -int huft_build(b, n, s, d, e, t, m) -unsigned *b; /* code lengths in bits (all assumed <= BMAX) */ -unsigned n; /* number of codes (assumed <= N_MAX) */ -unsigned s; /* number of simple-valued codes (0..s-1) */ -ush *d; /* list of base values for non-simple codes */ -ush *e; /* list of extra bits for non-simple codes */ -struct huft **t; /* result: starting table */ -int *m; /* maximum lookup bits, returns actual */ -/* Given a list of code lengths and a maximum table size, make a set of - tables to decode that set of codes. Return zero on success, one if - the given code set is incomplete (the tables are still built in this - case), two if the input is invalid (all zero length codes or an - oversubscribed set of lengths), and three if not enough memory. */ -{ - unsigned a; /* counter for codes of length k */ - unsigned c[BMAX+1]; /* bit length count table */ - unsigned f; /* i repeats in table every f entries */ - int g; /* maximum code length */ - int h; /* table level */ - register unsigned i; /* counter, current code */ - register unsigned j; /* counter */ - register int k; /* number of bits in current code */ - int l; /* bits per table (returned in m) */ - register unsigned *p; /* pointer into c[], b[], or v[] */ - register struct huft *q; /* points to current table */ - struct huft r; /* table entry for structure assignment */ - struct huft *u[BMAX]; /* table stack */ - unsigned v[N_MAX]; /* values in order of bit length */ - register int w; /* bits before this table == (l * h) */ - unsigned x[BMAX+1]; /* bit offsets, then code stack */ - unsigned *xp; /* pointer into x */ - int y; /* number of dummy codes added */ - unsigned z; /* number of entries in current table */ - -DEBG("huft1 "); - - /* Generate counts for each bit length */ - memzero(c, sizeof(c)); - p = b; i = n; - do { - c[*p++]++; /* assume all entries <= BMAX */ - } while (--i); - if (c[0] == n) /* null input--all zero length codes */ - { - *t = (struct huft *)NULL; - *m = 0; - return 0; - } - -DEBG("huft2 "); - - /* Find minimum and maximum length, bound *m by those */ - l = *m; - for (j = 1; j <= BMAX; j++) - if (c[j]) - break; - k = j; /* minimum code length */ - if ((unsigned)l < j) - l = j; - for (i = BMAX; i; i--) - if (c[i]) - break; - g = i; /* maximum code length */ - if ((unsigned)l > i) - l = i; - *m = l; - -DEBG("huft3 "); - - /* Adjust last length count to fill out codes, if needed */ - for (y = 1 << j; j < i; j++, y <<= 1) - if ((y -= c[j]) < 0) - return 2; /* bad input: more codes than bits */ - if ((y -= c[i]) < 0) - return 2; - c[i] += y; - -DEBG("huft4 "); - - /* Generate starting offsets into the value table for each length */ - x[1] = j = 0; - p = c + 1; xp = x + 2; - while (--i) { /* note that i == g from above */ - *xp++ = (j += *p++); - } - -DEBG("huft5 "); - - /* Make a table of values in order of bit lengths */ - p = b; i = 0; - do { - if ((j = *p++) != 0) - v[x[j]++] = i; - } while (++i < n); - -DEBG("h6 "); - - /* Generate the Huffman codes and for each, make the table entries */ - x[0] = i = 0; /* first Huffman code is zero */ - p = v; /* grab values in bit order */ - h = -1; /* no tables yet--level -1 */ - w = -l; /* bits decoded == (l * h) */ - u[0] = (struct huft *)NULL; /* just to keep compilers happy */ - q = (struct huft *)NULL; /* ditto */ - z = 0; /* ditto */ -DEBG("h6a "); - - /* go through the bit lengths (k already is bits in shortest code) */ - for (; k <= g; k++) - { -DEBG("h6b "); - a = c[k]; - while (a--) - { -DEBG("h6b1 "); - /* here i is the Huffman code of length k bits for value *p */ - /* make tables up to required level */ - while (k > w + l) - { -DEBG1("1 "); - h++; - w += l; /* previous table always l bits */ - - /* compute minimum size table less than or equal to l bits */ - z = (z = g - w) > (unsigned)l ? l : z; /* upper limit on table size */ - if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ - { /* too few codes for k-w bit table */ -DEBG1("2 "); - f -= a + 1; /* deduct codes from patterns left */ - xp = c + k; - while (++j < z) /* try smaller tables up to z bits */ - { - if ((f <<= 1) <= *++xp) - break; /* enough codes to use up j bits */ - f -= *xp; /* else deduct codes from patterns */ - } - } -DEBG1("3 "); - z = 1 << j; /* table entries for j-bit table */ - - /* allocate and link in new table */ - q = (struct huft *)malloc((z + 1)*sizeof(struct huft)); -DEBG1("4 "); - hufts += z + 1; /* track memory usage */ - *t = q + 1; /* link to list for huft_free() */ - *(t = &(q->v.t)) = (struct huft *)NULL; - u[h] = ++q; /* table starts after link */ - -DEBG1("5 "); - /* connect to last table, if there is one */ - if (h) - { - x[h] = i; /* save pattern for backing up */ - r.b = (uch)l; /* bits to dump before this table */ - r.e = (uch)(16 + j); /* bits in this table */ - r.v.t = q; /* pointer to this table */ - j = i >> (w - l); /* (get around Turbo C bug) */ - u[h-1][j] = r; /* connect to last table */ - } -DEBG1("6 "); - } -DEBG("h6c "); - - /* set up table entry in r */ - r.b = (uch)(k - w); - if (p >= v + n) - r.e = 99; /* out of values--invalid code */ - else if (*p < s) - { - r.e = (uch)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ - r.v.n = *p++; /* simple code is just the value */ - } - else - { - r.e = (uch)e[*p - s]; /* non-simple--look up in lists */ - r.v.n = d[*p++ - s]; - } -DEBG("h6d "); - - /* fill code-like entries with r */ - f = 1 << (k - w); - for (j = i >> w; j < z; j += f) - q[j] = r; - - /* backwards increment the k-bit code i */ - for (j = 1 << (k - 1); i & j; j >>= 1) - i ^= j; - i ^= j; - - /* backup over finished tables */ - while ((i & ((1 << w) - 1)) != x[h]) - { - h--; /* don't need to update q */ - w -= l; - } -DEBG("h6e "); - } -DEBG("h6f "); - } - -DEBG("huft7 "); - - /* Return true (1) if we were given an incomplete table */ - return y != 0 && g != 1; -} - - - -int huft_free(t) -struct huft *t; /* table to free */ -/* Free the malloc'ed tables built by huft_build(), which makes a linked - list of the tables it made, with the links in a dummy first entry of - each table. */ -{ - register struct huft *p, *q; - - - /* Go through linked list, freeing from the malloced (t[-1]) address. */ - p = t; - while (p != (struct huft *)NULL) - { - q = (--p)->v.t; - free(p); - p = q; - } - return 0; -} - - -int inflate_codes(tl, td, bl, bd) -struct huft *tl, *td; /* literal/length and distance decoder tables */ -int bl, bd; /* number of bits decoded by tl[] and td[] */ -/* inflate (decompress) the codes in a deflated (compressed) block. - Return an error code or zero if it all goes ok. */ -{ - register unsigned e; /* table entry flag/number of extra bits */ - unsigned n, d; /* length and index for copy */ - unsigned w; /* current window position */ - struct huft *t; /* pointer to table entry */ - unsigned ml, md; /* masks for bl and bd bits */ - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - - - /* make local copies of globals */ - b = bb; /* initialize bit buffer */ - k = bk; - w = wp; /* initialize window position */ - - /* inflate the coded data */ - ml = mask_bits[bl]; /* precompute masks for speed */ - md = mask_bits[bd]; - for (;;) /* do until end of block */ - { - NEEDBITS((unsigned)bl) - if ((e = (t = tl + ((unsigned)b & ml))->e) > 16) - do { - if (e == 99) - return 1; - DUMPBITS(t->b) - e -= 16; - NEEDBITS(e) - } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); - DUMPBITS(t->b) - if (e == 16) /* then it's a literal */ - { - slide[w++] = (uch)t->v.n; - if (w == WSIZE) - { - flush_output(w); - w = 0; - } - } - else /* it's an EOB or a length */ - { - /* exit if end of block */ - if (e == 15) - break; - - /* get length of block to copy */ - NEEDBITS(e) - n = t->v.n + ((unsigned)b & mask_bits[e]); - DUMPBITS(e); - - /* decode distance of block to copy */ - NEEDBITS((unsigned)bd) - if ((e = (t = td + ((unsigned)b & md))->e) > 16) - do { - if (e == 99) - return 1; - DUMPBITS(t->b) - e -= 16; - NEEDBITS(e) - } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); - DUMPBITS(t->b) - NEEDBITS(e) - d = w - t->v.n - ((unsigned)b & mask_bits[e]); - DUMPBITS(e) - - /* do the copy */ - do { - n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e); -#if !defined(NOMEMCPY) && !defined(DEBUG) - if (w - d >= e) /* (this test assumes unsigned comparison) */ - { - memcpy(slide + w, slide + d, e); - w += e; - d += e; - } - else /* do it slow to avoid memcpy() overlap */ -#endif /* !NOMEMCPY */ - do { - slide[w++] = slide[d++]; - } while (--e); - if (w == WSIZE) - { - flush_output(w); - w = 0; - } - } while (n); - } - } - - - /* restore the globals from the locals */ - wp = w; /* restore global window pointer */ - bb = b; /* restore global bit buffer */ - bk = k; - - /* done */ - return 0; -} - - - -int inflate_stored() -/* "decompress" an inflated type 0 (stored) block. */ -{ - unsigned n; /* number of bytes in block */ - unsigned w; /* current window position */ - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - -DEBG("<stor"); - - /* make local copies of globals */ - b = bb; /* initialize bit buffer */ - k = bk; - w = wp; /* initialize window position */ - - - /* go to byte boundary */ - n = k & 7; - DUMPBITS(n); - - - /* get the length and its complement */ - NEEDBITS(16) - n = ((unsigned)b & 0xffff); - DUMPBITS(16) - NEEDBITS(16) - if (n != (unsigned)((~b) & 0xffff)) - return 1; /* error in compressed data */ - DUMPBITS(16) - - - /* read and output the compressed data */ - while (n--) - { - NEEDBITS(8) - slide[w++] = (uch)b; - if (w == WSIZE) - { - flush_output(w); - w = 0; - } - DUMPBITS(8) - } - - - /* restore the globals from the locals */ - wp = w; /* restore global window pointer */ - bb = b; /* restore global bit buffer */ - bk = k; - - DEBG(">"); - return 0; -} - - - -int inflate_fixed() -/* decompress an inflated type 1 (fixed Huffman codes) block. We should - either replace this with a custom decoder, or at least precompute the - Huffman tables. */ -{ - int i; /* temporary variable */ - struct huft *tl; /* literal/length code table */ - struct huft *td; /* distance code table */ - int bl; /* lookup bits for tl */ - int bd; /* lookup bits for td */ - unsigned l[288]; /* length list for huft_build */ - -DEBG("<fix"); - - /* set up literal table */ - for (i = 0; i < 144; i++) - l[i] = 8; - for (; i < 256; i++) - l[i] = 9; - for (; i < 280; i++) - l[i] = 7; - for (; i < 288; i++) /* make a complete, but wrong code set */ - l[i] = 8; - bl = 7; - if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0) - return i; - - - /* set up distance table */ - for (i = 0; i < 30; i++) /* make an incomplete code set */ - l[i] = 5; - bd = 5; - if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1) - { - huft_free(tl); - - DEBG(">"); - return i; - } - - - /* decompress until an end-of-block code */ - if (inflate_codes(tl, td, bl, bd)) - return 1; - - - /* free the decoding tables, return */ - huft_free(tl); - huft_free(td); - return 0; -} - - - -int inflate_dynamic() -/* decompress an inflated type 2 (dynamic Huffman codes) block. */ -{ - int i; /* temporary variables */ - unsigned j; - unsigned l; /* last length */ - unsigned m; /* mask for bit lengths table */ - unsigned n; /* number of lengths to get */ - struct huft *tl; /* literal/length code table */ - struct huft *td; /* distance code table */ - int bl; /* lookup bits for tl */ - int bd; /* lookup bits for td */ - unsigned nb; /* number of bit length codes */ - unsigned nl; /* number of literal/length codes */ - unsigned nd; /* number of distance codes */ -#ifdef PKZIP_BUG_WORKAROUND - unsigned ll[288+32]; /* literal/length and distance code lengths */ -#else - unsigned ll[286+30]; /* literal/length and distance code lengths */ -#endif - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - -DEBG("<dyn"); - - /* make local bit buffer */ - b = bb; - k = bk; - - - /* read in table lengths */ - NEEDBITS(5) - nl = 257 + ((unsigned)b & 0x1f); /* number of literal/length codes */ - DUMPBITS(5) - NEEDBITS(5) - nd = 1 + ((unsigned)b & 0x1f); /* number of distance codes */ - DUMPBITS(5) - NEEDBITS(4) - nb = 4 + ((unsigned)b & 0xf); /* number of bit length codes */ - DUMPBITS(4) -#ifdef PKZIP_BUG_WORKAROUND - if (nl > 288 || nd > 32) -#else - if (nl > 286 || nd > 30) -#endif - return 1; /* bad lengths */ - -DEBG("dyn1 "); - - /* read in bit-length-code lengths */ - for (j = 0; j < nb; j++) - { - NEEDBITS(3) - ll[border[j]] = (unsigned)b & 7; - DUMPBITS(3) - } - for (; j < 19; j++) - ll[border[j]] = 0; - -DEBG("dyn2 "); - - /* build decoding table for trees--single level, 7 bit lookup */ - bl = 7; - if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) - { - if (i == 1) - huft_free(tl); - return i; /* incomplete code set */ - } - -DEBG("dyn3 "); - - /* read in literal and distance code lengths */ - n = nl + nd; - m = mask_bits[bl]; - i = l = 0; - while ((unsigned)i < n) - { - NEEDBITS((unsigned)bl) - j = (td = tl + ((unsigned)b & m))->b; - DUMPBITS(j) - j = td->v.n; - if (j < 16) /* length of code in bits (0..15) */ - ll[i++] = l = j; /* save last length in l */ - else if (j == 16) /* repeat last length 3 to 6 times */ - { - NEEDBITS(2) - j = 3 + ((unsigned)b & 3); - DUMPBITS(2) - if ((unsigned)i + j > n) - return 1; - while (j--) - ll[i++] = l; - } - else if (j == 17) /* 3 to 10 zero length codes */ - { - NEEDBITS(3) - j = 3 + ((unsigned)b & 7); - DUMPBITS(3) - if ((unsigned)i + j > n) - return 1; - while (j--) - ll[i++] = 0; - l = 0; - } - else /* j == 18: 11 to 138 zero length codes */ - { - NEEDBITS(7) - j = 11 + ((unsigned)b & 0x7f); - DUMPBITS(7) - if ((unsigned)i + j > n) - return 1; - while (j--) - ll[i++] = 0; - l = 0; - } - } - -DEBG("dyn4 "); - - /* free decoding table for trees */ - huft_free(tl); - -DEBG("dyn5 "); - - /* restore the global bit buffer */ - bb = b; - bk = k; - -DEBG("dyn5a "); - - /* build the decoding tables for literal/length and distance codes */ - bl = lbits; - if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) - { -DEBG("dyn5b "); - if (i == 1) { - error(" incomplete literal tree\n"); - huft_free(tl); - } - return i; /* incomplete code set */ - } -DEBG("dyn5c "); - bd = dbits; - if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) - { -DEBG("dyn5d "); - if (i == 1) { - error(" incomplete distance tree\n"); -#ifdef PKZIP_BUG_WORKAROUND - i = 0; - } -#else - huft_free(td); - } - huft_free(tl); - return i; /* incomplete code set */ -#endif - } - -DEBG("dyn6 "); - - /* decompress until an end-of-block code */ - if (inflate_codes(tl, td, bl, bd)) - return 1; - -DEBG("dyn7 "); - - /* free the decoding tables, return */ - huft_free(tl); - huft_free(td); - - DEBG(">"); - return 0; -} - - - -int inflate_block(e) -int *e; /* last block flag */ -/* decompress an inflated block */ -{ - unsigned t; /* block type */ - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - - DEBG("<blk"); - - /* make local bit buffer */ - b = bb; - k = bk; - - - /* read in last block bit */ - NEEDBITS(1) - *e = (int)b & 1; - DUMPBITS(1) - - - /* read in block type */ - NEEDBITS(2) - t = (unsigned)b & 3; - DUMPBITS(2) - - - /* restore the global bit buffer */ - bb = b; - bk = k; - - /* inflate that block type */ - if (t == 2) - return inflate_dynamic(); - if (t == 0) - return inflate_stored(); - if (t == 1) - return inflate_fixed(); - - DEBG(">"); - - /* bad block type */ - return 2; -} - - - -int inflate() -/* decompress an inflated entry */ -{ - int e; /* last block flag */ - int r; /* result code */ - unsigned h; /* maximum struct huft's malloc'ed */ - - - /* initialize window, bit buffer */ - wp = 0; - bk = 0; - bb = 0; - - - /* decompress until the last block */ - h = 0; - do { - hufts = 0; - if ((r = inflate_block(&e)) != 0) - return r; - if (hufts > h) - h = hufts; - } while (!e); - - /* Undo too much lookahead. The next read will be byte aligned so we - * can discard unused bits in the last meaningful byte. - */ - while (bk >= 8) { - bk -= 8; - inptr--; - } - - /* flush out slide */ - flush_output(wp); - - - /* return success */ -#ifdef DEBUG - fprintf(stderr, "<%u> ", h); -#endif /* DEBUG */ - return 0; -} diff --git a/arch/i386/boot/compressed/lzw.h b/arch/i386/boot/compressed/lzw.h deleted file mode 100644 index 4e640f5a2..000000000 --- a/arch/i386/boot/compressed/lzw.h +++ /dev/null @@ -1,42 +0,0 @@ -/* lzw.h -- define the lzw functions. - * Copyright (C) 1992-1993 Jean-loup Gailly. - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License, see the file COPYING. - */ - -#if !defined(OF) && defined(lint) -# include "gzip.h" -#endif - -#ifndef BITS -# define BITS 16 -#endif -#define INIT_BITS 9 /* Initial number of bits per code */ - -#define LZW_MAGIC "\037\235" /* Magic header for lzw files, 1F 9D */ - -#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ -/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. - * It's a pity that old uncompress does not check bit 0x20. That makes - * extension of the format actually undesirable because old compress - * would just crash on the new format instead of giving a meaningful - * error message. It does check the number of bits, but it's more - * helpful to say "unsupported format, get a new version" than - * "can only handle 16 bits". - */ - -#define BLOCK_MODE 0x80 -/* Block compression: if table is full and compression rate is dropping, - * clear the dictionary. - */ - -#define LZW_RESERVED 0x60 /* reserved bits */ - -#define CLEAR 256 /* flush the dictionary */ -#define FIRST (CLEAR+1) /* first free entry */ - -extern int maxbits; /* max bits per code for LZW */ -extern int block_mode; /* block compress mode -C compatible with 2.0 */ - -extern void lzw OF((int in, int out)); -extern void unlzw OF((int in, int out)); diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c index 2623925c7..296517fee 100644 --- a/arch/i386/boot/compressed/misc.c +++ b/arch/i386/boot/compressed/misc.c @@ -5,15 +5,75 @@ * adapted for Linux. * * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 - * puts by Nick Holloway 1993 + * puts by Nick Holloway 1993, better puts by Martin Mares 1995 + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 */ -#include "gzip.h" -#include "lzw.h" +#include <string.h> #include <asm/segment.h> +#include <asm/io.h> /* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy +#define memzero(s, n) memset ((s), 0, (n)) + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* Sliding window buffer */ + +static unsigned insize = 0; /* valid bytes in inbuf */ +static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ +static unsigned outcnt = 0; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +/* * These are set up by the setup-routine at boot-time: */ @@ -24,10 +84,11 @@ struct screen_info { unsigned short orig_video_page; unsigned char orig_video_mode; unsigned char orig_video_cols; - unsigned short orig_video_ega_ax; + unsigned short unused2; unsigned short orig_video_ega_bx; - unsigned short orig_video_ega_cx; + unsigned short unused3; unsigned char orig_video_lines; + unsigned char orig_video_isVGA; }; /* @@ -40,82 +101,73 @@ struct screen_info { #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) #define AUX_DEVICE_INFO (*(unsigned char *)0x901FF) -#define EOF -1 - -DECLARE(uch, inbuf, INBUFSIZ); -DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA); -DECLARE(uch, window, WSIZE); - -unsigned outcnt; -unsigned insize; -unsigned inptr; - extern char input_data[]; extern int input_len; -int input_ptr; - -int method, exit_code, part_nb, last_member; -int test = 0; -int force = 0; -int verbose = 1; -long bytes_in, bytes_out; - -char *output_data; -unsigned long output_ptr; - -extern int end; -long free_mem_ptr = (long)&end; - -int to_stdout = 0; -int hard_math = 0; - -void (*work)(int inf, int outf); -void makecrc(void); - -local int get_method(int); - -char *vidmem = (char *)0xb8000; -int lines, cols; - +static long bytes_out = 0; +static uch *output_data; +static unsigned long output_ptr = 0; + + +static void *malloc(int size); +static void free(void *where); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +#ifndef STANDALONE_DEBUG static void puts(const char *); - -void *malloc(int size) + +extern int end; +static long free_mem_ptr = (long)&end; +static long free_mem_end_ptr = 0x90000; + +#define INPLACE_MOVE_ROUTINE 0x1000 +#define LOW_BUFFER_START 0x2000 +#define LOW_BUFFER_END 0x90000 +#define LOW_BUFFER_SIZE ( LOW_BUFFER_END - LOW_BUFFER_START ) +#define HEAP_SIZE 0x2000 +static int high_loaded =0; +static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; + +static char *vidmem = (char *)0xb8000; +static int vidport; +static int lines, cols; + +#include "../../../../lib/inflate.c" + +static void *malloc(int size) { void *p; if (size <0) error("Malloc error\n"); if (free_mem_ptr <= 0) error("Memory error\n"); - while(1) { free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ p = (void *)free_mem_ptr; free_mem_ptr += size; - /* - * The part of the compressed kernel which has already been expanded - * is no longer needed. Therefore we can reuse it for malloc. - * With bigger kernels, this is necessary. - */ - - if (free_mem_ptr < (long)&end) { - if (free_mem_ptr > (long)&input_data[input_ptr]) - error("\nOut of memory\n"); - - return p; - } - if (free_mem_ptr < 0x90000) + if (free_mem_ptr >= free_mem_end_ptr) + error("\nOut of memory\n"); + return p; - puts("large kernel, low 1M tight..."); - free_mem_ptr = (long)input_data; - } } -void free(void *where) +static void free(void *where) { /* Don't care */ } +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + static void scroll() { int i; @@ -127,7 +179,7 @@ static void scroll() static void puts(const char *s) { - int x,y; + int x,y,pos; char c; x = SCREEN_INFO.orig_x; @@ -154,6 +206,12 @@ static void puts(const char *s) SCREEN_INFO.orig_x = x; SCREEN_INFO.orig_y = y; + + pos = (x + cols * y) * 2; /* Update cursor position */ + outb_p(14, vidport); + outb_p(0xff & (pos >> 9), vidport+1); + outb_p(15, vidport); + outb_p(0xff & (pos >> 1), vidport+1); } __ptr_t memset(__ptr_t s, int c, size_t n) @@ -172,129 +230,69 @@ __ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src, for (i=0;i<__n;i++) d[i] = s[i]; } - -extern ulg crc_32_tab[]; /* crc table, defined below */ - -/* =========================================================================== - * Run a set of bytes through the crc shift register. If s is a NULL - * pointer, then initialize the crc shift register contents instead. - * Return the current crc in either case. - */ -ulg updcrc(s, n) - uch *s; /* pointer to bytes to pump through */ - unsigned n; /* number of bytes in s[] */ -{ - register ulg c; /* temporary variable */ - - static ulg crc = (ulg)0xffffffffL; /* shift register contents */ - - if (s == NULL) { - c = 0xffffffffL; - } else { - c = crc; - while (n--) { - c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8); - } - } - crc = c; - return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */ -} - -/* =========================================================================== - * Clear input and output buffers - */ -void clear_bufs() -{ - outcnt = 0; - insize = inptr = 0; - bytes_in = bytes_out = 0L; -} +#endif /* =========================================================================== * Fill the input buffer. This is called only when the buffer is empty * and at least one byte is really needed. */ -int fill_inbuf() +static int fill_inbuf() { - int len, i; - - /* Read as much as possible */ - insize = 0; - do { - len = INBUFSIZ-insize; - if (len > (input_len-input_ptr+1)) len=input_len-input_ptr+1; - if (len == 0 || len == EOF) break; - - for (i=0;i<len;i++) inbuf[insize+i] = input_data[input_ptr+i]; - insize += len; - input_ptr += len; - } while (insize < INBUFSIZ); - - if (insize == 0) { - error("unable to fill buffer\n"); - } - bytes_in += (ulg)insize; - inptr = 1; - return inbuf[0]; + if (insize != 0) { + error("ran out of input data\n"); + } + + inbuf = input_data; + insize = input_len; + inptr = 1; + return inbuf[0]; } /* =========================================================================== * Write the output window window[0..outcnt-1] and update crc and bytes_out. * (Used for the decompressed data only.) */ -void flush_window() +static void flush_window_low() { - if (outcnt == 0) return; - updcrc(window, outcnt); - - memcpy(&output_data[output_ptr], (char *)window, outcnt); - + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, *out, ch; + + in = window; + out = &output_data[output_ptr]; + for (n = 0; n < outcnt; n++) { + ch = *out++ = *in++; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; bytes_out += (ulg)outcnt; output_ptr += (ulg)outcnt; outcnt = 0; } -/* - * Code to compute the CRC-32 table. Borrowed from - * gzip-1.0.3/makecrc.c. - */ - -ulg crc_32_tab[256]; - -void -makecrc(void) +static void flush_window_high() { -/* Not copyrighted 1990 Mark Adler */ - - unsigned long c; /* crc shift register */ - unsigned long e; /* polynomial exclusive-or pattern */ - int i; /* counter for all possible eight bit values */ - int k; /* byte being shifted into crc apparatus */ - - /* terms of polynomial defining this crc (except x^32): */ - static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; - - /* Make exclusive-or pattern from polynomial */ - e = 0; - for (i = 0; i < sizeof(p)/sizeof(int); i++) - e |= 1L << (31 - p[i]); - - crc_32_tab[0] = 0; - - for (i = 1; i < 256; i++) - { - c = 0; - for (k = i | 256; k != 1; k >>= 1) - { - c = c & 1 ? (c >> 1) ^ e : c >> 1; - if (k & 1) - c ^= e; + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, ch; + in = window; + for (n = 0; n < outcnt; n++) { + ch = *output_data++ = *in++; + if ((ulg)output_data == LOW_BUFFER_END) output_data=high_buffer_start; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); } - crc_32_tab[i] = c; - } + crc = c; + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window() +{ + if (high_loaded) flush_window_high(); + else flush_window_low(); } -void error(char *x) +static void error(char *x) { puts("\n\n"); puts(x); @@ -312,107 +310,96 @@ struct { short b; } stack_start = { & user_stack [STACK_SIZE] , KERNEL_DS }; -void decompress_kernel() -{ - if (SCREEN_INFO.orig_video_mode == 7) - vidmem = (char *) 0xb0000; - else - vidmem = (char *) 0xb8000; +#ifdef STANDALONE_DEBUG - lines = SCREEN_INFO.orig_video_lines; - cols = SCREEN_INFO.orig_video_cols; +static void gzip_mark(void **ptr) +{ +} - if (EXT_MEM_K < 1024) error("<2M of mem\n"); +static void gzip_release(void **ptr) +{ +} - output_data = (char *)0x100000; /* Points to 1M */ - output_ptr = 0; +char output_buffer[1024 * 800]; - exit_code = 0; - test = 0; - input_ptr = 0; - part_nb = 0; +int +main(argc, argv) + int argc; + char **argv; +{ + output_data = output_buffer; - clear_bufs(); makecrc(); - puts("Uncompressing Linux..."); + gunzip(); + puts("done.\n"); + return 0; +} - method = get_method(0); +#else - work(0, 0); +void setup_normal_output_buffer() +{ + if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n"); + output_data = (char *)0x100000; /* Points to 1M */ +} - puts("done.\n"); +struct moveparams { + uch *low_buffer_start; int lcount; + uch *high_buffer_start; int hcount; +}; - puts("Now booting the kernel\n"); +void setup_output_buffer_if_we_run_high(struct moveparams *mv) +{ + high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE); + if (EXT_MEM_K < (3*1024)) error("Less than 4MB of memory.\n"); + mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START; + high_loaded = 1; + free_mem_end_ptr = (long)high_buffer_start; + if ( (0x100000 + LOW_BUFFER_SIZE) > ((ulg)high_buffer_start)) { + high_buffer_start = (uch *)(0x100000 + LOW_BUFFER_SIZE); + mv->hcount = 0; /* say: we need not to move high_buffer */ + } + else mv->hcount = -1; + mv->high_buffer_start = high_buffer_start; } -/* ======================================================================== - * Check the magic number of the input file and update ofname if an - * original name was given and to_stdout is not set. - * Return the compression method, -1 for error, -2 for warning. - * Set inptr to the offset of the next byte to be processed. - * This function may be called repeatedly for an input file consisting - * of several contiguous gzip'ed members. - * IN assertions: there is at least one remaining compressed member. - * If the member is a zip file, it must be the only one. - */ -local int get_method(in) - int in; /* input file descriptor */ +void close_output_buffer_if_we_run_high(struct moveparams *mv) { - uch flags; - char magic[2]; /* magic header */ - - magic[0] = (char)get_byte(); - magic[1] = (char)get_byte(); - - method = -1; /* unknown yet */ - part_nb++; /* number of parts in gzip file */ - last_member = 0; - /* assume multiple members in gzip file except for record oriented I/O */ - - if (memcmp(magic, GZIP_MAGIC, 2) == 0 - || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) { - - work = unzip; - method = (int)get_byte(); - flags = (uch)get_byte(); - if ((flags & ENCRYPTED) != 0) - error("Input is encrypted\n"); - if ((flags & CONTINUATION) != 0) - error("Multi part input\n"); - if ((flags & RESERVED) != 0) { - error("Input has invalid flags\n"); - exit_code = ERROR; - if (force <= 1) return -1; - } - (ulg)get_byte(); /* Get timestamp */ - ((ulg)get_byte()) << 8; - ((ulg)get_byte()) << 16; - ((ulg)get_byte()) << 24; - - (void)get_byte(); /* Ignore extra flags for the moment */ - (void)get_byte(); /* Ignore OS type for the moment */ - - if ((flags & EXTRA_FIELD) != 0) { - unsigned len = (unsigned)get_byte(); - len |= ((unsigned)get_byte())<<8; - while (len--) (void)get_byte(); + mv->lcount = bytes_out; + if (bytes_out > LOW_BUFFER_SIZE) { + mv->lcount = LOW_BUFFER_SIZE; + if (mv->hcount) mv->hcount = bytes_out - LOW_BUFFER_SIZE; } + else mv->hcount = 0; +} - /* Get original file name if it was truncated */ - if ((flags & ORIG_NAME) != 0) { - if (to_stdout || part_nb > 1) { - /* Discard the old name */ - while (get_byte() != 0) /* null */ ; - } else { - } /* to_stdout */ - } /* orig_name */ - - /* Discard file comment if any */ - if ((flags & COMMENT) != 0) { - while (get_byte() != 0) /* null */ ; + +int decompress_kernel(struct moveparams *mv) +{ + if (SCREEN_INFO.orig_video_mode == 7) { + vidmem = (char *) 0xb0000; + vidport = 0x3b4; + } else { + vidmem = (char *) 0xb8000; + vidport = 0x3d4; } - } else - error("unknown compression method"); - return method; + + lines = SCREEN_INFO.orig_video_lines; + cols = SCREEN_INFO.orig_video_cols; + + if (free_mem_ptr < 0x100000) setup_normal_output_buffer(); + else setup_output_buffer_if_we_run_high(mv); + + makecrc(); + puts("Uncompressing Linux..."); + gunzip(); + puts("done.\nNow booting the kernel\n"); + if (high_loaded) close_output_buffer_if_we_run_high(mv); + return high_loaded; } +#endif + + + + diff --git a/arch/i386/boot/compressed/unzip.c b/arch/i386/boot/compressed/unzip.c deleted file mode 100644 index d4a6617cd..000000000 --- a/arch/i386/boot/compressed/unzip.c +++ /dev/null @@ -1,180 +0,0 @@ -/* unzip.c -- decompress files in gzip or pkzip format. - * Copyright (C) 1992-1993 Jean-loup Gailly - * - * Adapted for Linux booting by Hannu Savolainen 1993 - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License, see the file COPYING. - * - * The code in this file is derived from the file funzip.c written - * and put in the public domain by Mark Adler. - */ - -/* - This version can extract files in gzip or pkzip format. - For the latter, only the first entry is extracted, and it has to be - either deflated or stored. - */ - -#ifndef lint -static char rcsid[] = "$Id: unzip.c,v 0.9 1993/02/10 16:07:22 jloup Exp $"; -#endif - -#include "gzip.h" -#include "crypt.h" - -#include <stdio.h> - -/* PKZIP header definitions */ -#define LOCSIG 0x04034b50L /* four-byte lead-in (lsb first) */ -#define LOCFLG 6 /* offset of bit flag */ -#define CRPFLG 1 /* bit for encrypted entry */ -#define EXTFLG 8 /* bit for extended local header */ -#define LOCHOW 8 /* offset of compression method */ -#define LOCTIM 10 /* file mod time (for decryption) */ -#define LOCCRC 14 /* offset of crc */ -#define LOCSIZ 18 /* offset of compressed size */ -#define LOCLEN 22 /* offset of uncompressed length */ -#define LOCFIL 26 /* offset of file name field length */ -#define LOCEXT 28 /* offset of extra field length */ -#define LOCHDR 30 /* size of local header, including sig */ -#define EXTHDR 16 /* size of extended local header, inc sig */ - - -/* Globals */ - -int decrypt; /* flag to turn on decryption */ -char *key; /* not used--needed to link crypt.c */ -int pkzip = 0; /* set for a pkzip file */ -int extended = 0; /* set if extended local header */ - -/* =========================================================================== - * Check zip file and advance inptr to the start of the compressed data. - * Get ofname from the local header if necessary. - */ -int check_zipfile(in) - int in; /* input file descriptors */ -{ - uch *h = inbuf + inptr; /* first local header */ - - /* ifd = in; */ - - /* Check validity of local header, and skip name and extra fields */ - inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT); - - if (inptr > insize || LG(h) != LOCSIG) { - error("input not a zip"); - } - method = h[LOCHOW]; - if (method != STORED && method != DEFLATED) { - error("first entry not deflated or stored--can't extract"); - } - - /* If entry encrypted, decrypt and validate encryption header */ - if ((decrypt = h[LOCFLG] & CRPFLG) != 0) { - error("encrypted file\n"); - exit_code = ERROR; - return -1; - } - - /* Save flags for unzip() */ - extended = (h[LOCFLG] & EXTFLG) != 0; - pkzip = 1; - - /* Get ofname and time stamp from local header (to be done) */ - return 0; -} - -/* =========================================================================== - * Unzip in to out. This routine works on both gzip and pkzip files. - * - * IN assertions: the buffer inbuf contains already the beginning of - * the compressed data, from offsets inptr to insize-1 included. - * The magic header has already been checked. The output buffer is cleared. - */ -void unzip(in, out) - int in, out; /* input and output file descriptors */ -{ - ulg orig_crc = 0; /* original crc */ - ulg orig_len = 0; /* original uncompressed length */ - int n; - uch buf[EXTHDR]; /* extended local header */ - - /* ifd = in; - ofd = out; */ - - updcrc(NULL, 0); /* initialize crc */ - - if (pkzip && !extended) { /* crc and length at the end otherwise */ - orig_crc = LG(inbuf + LOCCRC); - orig_len = LG(inbuf + LOCLEN); - } - - /* Decompress */ - if (method == DEFLATED) { - - int res = inflate(); - - if (res == 3) { - error("out of memory"); - } else if (res != 0) { - error("invalid compressed format"); - } - - } else if (pkzip && method == STORED) { - - register ulg n = LG(inbuf + LOCLEN); - - if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) { - - error("length mismatch"); - } - while (n--) { - uch c = (uch)get_byte(); -#ifdef CRYPT - if (decrypt) zdecode(c); -#endif - if (!test) put_char(c); - } - } else { - error("internal error, invalid method"); - } - - /* Get the crc and original length */ - if (!pkzip) { - /* crc32 (see algorithm.doc) - * uncompressed input size modulo 2^32 - */ - for (n = 0; n < 8; n++) { - buf[n] = (uch)get_byte(); /* may cause an error if EOF */ - } - orig_crc = LG(buf); - orig_len = LG(buf+4); - - } else if (extended) { /* If extended header, check it */ - /* signature - 4bytes: 0x50 0x4b 0x07 0x08 - * CRC-32 value - * compressed size 4-bytes - * uncompressed size 4-bytes - */ - for (n = 0; n < EXTHDR; n++) { - buf[n] = (uch)get_byte(); /* may cause an error if EOF */ - } - orig_crc = LG(buf+4); - orig_len = LG(buf+12); - } - - /* Validate decompression */ - if (orig_crc != updcrc(outbuf, 0)) { - error("crc error"); - } - if (orig_len != bytes_out) { - error("length error"); - } - - /* Check if there are more entries in a pkzip file */ - if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) { - error("zip file has more than one entry"); - } - extended = pkzip = 0; /* for next file */ -} diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index d4f56ef2a..201a2936b 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -17,23 +17,32 @@ ! some changes and additional features by Christoph Niemann, ! March 1993/June 1994 (Christoph.Niemann@linux.org) ! +! add APM BIOS checking by Stephen Rothwell, May 1994 +! (Stephen.Rothwell@pd.necisa.oz.au) +! +! High load stuff, initrd support and position independency +! by Hans Lermen & Werner Almesberger, February 1996 +! <lermen@elserv.ffm.fgan.de>, <almesber@lrc.epfl.ch> +! +! Video handling moved to video.S by Martin Mares, March 1996 +! <mj@k332.feld.cvut.cz> ! NOTE! These had better be the same as in bootsect.s! #define __ASSEMBLY__ #include <linux/config.h> #include <asm/segment.h> - -#ifndef SVGA_MODE -#define SVGA_MODE ASK_VGA -#endif +#include <linux/version.h> +#include <linux/compile.h> ! Signature words to ensure LILO loaded us right #define SIG1 0xAA55 #define SIG2 0x5A5A -INITSEG = DEF_INITSEG ! we move boot here - out of the way -SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536). -SETUPSEG = DEF_SETUPSEG ! this is the current segment +INITSEG = DEF_INITSEG ! 0x9000, we move boot here - out of the way +SYSSEG = DEF_SYSSEG ! 0x1000, system loaded at 0x10000 (65536). +SETUPSEG = DEF_SETUPSEG ! 0x9020, this is the current segment + ! ... and the former contents of CS +DELTA_INITSEG = SETUPSEG - INITSEG ! 0x0020 .globl begtext, begdata, begbss, endtext, enddata, endbss .text @@ -46,14 +55,77 @@ begbss: entry start start: + jmp start_of_setup +! ------------------------ start of header -------------------------------- +! +! SETUP-header, must start at CS:2 (old 0x9020:2) +! + .ascii "HdrS" ! Signature for SETUP-header + .word 0x0201 ! Version number of header format + ! (must be >= 0x0105 + ! else old loadlin-1.5 will fail) +realmode_swtch: .word 0,0 ! default_switch,SETUPSEG +start_sys_seg: .word SYSSEG + .word kernel_version ! pointing to kernel version string + ! note: above part of header is compatible with loadlin-1.5 (header v1.5), + ! must not change it + +type_of_loader: .byte 0 ! = 0, old one (LILO, Loadlin, + ! Bootlin, SYSLX, bootsect...) + ! else it is set by the loader: + ! 0xTV: T=0 for LILO + ! T=1 for Loadlin + ! T=2 for bootsect-loader + ! T=3 for SYSLX + ! T=4 for ETHERBOOT + ! V = version +loadflags: .byte 0 ! unused bits =0 (reserved for future development) +LOADED_HIGH = 1 ! bit within loadflags, + ! if set, then the kernel is loaded high +CAN_USE_HEAP = 0x80 ! if set, the loader also has set heap_end_ptr + ! to tell how much space behind setup.S + | can be used for heap purposes. + ! Only the loader knows what is free! +setup_move_size: .word 0x8000 ! size to move, when we (setup) are not + ! loaded at 0x90000. We will move ourselves + ! to 0x90000 then just before jumping into + ! the kernel. However, only the loader + ! know how much of data behind us also needs + ! to be loaded. +code32_start: .long 0x1000 ! here loaders can put a different + ! start address for 32-bit code. + ! 0x1000 = default for zImage + ! 0x100000 = default for big kernel +ramdisk_image: .long 0 ! address of loaded ramdisk image + ! Here the loader (or kernel generator) puts + ! the 32-bit address were it loaded the image. + ! This only will be interpreted by the kernel. +ramdisk_size: .long 0 ! its size in bytes +bootsect_kludge: + .word bootsect_helper,SETUPSEG +heap_end_ptr: .word modelist+1024 ! space from here (exclusive) down to + ! end of setup code can be used by setup + ! for local heap purposes. +! ------------------------ end of header ---------------------------------- + +start_of_setup: ! Bootlin depends on this being done early mov ax,#0x01500 mov dl,#0x81 int 0x13 -! Check signature at end of setup - mov ax,#SETUPSEG +#ifdef SAFE_RESET_DISK_CONTROLLER +! Reset the disk controller. + mov ax,#0x0000 + mov dl,#0x80 + int 0x13 +#endif + +! set DS=CS, we know that SETUPSEG == CS at this point + mov ax,cs ! aka #SETUPSEG mov ds,ax + +! Check signature at end of setup cmp setup_sig1,#SIG1 jne bad_sig cmp setup_sig2,#SIG2 @@ -65,13 +137,18 @@ start: prtstr: lodsb and al,al jz fin - call prnt1 + call prtchr jmp prtstr fin: ret +! Space printing + +prtsp2: call prtspc ! Print double space +prtspc: mov al,#0x20 ! Print single space (fall-thru!) + ! Part of above routine, this one just prints ascii al -prnt1: push ax +prtchr: push ax push cx xor bh,bh mov cx,#0x01 @@ -82,18 +159,18 @@ prnt1: push ax ret beep: mov al,#0x07 - jmp prnt1 + jmp prtchr no_sig_mess: .ascii "No setup signature found ..." db 0x00 -start_sys_seg: .word SYSSEG good_sig1: jmp good_sig ! We now have to find the rest of the setup code/data bad_sig: - mov ax,#INITSEG + mov ax,cs ! aka #SETUPSEG + sub ax,#DELTA_INITSEG ! aka #INITSEG mov ds,ax xor bh,bh mov bl,[497] ! get setup sects from boot sector @@ -108,14 +185,14 @@ bad_sig: ! Move rest of setup code/data to here mov di,#2048 ! four sectors loaded by LILO sub si,si - mov ax,#SETUPSEG + mov ax,cs ! aka #SETUPSEG mov es,ax mov ax,#SYSSEG mov ds,ax rep movsw - mov ax,#SETUPSEG + mov ax,cs ! aka #SETUPSEG mov ds,ax cmp setup_sig1,#SIG1 jne no_sig @@ -130,58 +207,53 @@ no_sig_loop: jmp no_sig_loop good_sig: - mov ax,#INITSEG + mov ax,cs ! aka #SETUPSEG + sub ax,#DELTA_INITSEG ! aka #INITSEG mov ds,ax +! check if an old loader tries to load a big-kernel + seg cs + test byte ptr loadflags,#LOADED_HIGH ! have we a big kernel ? + jz loader_ok ! NO, no danger even for old loaders + ! YES, we have a big-kernel + seg cs + cmp byte ptr type_of_loader,#0 ! have we one of the new loaders ? + jnz loader_ok ! YES, ok + ! NO, we have an old loader, must give up + push cs + pop ds + lea si,loader_panic_mess + call prtstr + jmp no_sig_loop +loader_panic_mess: + .ascii "Wrong loader, giving up..." + db 0 + +loader_ok: ! Get memory size (extended mem, kB) mov ah,#0x88 int 0x15 mov [2],ax -! set the keyboard repeat rate to the max +! Set the keyboard repeat rate to the max mov ax,#0x0305 xor bx,bx ! clear bx int 0x16 -! check for EGA/VGA and some config parameters +! Check for video adapter and its parameters and allow the +! user to browse video modes. - mov ah,#0x12 - mov bl,#0x10 - int 0x10 - mov [8],ax - mov [10],bx - mov [12],cx - mov ax,#0x5019 - cmp bl,#0x10 - je novga - mov ax,#0x1a00 ! Added check for EGA/VGA discrimination - int 0x10 - mov bx,ax - mov ax,#0x5019 - cmp bl,#0x1a ! 1a means VGA, anything else EGA or lower - jne novga - call chsvga -novga: mov [14],ax - mov ah,#0x03 ! read cursor pos - xor bh,bh ! clear bh - int 0x10 ! save it in known place, con_init fetches - mov [0],dx ! it from 0x90000. - -! Get video-card data: - - mov ah,#0x0f - int 0x10 - mov [4],bx ! bh = display page - mov [6],ax ! al = video mode, ah = window width + call video ! NOTE: we need DS pointing to bootsector ! Get hd0 data xor ax,ax ! clear ax mov ds,ax lds si,[4*0x41] - mov ax,#INITSEG + mov ax,cs ! aka #SETUPSEG + sub ax,#DELTA_INITSEG ! aka #INITSEG push ax mov es,ax mov di,#0x0080 @@ -211,7 +283,8 @@ novga: mov [14],ax cmp ah,#3 je is_disk1 no_disk1: - mov ax,#INITSEG + mov ax,cs ! aka #SETUPSEG + sub ax,#DELTA_INITSEG ! aka #INITSEG mov es,ax mov di,#0x0090 mov cx,#0x10 @@ -221,9 +294,10 @@ no_disk1: stosb is_disk1: -! check for PS/2 pointing device +! Check for PS/2 pointing device - mov ax,#INITSEG + mov ax,cs ! aka #SETUPSEG + sub ax,#DELTA_INITSEG ! aka #INITSEG mov ds,ax mov [0x1ff],#0 ! default is no pointing device int 0x11 ! int 0x11: equipment determination @@ -231,23 +305,88 @@ is_disk1: jz no_psmouse mov [0x1ff],#0xaa ! device present no_psmouse: -! now we want to move to protected mode ... - cli ! no interrupts allowed ! - mov al,#0x80 ! disable NMI for the bootup sequence - out #0x70,al +#ifdef CONFIG_APM +! check for APM BIOS + ! NOTE: DS is pointing to the bootsector + ! + mov [64],#0 ! version == 0 means no APM BIOS + + mov ax,#0x05300 ! APM BIOS installation check + xor bx,bx + int 0x15 + jc done_apm_bios ! error -> no APM BIOS + + cmp bx,#0x0504d ! check for "PM" signature + jne done_apm_bios ! no signature -> no APM BIOS + + mov [64],ax ! record the APM BIOS version + mov [76],cx ! and flags + and cx,#0x02 ! Is 32 bit supported? + je done_apm_bios ! no ... + + mov ax,#0x05304 ! Disconnect first just in case + xor bx,bx + int 0x15 ! ignore return code + + mov ax,#0x05303 ! 32 bit connect + xor bx,bx + int 0x15 + jc no_32_apm_bios ! error + + mov [66],ax ! BIOS code segment + mov [68],ebx ! BIOS entry point offset + mov [72],cx ! BIOS 16 bit code segment + mov [74],dx ! BIOS data segment + mov [78],si ! BIOS code segment length + mov [80],di ! BIOS data segment length + jmp done_apm_bios + +no_32_apm_bios: + and [76], #0xfffd ! remove 32 bit support bit + +done_apm_bios: +#endif + +! Now we want to move to protected mode ... + + seg cs + cmp realmode_swtch,#0 + jz rmodeswtch_normal + seg cs + callf far * realmode_swtch + jmp rmodeswtch_end +rmodeswtch_normal: + push cs + call default_switch +rmodeswtch_end: + +! we get the code32 start address and modify the below 'jmpi' +! (loader may have changed it) + seg cs + mov eax,code32_start + seg cs + mov code32,eax -! first we move the system to its rightful place +! Now we move the system to its rightful place +! ...but we check, if we have a big-kernel. +! in this case we *must* not move it ... + seg cs + test byte ptr loadflags,#LOADED_HIGH + jz do_move0 ! we have a normal low loaded zImage + ! we have a high loaded big kernel + jmp end_move ! ... and we skip moving +do_move0: mov ax,#0x100 ! start of destination segment + mov bp,cs ! aka #SETUPSEG + sub bp,#DELTA_INITSEG ! aka #INITSEG seg cs mov bx,start_sys_seg ! start of source segment cld ! 'direction'=0, movs moves forward do_move: mov es,ax ! destination segment inc ah ! instead of add ax,#0x100 - cmp ax,#0x9000 - jz end_move mov ds,bx ! source segment add bx,#0x100 sub di,di @@ -255,13 +394,58 @@ do_move: mov cx,#0x800 rep movsw - jmp do_move + cmp bx,bp ! we assume start_sys_seg > 0x200, + ! so we will perhaps read one page more then + ! needed, but never overwrite INITSEG because + ! destination is minimum one page below source + jb do_move ! then we load the segment descriptors end_move: - mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-) + mov ax,cs ! aka #SETUPSEG ! right, forgot this at first. didn't work :-) + mov ds,ax + +! If we have our code not at 0x90000, we need to move it there now. +! We also then need to move the params behind it (commandline) +! Because we would overwrite the code on the current IP, we move +! it in two steps, jumping high after the first one. + mov ax,cs + cmp ax,#SETUPSEG + je end_move_self + cli ! make sure we really have interrupts disabled ! + ! because after this the stack should not be used + sub ax,#DELTA_INITSEG ! aka #INITSEG + mov dx,ss + cmp dx,ax + jb move_self_1 + add dx,#INITSEG + sub dx,ax ! this will be SS after the move +move_self_1: mov ds,ax + mov ax,#INITSEG ! real INITSEG + mov es,ax + seg cs + mov cx,setup_move_size + std ! we have to move up, so we use direction down + ! because the areas may overlap + mov di,cx + dec di + mov si,di + sub cx,#move_self_here+0x200 + rep + movsb + jmpi move_self_here,SETUPSEG ! jump to our final place +move_self_here: + mov cx,#move_self_here+0x200 + rep + movsb + mov ax,#SETUPSEG + mov ds,ax + mov ss,dx + ! now we are at the right place +end_move_self: + lidt idt_48 ! load idt with 0,0 lgdt gdt_48 ! load gdt with whatever appropriate @@ -319,7 +503,7 @@ end_move: mov al,#0xFB ! mask all irq's but irq2 which out #0x21,al ! is cascaded -! well, that certainly wasn't fun :-(. Hopefully it works, and we don't +! Well, that certainly wasn't fun :-(. Hopefully it works, and we don't ! need no steenking BIOS anyway (except for the initial loading :-). ! The BIOS-routine wants lots of unnecessary data, and it's less ! "interesting" anyway. This is how REAL programmers do it. @@ -327,17 +511,127 @@ end_move: ! Well, now's the time to actually move into protected mode. To make ! things as simple as possible, we do no register set-up or anything, ! we let the gnu-compiled 32-bit programs do that. We just jump to -! absolute address 0x00000, in 32-bit protected mode. +! absolute address 0x1000 (or the loader supplied one), +! in 32-bit protected mode. ! ! Note that the short jump isn't strictly needed, although there are ! reasons why it might be a good idea. It won't hurt in any case. ! - xor ax,ax - inc ax ! protected mode (PE) bit + mov ax,#1 ! protected mode (PE) bit lmsw ax ! This is it! jmp flush_instr flush_instr: - jmpi 0x1000,KERNEL_CS ! jmp offset 1000 of segment 0x10 (cs) + xor bx,bx ! Flag to indicate a boot + +! NOTE: For high loaded big kernels we need a +! jmpi 0x100000,KERNEL_CS +! +! but we yet haven't reloaded the CS register, so the default size +! of the target offset still is 16 bit. +! However, using an operant prefix (0x66), the CPU will properly +! take our 48 bit far pointer. (INTeL 80386 Programmer's Reference +! Manual, Mixing 16-bit and 32-bit code, page 16-6) + db 0x66,0xea ! prefix + jmpi-opcode +code32: dd 0x1000 ! will be set to 0x100000 for big kernels + dw KERNEL_CS + + +kernel_version: .ascii UTS_RELEASE + .ascii " (" + .ascii LINUX_COMPILE_BY + .ascii "@" + .ascii LINUX_COMPILE_HOST + .ascii ") " + .ascii UTS_VERSION + db 0 + +! This is the default real mode switch routine. +! to be called just before protected mode transition + +default_switch: + cli ! no interrupts allowed ! + mov al,#0x80 ! disable NMI for the bootup sequence + out #0x70,al + retf + +! This routine only gets called, if we get loaded by the simple +! bootsect loader _and_ have a bzImage to load. +! Because there is no place left in the 512 bytes of the boot sector, +! we must emigrate to code space here. +! +bootsect_helper: + seg cs + cmp word ptr bootsect_es,#0 + jnz bootsect_second + seg cs + mov byte ptr type_of_loader,#0x20 + mov ax,es + shr ax,#4 + seg cs + mov byte ptr bootsect_src_base+2,ah + mov ax,es + seg cs + mov bootsect_es,ax + sub ax,#SYSSEG + retf ! nothing else to do for now +bootsect_second: + push cx + push si + push bx + test bx,bx ! 64K full ? + jne bootsect_ex + mov cx,#0x8000 ! full 64K move, INT15 moves words + push cs + pop es + mov si,#bootsect_gdt + mov ax,#0x8700 + int 0x15 + jc bootsect_panic ! this, if INT15 fails + seg cs + mov es,bootsect_es ! we reset es to always point to 0x10000 + seg cs + inc byte ptr bootsect_dst_base+2 +bootsect_ex: + seg cs + mov ah, byte ptr bootsect_dst_base+2 + shl ah,4 ! we now have the number of moved frames in ax + xor al,al + pop bx + pop si + pop cx + retf + +bootsect_gdt: + .word 0,0,0,0 + .word 0,0,0,0 +bootsect_src: + .word 0xffff +bootsect_src_base: + .byte 0,0,1 ! base = 0x010000 + .byte 0x93 ! typbyte + .word 0 ! limit16,base24 =0 +bootsect_dst: + .word 0xffff +bootsect_dst_base: + .byte 0,0,0x10 ! base = 0x100000 + .byte 0x93 ! typbyte + .word 0 ! limit16,base24 =0 + .word 0,0,0,0 ! BIOS CS + .word 0,0,0,0 ! BIOS DS +bootsect_es: + .word 0 + +bootsect_panic: + push cs + pop ds + cld + lea si,bootsect_panic_mess + call prtstr +bootsect_panic_loop: + jmp bootsect_panic_loop +bootsect_panic_mess: + .ascii "INT15 refuses to access high mem, giving up..." + db 0 ! This routine checks that the keyboard command queue is empty ! (after emptying the output buffers) @@ -356,45 +650,6 @@ no_output: test al,#2 ! is input buffer full? jnz empty_8042 ! yes - loop ret -! -! Read a key and return the (US-)ascii code in al, scan code in ah -! -getkey: - xor ah,ah - int 0x16 - ret - -! -! Read a key with a timeout of 30 seconds. The cmos clock is used to get -! the time. -! -getkt: - call gettime - add al,#30 ! wait 30 seconds - cmp al,#60 - jl lminute - sub al,#60 -lminute: - mov cl,al -again: mov ah,#0x01 - int 0x16 - jnz getkey ! key pressed, so get it - call gettime - cmp al,cl - jne again - mov al,#0x20 ! timeout, return default char `space' - ret - -! -! Flush the keyboard buffer -! -flush: mov ah,#0x01 - int 0x16 - jz empty - xor ah,ah - int 0x16 - jmp flush -empty: ret ! ! Read the cmos clock. Return the seconds in al @@ -413,524 +668,30 @@ gettime: ret ! -! Delay is needed after doing i/o +! Delay is needed after doing I/O ! delay: .word 0x00eb ! jmp $+2 ret -! Routine trying to recognize type of SVGA-board present (if any) -! and if it recognize one gives the choices of resolution it offers. -! If one is found the resolution chosen is given by al,ah (rows,cols). - -chsvga: cld - push ds - push cs - mov ax,[0x01fa] - pop ds - mov modesave,ax - mov ax,#0xc000 - mov es,ax - mov ax,modesave - cmp ax,#NORMAL_VGA - je defvga - cmp ax,#EXTENDED_VGA - je vga50 - cmp ax,#ASK_VGA - jne svga - lea si,msg1 - call prtstr - call flush -nokey: call getkt - cmp al,#0x0d ! enter ? - je svga ! yes - svga selection - cmp al,#0x20 ! space ? - je defvga ! no - repeat - call beep - jmp nokey -defvga: mov ax,#0x5019 - pop ds - ret -/* extended vga mode: 80x50 */ -vga50: - mov ax,#0x1112 - xor bl,bl - int 0x10 ! use 8x8 font set (50 lines on VGA) - mov ax,#0x1200 - mov bl,#0x20 - int 0x10 ! use alternate print screen - mov ax,#0x1201 - mov bl,#0x34 - int 0x10 ! turn off cursor emulation - mov ah,#0x01 - mov cx,#0x0607 - int 0x10 ! turn on cursor (scan lines 6 to 7) - pop ds - mov ax,#0x5032 ! return 80x50 - ret -/* extended vga mode: 80x28 */ -vga28: - pop ax ! clean the stack - mov ax,#0x1111 - xor bl,bl - int 0x10 ! use 9x14 fontset (28 lines on VGA) - mov ah, #0x01 - mov cx,#0x0b0c - int 0x10 ! turn on cursor (scan lines 11 to 12) - pop ds - mov ax,#0x501c ! return 80x28 - ret -/* svga modes */ ! -! test for presence of an S3 VGA chip. The algorithm was taken -! from the SuperProbe package of XFree86 1.2.1 -! report bugs to Christoph.Niemann@linux.org +! Descriptor tables ! -svga: cld - mov cx,#0x0f35 ! we store some constants in cl/ch - mov dx,#0x03d4 - movb al,#0x38 - call inidx - mov bh,al ! store current value of CRT-register 0x38 - mov ax,#0x0038 - call outidx ! disable writing to special regs - movb al,cl ! check whether we can write special reg 0x35 - call inidx - movb bl,al ! save the current value of CRT reg 0x35 - andb al,#0xf0 ! clear bits 0-3 - movb ah,al - movb al,cl ! and write it to CRT reg 0x35 - call outidx - call inidx ! now read it back - andb al,ch ! clear the upper 4 bits - jz s3_2 ! the first test failed. But we have a - movb ah,bl ! second chance - mov al,cl - call outidx - jmp s3_1 ! do the other tests -s3_2: mov ax,cx ! load ah with 0xf and al with 0x35 - orb ah,bl ! set the upper 4 bits of ah with the orig value - call outidx ! write ... - call inidx ! ... and reread - andb al,cl ! turn off the upper 4 bits - push ax - movb ah,bl ! restore old value in register 0x35 - movb al,cl - call outidx - pop ax - cmp al,ch ! setting lower 4 bits was successful => bad - je no_s3 ! writing is allowed => this is not an S3 -s3_1: mov ax,#0x4838 ! allow writing to special regs by putting - call outidx ! magic number into CRT-register 0x38 - movb al,cl ! check whether we can write special reg 0x35 - call inidx - movb bl,al - andb al,#0xf0 - movb ah,al - movb al,cl - call outidx - call inidx - andb al,ch - jnz no_s3 ! no, we can't write => no S3 - mov ax,cx - orb ah,bl - call outidx - call inidx - andb al,ch - push ax - movb ah,bl ! restore old value in register 0x35 - movb al,cl - call outidx - pop ax - cmp al,ch - jne no_s31 ! writing not possible => no S3 - movb al,#0x30 - call inidx ! now get the S3 id ... - lea di,idS3 - mov cx,#0x10 - repne - scasb - je no_s31 - lea si,dsc_S3 ! table of descriptions of video modes for BIOS - lea di,mo_S3 ! table of sizes of video modes for my BIOS - movb ah,bh - movb al,#0x38 - call outidx ! restore old value of CRT register 0x38 - br selmod ! go ask for video mode -no_s3: movb al,#0x35 ! restore CRT register 0x35 - movb ah,bl - call outidx -no_s31: movb ah,bh - movb al,#0x38 - call outidx ! restore old value of CRT register 0x38 - - lea si,idati ! Check ATI 'clues' - mov di,#0x31 - mov cx,#0x09 - repe - cmpsb - jne noati - lea si,dscati - lea di,moati - br selmod -noati: mov ax,#0x200f ! Check Ahead 'clues' - mov dx,#0x3ce - out dx,ax - inc dx - in al,dx - cmp al,#0x20 - je isahed - cmp al,#0x21 - jne noahed -isahed: lea si,dscahead - lea di,moahead - br selmod -noahed: mov dx,#0x3c3 ! Check Chips & Tech. 'clues' - in al,dx - or al,#0x10 - out dx,al - mov dx,#0x104 - in al,dx - mov bl,al - mov dx,#0x3c3 - in al,dx - and al,#0xef - out dx,al - cmp bl,[idcandt] - jne nocant - lea si,dsccandt - lea di,mocandt - br selmod -nocant: mov dx,#0x3d4 ! Check Cirrus 'clues' - mov al,#0x0c - out dx,al - inc dx - in al,dx - mov bl,al - xor al,al - out dx,al - dec dx - mov al,#0x1f - out dx,al - inc dx - in al,dx - mov bh,al - xor ah,ah - shl al,#4 - mov cx,ax - mov al,bh - shr al,#4 - add cx,ax - shl cx,#8 - add cx,#6 - mov ax,cx - mov dx,#0x3c4 - out dx,ax - inc dx - in al,dx - and al,al - jnz nocirr - mov al,bh - out dx,al - in al,dx - cmp al,#0x01 - jne nocirr - call rst3d4 - lea si,dsccirrus - lea di,mocirrus - br selmod -rst3d4: mov dx,#0x3d4 - mov al,bl - xor ah,ah - shl ax,#8 - add ax,#0x0c - out dx,ax - ret -nocirr: call rst3d4 ! Check Everex 'clues' - mov ax,#0x7000 - xor bx,bx - int 0x10 - cmp al,#0x70 - jne noevrx - shr dx,#4 - cmp dx,#0x678 - je istrid - cmp dx,#0x236 - je istrid - lea si,dsceverex - lea di,moeverex - br selmod -istrid: lea cx,ev2tri - jmp cx -noevrx: lea si,idgenoa ! Check Genoa 'clues' - xor ax,ax - seg es - mov al,[0x37] - mov di,ax - mov cx,#0x04 - dec si - dec di -l1: inc si - inc di - mov al,(si) - test al,al - jz l2 - seg es - cmp al,(di) -l2: loope l1 - cmp cx,#0x00 - jne nogen - lea si,dscgenoa - lea di,mogenoa - br selmod -nogen: cld - lea si,idoakvga - mov di,#0x08 - mov cx,#0x08 - repe - cmpsb - jne nooak - lea si,dscoakvga - lea di,mooakvga - br selmod -nooak: cld - lea si,idparadise ! Check Paradise 'clues' - mov di,#0x7d - mov cx,#0x04 - repe - cmpsb - jne nopara - lea si,dscparadise - lea di,moparadise - br selmod -nopara: mov dx,#0x3c4 ! Check Trident 'clues' - mov al,#0x0e - out dx,al - inc dx - in al,dx - xchg ah,al - xor al,al - out dx,al - in al,dx - xchg al,ah - mov bl,al ! Strange thing ... in the book this wasn't - and bl,#0x02 ! necessary but it worked on my card which - jz setb2 ! is a trident. Without it the screen goes - and al,#0xfd ! blurred ... - jmp clrb2 ! -setb2: or al,#0x02 ! -clrb2: out dx,al - and ah,#0x0f - cmp ah,#0x02 - jne notrid -ev2tri: lea si,dsctrident - lea di,motrident - jmp selmod -notrid: mov dx,#0x3cd ! Check Tseng 'clues' - in al,dx ! Could things be this simple ! :-) - mov bl,al - mov al,#0x55 - out dx,al - in al,dx - mov ah,al - mov al,bl - out dx,al - cmp ah,#0x55 - jne notsen - lea si,dsctseng - lea di,motseng - jmp selmod -notsen: mov dx,#0x3cc ! Check Video7 'clues' - in al,dx - mov dx,#0x3b4 - and al,#0x01 - jz even7 - mov dx,#0x3d4 -even7: mov al,#0x0c - out dx,al - inc dx - in al,dx - mov bl,al - mov al,#0x55 - out dx,al - in al,dx - dec dx - mov al,#0x1f - out dx,al - inc dx - in al,dx - mov bh,al - dec dx - mov al,#0x0c - out dx,al - inc dx - mov al,bl - out dx,al - mov al,#0x55 - xor al,#0xea - cmp al,bh - jne novid7 - lea si,dscvideo7 - lea di,movideo7 - jmp selmod -novid7: lea si,dsunknown - lea di,mounknown -selmod: xor cx,cx - mov cl,(di) - mov ax,modesave - cmp ax,#ASK_VGA - je askmod - cmp ax,#NORMAL_VGA - je askmod - cmp al,cl - jl gotmode - push si - lea si,msg4 - call prtstr - pop si -askmod: push si - lea si,msg2 - call prtstr - pop si - push si - push cx -tbl: pop bx - push bx - mov al,bl - sub al,cl - call modepr - lodsw - xchg al,ah - call dprnt - xchg ah,al - push ax - mov al,#0x78 - call prnt1 - pop ax - call dprnt - push si - lea si,crlf ! print CR+LF - call prtstr - pop si - loop tbl - pop cx - lea si,msg3 - call prtstr - pop si - add cl,#0x30 - jmp nonum -nonumb: call beep -nonum: call getkey - cmp al,#0x30 ! ascii `0' - jb nonumb - cmp al,#0x3a ! ascii `9' - jbe number - cmp al,#0x61 ! ascii `a' - jb nonumb - cmp al,#0x7a ! ascii `z' - ja nonumb - sub al,#0x27 - cmp al,cl - jae nonumb - sub al,#0x30 - jmp gotmode -number: cmp al,cl - jae nonumb - sub al,#0x30 -gotmode: xor ah,ah - or al,al - beq vga50 - push ax - dec ax - beq vga28 - add di,ax - mov al,(di) - int 0x10 - pop ax - shl ax,#1 - add si,ax - lodsw - pop ds - ret - -! Routine to write al into a VGA-register that is -! accessed via an index register -! -! dx contains the address of the index register -! al contains the index -! ah contains the value to write to the data register (dx + 1) -! -! no registers are changed - -outidx: out dx,al - push ax - mov al,ah - inc dx - out dx,al - dec dx - pop ax - ret -inidx: out dx,al - inc dx - in al,dx - dec dx - ret - -! Routine to print a decimal value on screen, the value to be -! printed is put in al (i.e 0-255). - -dprnt: push ax - push cx - xor ah,ah ! Clear ah - mov cl,#0x0a - idiv cl - cmp al,#0x09 - jbe lt100 - call dprnt - jmp skip10 -lt100: add al,#0x30 - call prnt1 -skip10: mov al,ah - add al,#0x30 - call prnt1 - pop cx - pop ax - ret - -! -! Routine to print the mode number key on screen. Mode numbers -! 0-9 print the ascii values `0' to '9', 10-35 are represented by -! the letters `a' to `z'. This routine prints some spaces around the -! mode no. -! - -modepr: push ax - cmp al,#0x0a - jb digit ! Here is no check for number > 35 - add al,#0x27 -digit: add al,#0x30 - mov modenr, al - push si - lea si, modestring - call prtstr - pop si - pop ax - ret gdt: .word 0,0,0,0 ! dummy .word 0,0,0,0 ! unused - .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb) + .word 0xFFFF ! 4Gb - (0x100000*0x1000 = 4Gb) .word 0x0000 ! base address=0 .word 0x9A00 ! code read/exec - .word 0x00C0 ! granularity=4096, 386 + .word 0x00CF ! granularity=4096, 386 (+5th nibble of limit) - .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb) + .word 0xFFFF ! 4Gb - (0x100000*0x1000 = 4Gb) .word 0x0000 ! base address=0 .word 0x9200 ! data read/write - .word 0x00C0 ! granularity=4096, 386 + .word 0x00CF ! granularity=4096, 386 (+5th nibble of limit) idt_48: .word 0 ! idt limit=0 @@ -940,69 +701,26 @@ gdt_48: .word 0x800 ! gdt limit=2048, 256 GDT entries .word 512+gdt,0x9 ! gdt base = 0X9xxxx -msg1: .ascii "Press <RETURN> to see SVGA-modes available, <SPACE> to continue or wait 30 secs." - db 0x0d, 0x0a, 0x0a, 0x00 -msg2: .ascii "Mode: COLSxROWS:" - db 0x0d, 0x0a, 0x0a, 0x00 -msg3: db 0x0d, 0x0a - .ascii "Choose mode by pressing the corresponding number or letter." -crlf: db 0x0d, 0x0a, 0x00 -msg4: .ascii "You passed an undefined mode number to setup. Please choose a new mode." - db 0x0d, 0x0a, 0x0a, 0x07, 0x00 -modestring: .ascii " " -modenr: db 0x00 ! mode number - .ascii ": " - db 0x00 - -idati: .ascii "761295520" -idcandt: .byte 0xa5 -idgenoa: .byte 0x77, 0x00, 0x99, 0x66 -idparadise: .ascii "VGA=" -idoakvga: .ascii "OAK VGA " -idS3: .byte 0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95 - .byte 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0 - -! Manufacturer: Numofmodes+2: Mode: -! Number of modes is the number of chip-specific svga modes plus the extended -! modes available on any vga (currently 2) - -moati: .byte 0x06, 0x23, 0x33, 0x22, 0x21 -moahead: .byte 0x07, 0x22, 0x23, 0x24, 0x2f, 0x34 -mocandt: .byte 0x04, 0x60, 0x61 -mocirrus: .byte 0x06, 0x1f, 0x20, 0x22, 0x31 -moeverex: .byte 0x0c, 0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40 -mogenoa: .byte 0x0c, 0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78 -moparadise: .byte 0x04, 0x55, 0x54 -motrident: .byte 0x09, 0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a -motseng: .byte 0x07, 0x26, 0x2a, 0x23, 0x24, 0x22 -movideo7: .byte 0x08, 0x40, 0x43, 0x44, 0x41, 0x42, 0x45 -mooakvga: .byte 0x08, 0x00, 0x07, 0x4e, 0x4f, 0x50, 0x51 -mo_S3: .byte 0x04, 0x54, 0x55 -mounknown: .byte 0x02 - -! msb = Cols lsb = Rows: -! The first two modes are standard vga modes available on any vga. -! mode 0 is 80x50 and mode 1 is 80x28 - -dscati: .word 0x5032, 0x501c, 0x8419, 0x842c, 0x641e, 0x6419 -dscahead: .word 0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042 -dsccandt: .word 0x5032, 0x501c, 0x8419, 0x8432 -dsccirrus: .word 0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425 -dsceverex: .word 0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e -dscgenoa: .word 0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b -dscparadise: .word 0x5032, 0x501c, 0x8419, 0x842c -dsctrident: .word 0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c -dsctseng: .word 0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c -dscvideo7: .word 0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c -dscoakvga: .word 0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b -dsc_S3: .word 0x5032, 0x501c, 0x842b, 0x8419 -dsunknown: .word 0x5032, 0x501c -modesave: .word SVGA_MODE - -! This must be last +! +! Include video setup & detection code +! + +#include "video.S" + +! +! Setup signature -- must be last +! + setup_sig1: .word SIG1 setup_sig2: .word SIG2 +! +! After this point, there is some free space which is used by the video mode +! handling code to store the temporary mode table (not used by the kernel). +! + +modelist: + .text endtext: .data diff --git a/arch/i386/boot/tools/build.c b/arch/i386/boot/tools/build.c index 31277a019..a7e9d063f 100644 --- a/arch/i386/boot/tools/build.c +++ b/arch/i386/boot/tools/build.c @@ -18,6 +18,8 @@ /* * Changes by tytso to allow root device specification + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + * Cross compiling fixes by Gertjan van Wingerde, July 1996 */ #include <stdio.h> /* fprintf */ @@ -30,13 +32,20 @@ #include <fcntl.h> #include <linux/a.out.h> #include <linux/config.h> +#include <errno.h> #define MINIX_HEADER 32 #define N_MAGIC_OFFSET 1024 +#ifndef __BFD__ static int GCC_HEADER = sizeof(struct exec); +#endif +#ifdef __BIG_KERNEL__ +#define SYS_SIZE 0xffff +#else #define SYS_SIZE DEF_SYSSIZE +#endif #define DEFAULT_MAJOR_ROOT 0 #define DEFAULT_MINOR_ROOT 0 @@ -48,6 +57,7 @@ static int GCC_HEADER = sizeof(struct exec); #define STRINGIFY(x) #x typedef union { + int i; long l; short s[2]; char b[4]; @@ -64,6 +74,17 @@ long intel_long(long l) return t.l; } +int intel_int(int i) +{ + conv t; + + t.b[0] = i & 0xff; i >>= 8; + t.b[1] = i & 0xff; i >>= 8; + t.b[2] = i & 0xff; i >>= 8; + t.b[3] = i & 0xff; i >>= 8; + return t.i; +} + short intel_short(short l) { conv t; @@ -73,7 +94,7 @@ short intel_short(short l) return t.s[0]; } -void die(char * str) +void die(const char * str) { fprintf(stderr,"%s\n",str); exit(1); @@ -86,10 +107,12 @@ void usage(void) int main(int argc, char ** argv) { - int i,c,id, sz; - unsigned long sys_size; + int i,c,id,sz,tmp_int; + unsigned long sys_size, tmp_long; char buf[1024]; +#ifndef __BFD__ struct exec *ex = (struct exec *)buf; +#endif char major_root, minor_root; struct stat sb; unsigned char setup_sectors; @@ -167,8 +190,27 @@ int main(int argc, char ** argv) if (((long *) buf)[7] != 0) die("Illegal symbol table in 'setup'"); for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c ) +#ifdef __BIG_KERNEL__ + { + if (!i) { + /* Working with memcpy because of alignment constraints + on Sparc - Gertjan */ + memcpy(&tmp_long, &buf[2], sizeof(long)); + if (tmp_long != intel_long(0x53726448) ) + die("Wrong magic in loader header of 'setup'"); + memcpy(&tmp_int, &buf[6], sizeof(int)); + if (tmp_int < intel_int(0x200)) + die("Wrong version of loader header of 'setup'"); + buf[0x11] = 1; /* LOADED_HIGH */ + tmp_long = intel_long(0x100000); + memcpy(&buf[0x14], &tmp_long, sizeof(long)); /* code32_start */ + } +#endif if (write(1,buf,c)!=c) die("Write call failed"); +#ifdef __BIG_KERNEL__ + } +#endif if (c != 0) die("read-error on 'setup'"); close (id); @@ -190,6 +232,7 @@ int main(int argc, char ** argv) if ((id=open(argv[3],O_RDONLY,0))<0) die("Unable to open 'system'"); +#ifndef __BFD__ if (read(id,buf,GCC_HEADER) != GCC_HEADER) die("Unable to read header of 'system'"); if (N_MAGIC(*ex) == ZMAGIC) { @@ -203,6 +246,14 @@ int main(int argc, char ** argv) ex->a_data /1024, ex->a_bss /1024); sz = N_SYMOFF(*ex) - GCC_HEADER + 4; +#else + if (fstat (id, &sb)) { + perror ("fstat"); + die ("Unable to stat 'system'"); + } + sz = sb.st_size; + fprintf (stderr, "System is %d kB\n", sz/1024); +#endif sys_size = (sz + 15) / 16; if (sys_size > SYS_SIZE) die("System is too big"); diff --git a/arch/i386/boot/video.S b/arch/i386/boot/video.S new file mode 100644 index 000000000..8507b7081 --- /dev/null +++ b/arch/i386/boot/video.S @@ -0,0 +1,1839 @@ +! +! Display adapter & video mode setup, version 2.10 (11-Nov-96) +! +! Copyright (C) 1995, 1996 Martin Mares <mj@k332.feld.cvut.cz> +! Based on the original setup.S code (C) Linus Torvalds and Mats Anderson +! + +! Enable autodetection of SVGA adapters and modes +#define CONFIG_VIDEO_SVGA + +! Enable autodetection of VESA modes +#define CONFIG_VIDEO_VESA + +! Enable compacting of mode table +#define CONFIG_VIDEO_COMPACT + +! Retain screen contents when switching modes +#define CONFIG_VIDEO_RETAIN + +! Enable local mode list +#undef CONFIG_VIDEO_LOCAL + +! Force 400 scan lines for standard modes (hack to fix bad behaviour +! of certain broken BIOS'es -- don't use unless needed) +#undef CONFIG_VIDEO_400_HACK + +! A special hack allowing to force specific BIOS mode ID along with specific +! dimensions. Especially useful for certain X-Window graphics mode hacks +! (e.g., 800x600 modes on IBM ThinkPad). +#undef CONFIG_VIDEO_GFX_HACK +#define VIDEO_GFX_BIOS_AX 0x4f02 /* 800x600 on ThinkPad */ +#define VIDEO_GFX_BIOS_BX 0x0102 +#define VIDEO_GFX_DUMMY_RESOLUTION 0x6425 /* 100x37 */ + +! This code uses an extended set of video mode numbers. These include: +! Aliases for standard modes +! NORMAL_VGA (-1) +! EXTENDED_VGA (-2) +! ASK_VGA (-3) +! Video modes numbered by menu position -- NOT RECOMMENDED because of lack +! of compatibility when extending the table. These are between 0x00 and 0xff. +#define VIDEO_FIRST_MENU 0x0000 +! Standard BIOS video modes (BIOS number + 0x0100) +#define VIDEO_FIRST_BIOS 0x0100 +! VESA BIOS video modes (VESA number + 0x0200) +#define VIDEO_FIRST_VESA 0x0200 +! Video7 special modes (BIOS number + 0x0900) +#define VIDEO_FIRST_V7 0x0900 +! Special video modes +#define VIDEO_FIRST_SPECIAL 0x0f00 +#define VIDEO_80x25 0x0f00 +#define VIDEO_8POINT 0x0f01 +#define VIDEO_80x43 0x0f02 +#define VIDEO_80x28 0x0f03 +#define VIDEO_CURRENT_MODE 0x0f04 +#define VIDEO_80x30 0x0f05 +#define VIDEO_80x34 0x0f06 +#define VIDEO_80x60 0x0f07 +#define VIDEO_GFX_HACK 0x0f08 +#define VIDEO_LAST_SPECIAL 0x0f09 +! Video modes given by resolution +#define VIDEO_FIRST_RESOLUTION 0x1000 + +! The "recalculate timings" flag +#define VIDEO_RECALC 0x8000 + +! Positions of various video parameters passed to the kernel +#define PARAM_CURSOR_POS 0 +#define PARAM_VIDEO_PAGE 4 +#define PARAM_VIDEO_MODE 6 +#define PARAM_VIDEO_COLS 7 +#define PARAM_VIDEO_EGA_BX 10 +#define PARAM_VIDEO_LINES 14 +#define PARAM_HAVE_VGA 15 +#define PARAM_FONT_POINTS 16 + +! Define DO_STORE according to CONFIG_VIDEO_RETAIN +#ifdef CONFIG_VIDEO_RETAIN +#define DO_STORE call store_screen +#else +#define DO_STORE +#endif /* CONFIG_VIDEO_RETAIN */ + +! +! This is the main entry point called by setup.S +! +! Input: +! DS pointing to the bootsector + +video: push ds ! We use different segments + push ds ! FS contains original DS + pop fs + push cs ! DS is equal to CS + pop ds + push cs ! ES is equal to CS + pop es + xor ax,ax + mov gs,ax ! GS is zero + cld + call basic_detect ! Basic adapter type testing (EGA/VGA/MDA/CGA) +#ifdef CONFIG_VIDEO_SELECT + seg fs ! User-selected video mode + mov ax,[0x01fa] + cmp ax,#ASK_VGA ! Bring up the menu + jz vid2 + call mode_set ! Set the mode + jc vid1 + lea si,badmdt ! Invalid mode ID + call prtstr +vid2: call mode_menu +vid1: +#ifdef CONFIG_VIDEO_RETAIN + call restore_screen ! Restore screen contents +#endif /* CONFIG_VIDEO_RETAIN */ +#endif /* CONFIG_VIDEO_SELECT */ + call mode_params ! Store mode parameters + pop ds ! Restore original DS + ret + +! +! Detect if we have CGA, MDA, EGA or VGA and pass it to the kernel. +! + +basic_detect: + seg fs ! Default is no VGA + movb [PARAM_HAVE_VGA],#0 + + mov ah,#0x12 ! Check EGA/VGA + mov bl,#0x10 + int 0x10 + seg fs + mov [PARAM_VIDEO_EGA_BX],bx ! Used for identification of EGA in the kernel + cmp bl,#0x10 ! No, this is a CGA/MDA/HGA card. + je basret + incb [adapter] + + mov ax,#0x1a00 ! Check for EGA/VGA discrimination + int 0x10 + cmp al,#0x1a ! 1a means VGA, anything else EGA + jne basret + seg fs + incb [PARAM_HAVE_VGA] ! We've detected a VGA + incb [adapter] + +basret: ret + +! +! Store the video mode parameters for later usage by the kernel. +! This is done by asking the BIOS except for the rows/columns +! parameters in the default 80x25 mode -- these are set directly, +! because some very obscure BIOSes supply insane values. +! + +mode_params: + mov ah,#0x03 ! Read cursor position + xor bh,bh + int 0x10 + seg fs + mov [PARAM_CURSOR_POS],dx + + mov ah,#0x0f ! Read page/mode/width + int 0x10 + seg fs + mov [PARAM_VIDEO_PAGE],bx + seg fs + mov [PARAM_VIDEO_MODE],ax ! Video mode and screen width + cmp al,#7 ! MDA/HGA => segment differs + jnz mopar0 + mov [video_segment],#0xb000 +mopar0: seg gs ! Font size + mov ax,[0x485] + seg fs + mov [PARAM_FONT_POINTS],ax ! (valid only on EGA/VGA) + + mov ax,[force_size] ! Forced size? + or ax,ax + jz mopar1 + seg fs + mov [PARAM_VIDEO_COLS],ah + seg fs + mov [PARAM_VIDEO_LINES],al + ret + +mopar1: mov al,#25 + cmpb [adapter],#0 ! If we are on CGA/MDA/HGA, the screen must + jz mopar2 ! have 25 lines. + seg gs ! On EGA/VGA, use the EGA+ BIOS variable + mov al,[0x484] ! containing maximal line number. + inc al +mopar2: seg fs + movb [PARAM_VIDEO_LINES],al + ret + +#ifdef CONFIG_VIDEO_SELECT + +! +! The video mode menu +! + +mode_menu: + lea si,keymsg ! "Return/Space/Timeout" message + call prtstr + call flush +nokey: call getkt + cmp al,#0x0d ! ENTER ? + je listm ! yes - manual mode selection + cmp al,#0x20 ! SPACE ? + je defmd1 ! no - repeat + call beep + jmp nokey +defmd1: ret ! No mode selected => use the 80x25 default + +listm: call mode_table ! We need a mode table to be listed +listm0: lea si,name_bann ! Print adapter name + call prtstr + mov si,[card_name] + or si,si + jnz an2 + mov al,[adapter] + lea si,old_name + or al,al + jz an1 + lea si,ega_name + dec al + jz an1 + lea si,vga_name + jmp an1 +an2: call prtstr + lea si,svga_name +an1: call prtstr + lea si,listhdr ! Table header + call prtstr + mov dl,#0x30 ! DL holds mode number + lea si,modelist +lm1: cmp (si),#ASK_VGA ! End? + jz lm2 + mov al,dl ! Menu selection number + call prtchr + call prtsp2 + lodsw + call prthw ! Mode ID + call prtsp2 + mov al,(si+1) + call prtdec ! Rows + mov al,#0x78 ! 'x' + call prtchr + lodsw + call prtdec ! Columns + mov al,#0x0d ! New line + call prtchr + mov al,#0x0a + call prtchr + inc dl ! Next character + cmp dl,#0x3a + jnz lm1 + mov dl,#0x61 + jmp lm1 + +lm2: lea si,prompt ! Mode prompt + call prtstr + lea di,edit_buf ! Editor buffer +lm3: call getkey + cmp al,#0x0d ! Enter? + jz lment + cmp al,#0x08 ! Backspace? + jz lmbs + cmp al,#0x20 ! Printable? + jc lm3 + cmp di,#edit_buf+4 ! Enough space? + jz lm3 + stosb + call prtchr + jmp lm3 + +lmbs: cmp di,#edit_buf ! Backspace + jz lm3 + dec di + mov al,#0x08 + call prtchr + call prtspc + mov al,#0x08 + call prtchr + jmp lm3 + +lment: movb (di),#0 + lea si,crlft + call prtstr + lea si,edit_buf + cmpb (si),#0 ! Empty string => use default mode + jz lmdef + cmpb (si+1),#0 ! One character => menu selection + jz mnusel + cmp (si),#0x6373 ! "scan" => mode scanning + jnz lmhx + cmp (si+2),#0x6e61 + jz lmscan +lmhx: xor bx,bx ! Else => mode ID in hex +lmhex: lodsb + or al,al + jz lmuse1 + sub al,#0x30 + jc lmbad + cmp al,#10 + jc lmhx1 + sub al,#7 + and al,#0xdf + cmp al,#10 + jc lmbad + cmp al,#16 + jnc lmbad +lmhx1: shl bx,#4 + or bl,al + jmp lmhex +lmuse1: mov ax,bx + jmp lmuse + +mnusel: lodsb ! Menu selection + xor ah,ah + sub al,#0x30 + jc lmbad + cmp al,#10 + jc lmuse + cmp al,#0x61-0x30 + jc lmbad + sub al,#0x61-0x30-10 + cmp al,#36 + jnc lmbad +lmuse: call mode_set + jc lmdef +lmbad: lea si,unknt + call prtstr + br lm2 + +lmscan: cmpb [adapter],#0 ! Scanning supported only on EGA/VGA + jz lmbad + mov [mt_end],#0 ! Scanning of modes: done as new autodetection + movb [scanning],#1 + call mode_table + br listm0 + +lmdef: ret + +! +! Additional parts of mode_set... (relative jumps, you know) +! + +setv7: ! Video7 extended modes + DO_STORE + sub bh,#VIDEO_FIRST_V7>>8 + mov ax,#0x6f05 + int 0x10 + stc + ret + +_setrec: br setrec ! Ugly... + +! +! Aliases for backward compatibility. +! + +setalias: + mov ax,#VIDEO_80x25 + inc bx + jz mode_set + mov al,#VIDEO_8POINT-VIDEO_FIRST_SPECIAL + inc bx + jnz setbad + + ! Fall-thru ! + +! +! Setting of user mode (AX=mode ID) => CF=success +! + +mode_set: + mov bx,ax + cmp ah,#0xff + jz setalias + test ah,#VIDEO_RECALC>>8 + jnz _setrec + cmp ah,#VIDEO_FIRST_RESOLUTION>>8 + jnc setres + cmp ah,#VIDEO_FIRST_SPECIAL>>8 + jz setspc + cmp ah,#VIDEO_FIRST_V7>>8 + jz setv7 + cmp ah,#VIDEO_FIRST_VESA>>8 + jnc setvesa + or ah,ah + jz setmenu + dec ah + jz setbios +setbad: clc + movb [do_restore],#0 ! The screen needn't be restored + ret + +setvesa: + DO_STORE + sub bh,#VIDEO_FIRST_VESA>>8 + mov ax,#0x4f02 ! VESA BIOS mode set call + int 0x10 + cmp ax,#0x004f ! AL=4f if implemented, AH=0 if OK + jnz setbad + stc + ret + +setbios: + DO_STORE + int 0x10 ! Standard BIOS mode set call + push bx + mov ah,#0x0f ! Check if really set + int 0x10 + pop bx + cmp al,bl + jnz setbad + stc + ret + +setspc: xor bh,bh ! Set special mode + cmp bl,#VIDEO_LAST_SPECIAL-VIDEO_FIRST_SPECIAL + jnc setbad + add bx,bx + .word 0xa7ff, spec_inits ! JMP [BX+spec_inits] + +setmenu: + or al,al ! 80x25 is an exception + jz set_80x25 + push bx ! Set mode chosen from menu + call mode_table ! Build the mode table + pop ax + shl ax,#2 + add si,ax + cmp si,di + jnc setbad + mov ax,(si) ! Fetch mode ID +_m_s: jmp mode_set + +setres: + push bx ! Set mode chosen by its resolution + call mode_table + pop bx + xchg bh,bl +setr1: lodsw + cmp ax,#ASK_VGA ! End of the list? + jz setbad + lodsw + cmp ax,bx + jnz setr1 + mov ax,(si-4) ! Fetch mode ID + jmp _m_s + +! +! Recalculate vertical display end registers -- this fixes various +! inconsistencies of extended modes on many adapters. Called when +! the VIDEO_RECALC flag is set in the mode ID. +! + +setrec: sub ah,#VIDEO_RECALC>>8 ! Set the base mode + call mode_set + jnc rct3 + seg gs ! Font size in pixels + mov ax,[0x485] + seg gs ! Number of rows + mov bl,[0x484] + inc bl + mul bl ! Number of visible + dec ax ! scan lines - 1 + mov dx,#0x3d4 + mov bx,ax + mov al,#0x12 ! Lower 8 bits + mov ah,bl + out dx,ax + mov al,#0x07 ! Bits 8 and 9 in the overflow register + call inidx + xchg ah,al + and ah,#0xbd + shr bh,#1 + jnc rct1 + or ah,#0x02 +rct1: shr bh,#1 + jnc rct2 + or ah,#0x40 +rct2: mov al,#0x07 + out dx,ax + stc +rct3: ret + +! +! Table of routines for setting of the special modes. +! + +spec_inits: + .word set_80x25 + .word set_8pixel + .word set_80x43 + .word set_80x28 + .word set_current + .word set_80x30 + .word set_80x34 + .word set_80x60 + .word set_gfx + +! +! Set the 80x25 mode. If already set, do nothing. +! + +set_80x25: + mov [force_size],#0x5019 ! Override possibly broken BIOS vars +use_80x25: +#ifdef CONFIG_VIDEO_400_HACK + mov ax,#0x1202 ! Force 400 scan lines + mov bl,#0x30 + int 0x10 +#else + mov ah,#0x0f ! Get current mode ID + int 0x10 + cmp ax,#0x5007 ! Mode 7 (80x25 mono) is the only one available + jz st80 ! on CGA/MDA/HGA and is also available on EGAM + cmp ax,#0x5003 ! Unknown mode => force 80x25 color + jnz force3 +st80: cmpb [adapter],#0 ! CGA/MDA/HGA => mode 3/7 is always 80x25 + jz set80 + seg gs ! This is EGA+ -- beware of 80x50 etc. + mov al,[0x0484] + or al,al ! Some buggy BIOS'es set 0 rows + jz set80 + cmp al,#24 ! It's hopefully correct + jz set80 +#endif /* CONFIG_VIDEO_400_HACK */ +force3: DO_STORE + mov ax,#0x0003 ! Forced set + int 0x10 +set80: stc + ret + +! +! Set the 80x50/80x43 8-pixel mode. Simple BIOS calls. +! + +set_8pixel: + DO_STORE + call use_80x25 ! The base is 80x25 +set_8pt: + mov ax,#0x1112 ! Use 8x8 font + xor bl,bl + int 0x10 + mov ax,#0x1200 ! Use alternate print screen + mov bl,#0x20 + int 0x10 + mov ax,#0x1201 ! Turn off cursor emulation + mov bl,#0x34 + int 0x10 + mov ah,#0x01 ! Define cursor (scan lines 6 to 7) + mov cx,#0x0607 + int 0x10 +set_current: + stc + ret + +! +! Set the 80x28 mode. This mode works on all VGA's, because it's a standard +! 80x25 mode with 14-point fonts instead of 16-point. +! + +set_80x28: + DO_STORE + call use_80x25 ! The base is 80x25 +set14: mov ax,#0x1111 ! Use 9x14 font + xor bl,bl + int 0x10 + mov ah,#0x01 ! Define cursor (scan lines 11 to 12) + mov cx,#0x0b0c + int 0x10 + stc + ret + +! +! Set the 80x43 mode. This mode is works on all VGA's. +! It's a 350-scanline mode with 8-pixel font. +! + +set_80x43: + DO_STORE + mov ax,#0x1201 ! Set 350 scans + mov bl,#0x30 + int 0x10 + mov ax,#0x0003 ! Reset video mode + int 0x10 + jmp set_8pt ! Use 8-pixel font + +! +! Set the 80x30 mode (all VGA's). 480 scanlines, 16-pixel font. +! + +set_80x30: + call use_80x25 ! Start with real 80x25 + DO_STORE + mov dx,#0x3cc ! Get CRTC port + in al,dx + mov dl,#0xd4 + ror al,#1 ! Mono or color? + jc set48a + mov dl,#0xb4 +set48a: mov ax,#0x0c11 ! Vertical sync end (also unlocks CR0-7) + call outidx + mov ax,#0x0b06 ! Vertical total + call outidx + mov ax,#0x3e07 ! (Vertical) overflow + call outidx + mov ax,#0xea10 ! Vertical sync start + call outidx + mov ax,#0xdf12 ! Vertical display end + call outidx + mov ax,#0xe715 ! Vertical blank start + call outidx + mov ax,#0x0416 ! Vertical blank end + call outidx + push dx + mov dl,#0xcc ! Misc output register (read) + in al,dx + mov dl,#0xc2 ! (write) + and al,#0x0d ! Preserve clock select bits and color bit + or al,#0xe2 ! Set correct sync polarity + out dx,al + pop dx + mov [force_size],#0x501e + stc ! That's all. + ret + +! +! Set the 80x34 mode (all VGA's). 480 scans, 14-pixel font. +! + +set_80x34: + call set_80x30 ! Set 480 scans + call set14 ! And 14-pt font + mov ax,#0xdb12 ! VGA vertical display end + mov [force_size],#0x5022 +setvde: call outidx + stc + ret + +! +! Set the 80x60 mode (all VGA's). 480 scans, 8-pixel font. +! + +set_80x60: + call set_80x30 ! Set 480 scans + call set_8pt ! And 8-pt font + mov ax,#0xdf12 ! VGA vertical display end + mov [force_size],#0x503c + jmp setvde + +! +! Special hack for ThinkPad graphics +! + +set_gfx: +#ifdef CONFIG_VIDEO_GFX_HACK + mov ax,# VIDEO_GFX_BIOS_AX + mov bx,# VIDEO_GFX_BIOS_BX + int 0x10 + mov [force_size],# VIDEO_GFX_DUMMY_RESOLUTION + stc +#endif + ret + +#ifdef CONFIG_VIDEO_RETAIN + +! +! Store screen contents to temporary buffer. +! + +store_screen: + cmpb [do_restore],#0 ! Already stored? + jnz stsr + testb [loadflags],#CAN_USE_HEAP ! Have we space for storing? + jz stsr + push ax + push bx + push [force_size] ! Don't force specific size + mov [force_size],#0 + call mode_params ! Obtain params of current mode + pop [force_size] + + seg fs + mov ah,[PARAM_VIDEO_LINES] + seg fs + mov al,[PARAM_VIDEO_COLS] + mov bx,ax ! BX=dimensions + mul ah + mov cx,ax ! CX=number of characters to store + add ax,ax ! Calculate image size + add ax,#modelist+1024+4 + cmp ax,[heap_end_ptr] + jnc sts1 ! Unfortunately, out of memory + + seg fs ! Store mode params + mov ax,[PARAM_CURSOR_POS] + lea di,modelist+1024 + stosw + mov ax,bx + stosw + + push ds ! Store the screen + mov ds,[video_segment] + xor si,si + rep + movsw + pop ds + incb [do_restore] ! Screen will be restored later +sts1: pop bx + pop ax +stsr: ret + +! +! Restore screen contents from temporary buffer. +! + +restore_screen: + cmpb [do_restore],#0 ! Has the screen been stored? + jz res1 + call mode_params ! Get parameters of current mode + seg fs + mov cl,[PARAM_VIDEO_LINES] + seg fs + mov ch,[PARAM_VIDEO_COLS] + lea si,modelist+1024 ! Screen buffer + lodsw ! Set cursor position + mov dx,ax + cmp dh,cl + jc res2 + mov dh,cl + dec dh +res2: cmp dl,ch + jc res3 + mov dl,ch + dec dl +res3: mov ah,#0x02 + mov bh,#0x00 + int 0x10 + lodsw ! Display size + mov dl,ah ! DL=number of lines + mov ah,#0 ! BX=physical length of orig. line + mov bx,ax + cmp dl,cl ! Too many? + jc res4 + push ax + mov al,dl + sub al,cl + mul bl + add si,ax + add si,ax + pop ax + mov dl,cl +res4: cmp al,ch ! Too wide? + jc res5 + mov al,ch ! AX=width of src. line +res5: mov cl,#0 + xchg cl,ch + mov bp,cx ! BP=width of dest. line + push es + mov es,[video_segment] + xor di,di ! Move the data + add bx,bx ! Convert BX and BP to _bytes_ + add bp,bp +res6: push si + push di + mov cx,ax + rep + movsw + pop di + pop si + add di,bp + add si,bx + dec dl + jnz res6 + pop es ! Done +res1: ret + +#endif /* CONFIG_VIDEO_RETAIN */ + +! +! Write to indexed VGA register (AL=index, AH=data, DX=index reg. port) +! + +outidx: out dx,al + push ax + mov al,ah + inc dx + out dx,al + dec dx + pop ax + ret + +! +! Build the table of video modes (stored after the setup.S code at the +! `modelist' label. Each video mode record looks like: +! .word MODE-ID (our special mode ID (see above)) +! .byte rows (number of rows) +! .byte columns (number of columns) +! Returns address of the end of the table in DI, the end is marked +! with a ASK_VGA ID. +! + +mode_table: + mov di,[mt_end] ! Already filled? + or di,di + jnz mtab1x + lea di,modelist ! Store standard modes: + + mov eax,#VIDEO_80x25 + 0x50190000 ! The 80x25 mode (ALL) + stosd + mov al,[adapter] ! CGA/MDA/HGA -- no more modes + or al,al + jz mtabe + dec al + jnz mtabv + mov eax,#VIDEO_8POINT + 0x502b0000 ! The 80x43 EGA mode + stosd + jmp mtabe +mtab1x: jmp mtab1 + +mtabv: lea si,vga_modes ! All modes for std VGA + mov cx,#vga_modes_end-vga_modes + rep ! I'm unable to use movsw as I don't know how to store a half + movsb ! of the expression above to cx without using explicit shr. + + cmpb [scanning],#0 ! Mode scan requested? + jz mscan1 + call mode_scan +mscan1: + +#ifdef CONFIG_VIDEO_LOCAL + call local_modes +#endif /* CONFIG_VIDEO_LOCAL */ +#ifdef CONFIG_VIDEO_VESA + call vesa_modes ! Detect VESA VGA modes +#endif /* CONFIG_VIDEO_VESA */ +#ifdef CONFIG_VIDEO_SVGA + cmpb [scanning],#0 ! Bypass when scanning + jnz mscan2 + call svga_modes ! Detect SVGA cards & modes +mscan2: +#endif /* CONFIG_VIDEO_SVGA */ + +mtabe: + +#ifdef CONFIG_VIDEO_COMPACT + lea si,modelist ! Compact video mode list if requested. + mov dx,di + mov di,si +cmt1: cmp si,dx ! Scan all modes + jz cmt2 + lea bx,modelist ! Find in previous entries + mov cx,(si+2) +cmt3: cmp si,bx + jz cmt4 + cmp cx,(bx+2) ! Found => don't copy this entry + jz cmt5 + add bx,#4 + jmp cmt3 + +cmt4: movsd ! Copy entry + jmp cmt1 + +cmt5: add si,#4 ! Skip entry + jmp cmt1 + +cmt2: +#endif /* CONFIG_VIDEO_COMPACT */ + + mov (di),#ASK_VGA ! End marker + mov [mt_end],di +mtab1: lea si,modelist ! Returning: SI=mode list, DI=list end +ret0: ret + +! Modes usable on all standard VGAs + +vga_modes: + .word VIDEO_8POINT + .word 0x5032 ! 80x50 + .word VIDEO_80x43 + .word 0x502b ! 80x43 + .word VIDEO_80x28 + .word 0x501c ! 80x28 + .word VIDEO_80x30 + .word 0x501e ! 80x30 + .word VIDEO_80x34 + .word 0x5022 ! 80x34 + .word VIDEO_80x60 + .word 0x503c ! 80x60 +#ifdef CONFIG_VIDEO_GFX_HACK + .word VIDEO_GFX_HACK + .word VIDEO_GFX_DUMMY_RESOLUTION +#endif +vga_modes_end: + +! +! Detect VESA modes. +! + +#ifdef CONFIG_VIDEO_VESA + +vesa_modes: + cmpb [adapter],#2 ! VGA only + jnz ret0 + mov bp,di ! BP=original mode table end + add di,#0x200 ! Buffer space + mov ax,#0x4f00 ! VESA Get card info call + int #0x10 + mov di,bp + cmp ax,#0x004f ! Successful? + jnz ret0 + cmp (di+0x200),#0x4556 + jnz ret0 + cmp (di+0x202),#0x4153 + jnz ret0 + mov [card_name],#vesa_name ! Set name to "VESA VGA" + push gs + lgs si,(di+0x20e) ! GS:SI=mode list + mov cx,#128 ! Iteration limit +vesa1: seg gs ! Get next mode in the list + lodsw + cmp ax,#0xffff ! End of the table? + jz vesar + cmp ax,#0x0080 ! Check validity of mode ID + jc vesa2 + or ah,ah ! Valid ID's are 0x0000-0x007f and 0x0100-0x07ff + jz vesan ! [Certain BIOSes erroneously report 0x80-0xff] + cmp ax,#0x0800 + jnc vesae +vesa2: push cx + mov cx,ax ! Get mode information structure + mov ax,#0x4f01 + int 0x10 + mov bx,cx ! BX=mode number + add bh,#VIDEO_FIRST_VESA>>8 + pop cx + cmp ax,#0x004f + jnz vesan ! Don't report errors (buggy BIOSES :-[ ) + mov al,(di) ! Check capabilities. We require + and al,#0x19 ! a color text mode. + cmp al,#0x09 + jnz vesan + cmp (di+8),#0xb800 ! Standard video memory address required + jnz vesan + testb (di),#2 ! Mode characteristics supplied? + mov (di),bx ! Store mode number + jz vesa3 + xor dx,dx + mov bx,(di+0x12) ! Width + or bh,bh + jnz vesan + mov (di+3),bl + mov ax,(di+0x14) ! Height + or ah,ah + jnz vesan + mov (di+2),al + mul bl + cmp ax,#8193 ! Small enough for Linux console driver? + jnc vesan + jmp vesaok + +vesa3: sub bx,#0x8108 ! This mode has no detailed info specified, + jc vesan ! so it must be a standard VESA mode. + cmp bx,#5 + jnc vesan + mov ax,(bx+vesa_text_mode_table) + mov (di+2),ax +vesaok: add di,#4 ! The mode is valid. Store it. +vesan: loop vesa1 ! Next mode. Limit exceeded => error +vesae: lea si,vesaer + call prtstr + mov di,bp ! Discard already found modes. +vesar: pop gs + ret + +! +! Dimensions of standard VESA text modes +! + +vesa_text_mode_table: + db 60, 80 ! 0108 + db 25, 132 ! 0109 + db 43, 132 ! 010A + db 50, 132 ! 010B + db 60, 132 ! 010C + +#endif /* CONFIG_VIDEO_VESA */ + +! +! Scan for video modes. A bit dirty, but should work. +! + +mode_scan: + mov cx,#0x0100 ! Start with mode 0 +scm1: mov ah,#0 ! Test the mode + mov al,cl + int 0x10 + mov ah,#0x0f + int 0x10 + cmp al,cl + jnz scm2 ! Mode not set + mov dx,#0x3c0 ! Test if it's a text mode + mov al,#0x10 ! Mode bits + call inidx + and al,#0x03 + jnz scm2 + mov dl,#0xce ! Another set of mode bits + mov al,#0x06 + call inidx + shr al,#1 + jc scm2 + mov dl,#0xd4 ! Cursor location + mov al,#0x0f + call inidx + or al,al + jnz scm2 + mov ax,cx ! OK, store the mode + stosw + seg gs ! Number of rows + mov al,[0x484] + inc al + stosb + seg gs ! Number of columns + mov ax,[0x44a] + stosb +scm2: inc cl + jns scm1 + mov ax,#0x0003 ! Return back to mode 3 + int 0x10 + ret + +tstidx: out dx,ax ! OUT DX,AX and inidx +inidx: out dx,al ! Read from indexed VGA register + inc dx ! AL=index, DX=index reg port -> AL=data + in al,dx + dec dx + ret + +! +! Try to detect type of SVGA card and supply (usually approximate) video +! mode table for it. +! + +#ifdef CONFIG_VIDEO_SVGA + +svga_modes: + lea si,svga_table ! Test all known SVGA adapters +dosvga: lodsw + mov bp,ax ! Default mode table + or ax,ax + jz didsv1 + lodsw ! Pointer to test routine + push si + push di + push es + mov bx,#0xc000 + mov es,bx + call ax ! Call test routine + pop es + pop di + pop si + or bp,bp + jz dosvga + mov si,bp ! Found, copy the modes + mov ah,[svga_prefix] +cpsvga: lodsb + or al,al + jz didsv + stosw + movsw + jmp cpsvga + +didsv: mov [card_name],si ! Store pointer to card name +didsv1: ret + +! +! Table of all known SVGA cards. For each card, we store a pointer to +! a table of video modes supported by the card and a pointer to a routine +! used for testing of presence of the card. The video mode table is always +! followed by the name of the card or the chipset. +! + +svga_table: + .word ati_md, ati_test + .word oak_md, oak_test + .word paradise_md, paradise_test + .word realtek_md, realtek_test + .word s3_md, s3_test + .word chips_md, chips_test + .word video7_md, video7_test + .word cirrus5_md, cirrus5_test + .word cirrus6_md, cirrus6_test + .word cirrus1_md, cirrus1_test + .word ahead_md, ahead_test + .word everex_md, everex_test + .word genoa_md, genoa_test + .word trident_md, trident_test + .word tseng_md, tseng_test + .word 0 + +! +! Test routines and mode tables: +! + +! S3 - The test algorithm was taken from the SuperProbe package +! for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org + +s3_test: + mov cx,#0x0f35 ! we store some constants in cl/ch + mov dx,#0x03d4 + movb al,#0x38 + call inidx + mov bh,al ! store current value of CRT-register 0x38 + mov ax,#0x0038 + call outidx ! disable writing to special regs + movb al,cl ! check whether we can write special reg 0x35 + call inidx + movb bl,al ! save the current value of CRT reg 0x35 + andb al,#0xf0 ! clear bits 0-3 + movb ah,al + movb al,cl ! and write it to CRT reg 0x35 + call outidx + call inidx ! now read it back + andb al,ch ! clear the upper 4 bits + jz s3_2 ! the first test failed. But we have a + movb ah,bl ! second chance + mov al,cl + call outidx + jmp s3_1 ! do the other tests +s3_2: mov ax,cx ! load ah with 0xf and al with 0x35 + orb ah,bl ! set the upper 4 bits of ah with the orig value + call outidx ! write ... + call inidx ! ... and reread + andb al,cl ! turn off the upper 4 bits + push ax + movb ah,bl ! restore old value in register 0x35 + movb al,cl + call outidx + pop ax + cmp al,ch ! setting lower 4 bits was successful => bad + je no_s3 ! writing is allowed => this is not an S3 +s3_1: mov ax,#0x4838 ! allow writing to special regs by putting + call outidx ! magic number into CRT-register 0x38 + movb al,cl ! check whether we can write special reg 0x35 + call inidx + movb bl,al + andb al,#0xf0 + movb ah,al + movb al,cl + call outidx + call inidx + andb al,ch + jnz no_s3 ! no, we can't write => no S3 + mov ax,cx + orb ah,bl + call outidx + call inidx + andb al,ch + push ax + movb ah,bl ! restore old value in register 0x35 + movb al,cl + call outidx + pop ax + cmp al,ch + jne no_s31 ! writing not possible => no S3 + movb al,#0x30 + call inidx ! now get the S3 id ... + lea di,idS3 + mov cx,#0x10 + repne + scasb + je no_s31 + movb ah,bh + movb al,#0x38 + jmp s3rest +no_s3: movb al,#0x35 ! restore CRT register 0x35 + movb ah,bl + call outidx +no_s31: xor bp,bp ! Detection failed +s3rest: movb ah,bh + movb al,#0x38 ! restore old value of CRT register 0x38 + br outidx + +idS3: .byte 0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95 + .byte 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0 + +s3_md: .byte 0x54, 0x2b, 0x84 + .byte 0x55, 0x19, 0x84 + .byte 0 + .ascii "S3" + .byte 0 + +! ATI cards. + +ati_test: + lea si,idati + mov di,#0x31 + mov cx,#0x09 + repe + cmpsb + je atiok + xor bp,bp +atiok: ret + +idati: .ascii "761295520" + +ati_md: .byte 0x23, 0x19, 0x84 + .byte 0x33, 0x2c, 0x84 + .byte 0x22, 0x1e, 0x64 + .byte 0x21, 0x19, 0x64 + .byte 0x58, 0x21, 0x50 + .byte 0x5b, 0x1e, 0x50 + .byte 0 + .ascii "ATI" + .byte 0 + +! AHEAD + +ahead_test: + mov ax,#0x200f + mov dx,#0x3ce + out dx,ax + inc dx + in al,dx + cmp al,#0x20 + je isahed + cmp al,#0x21 + je isahed + xor bp,bp +isahed: ret + +ahead_md: + .byte 0x22, 0x2c, 0x84 + .byte 0x23, 0x19, 0x84 + .byte 0x24, 0x1c, 0x84 + .byte 0x2f, 0x32, 0xa0 + .byte 0x32, 0x22, 0x50 + .byte 0x34, 0x42, 0x50 + .byte 0 + .ascii "Ahead" + .byte 0 + +! Chips & Tech. + +chips_test: + mov dx,#0x3c3 + in al,dx + or al,#0x10 + out dx,al + mov dx,#0x104 + in al,dx + mov bl,al + mov dx,#0x3c3 + in al,dx + and al,#0xef + out dx,al + cmp bl,#0xa5 + je cantok + xor bp,bp +cantok: ret + +chips_md: + .byte 0x60, 0x19, 0x84 + .byte 0x61, 0x32, 0x84 + .byte 0 + .ascii "Chips & Technologies" + .byte 0 + +! Cirrus Logic 5X0 + +cirrus1_test: + mov dx,#0x3d4 + mov al,#0x0c + out dx,al + inc dx + in al,dx + mov bl,al + xor al,al + out dx,al + dec dx + mov al,#0x1f + out dx,al + inc dx + in al,dx + mov bh,al + xor ah,ah + shl al,#4 + mov cx,ax + mov al,bh + shr al,#4 + add cx,ax + shl cx,#8 + add cx,#6 + mov ax,cx + mov dx,#0x3c4 + out dx,ax + inc dx + in al,dx + and al,al + jnz nocirr + mov al,bh + out dx,al + in al,dx + cmp al,#0x01 + je iscirr +nocirr: xor bp,bp +iscirr: mov dx,#0x3d4 + mov al,bl + xor ah,ah + shl ax,#8 + add ax,#0x0c + out dx,ax + ret + +cirrus1_md: + .byte 0x1f, 0x19, 0x84 + .byte 0x20, 0x2c, 0x84 + .byte 0x22, 0x1e, 0x84 + .byte 0x31, 0x25, 0x64 + .byte 0 + .ascii "Cirrus Logic 5X0" + .byte 0 + +! Cirrus Logic 54XX + +cirrus5_test: + mov dx,#0x3c4 + mov al,#6 + call inidx + mov bl,al ! BL=backup + mov ax,#6 + call tstidx + cmp al,#0x0f + jne c5fail + mov ax,#0x1206 + call tstidx + cmp al,#0x12 + jne c5fail + mov al,#0x1e + call inidx + mov bh,al + mov ah,bh + and ah,#0xc0 + mov al,#0x1e + call tstidx + and al,#0x3f + jne c5xx + mov al,#0x1e + mov ah,bh + or ah,#0x3f + call tstidx + xor al,#0x3f + and al,#0x3f +c5xx: pushf + mov al,#0x1e + mov ah,bh + out dx,ax + popf + je c5done +c5fail: xor bp,bp +c5done: mov al,#6 + mov ah,bl + out dx,ax + ret + +cirrus5_md: + .byte 0x14, 0x19, 0x84 + .byte 0x54, 0x2b, 0x84 + .byte 0 + .ascii "Cirrus Logic 54XX" + .byte 0 + +! Cirrus Logic 64XX -- no known extra modes, but must be identified, because +! it's misidentified by the Ahead test. + +cirrus6_test: + mov dx,#0x3ce + mov al,#0x0a + call inidx + mov bl,al ! BL=backup + mov ax,#0xce0a + call tstidx + or al,al + jne c2fail + mov ax,#0xec0a + call tstidx + cmp al,#0x01 + jne c2fail + mov al,#0xaa + call inidx ! 4X, 5X, 7X and 8X are valid 64XX chip ID's + shr al,#4 + sub al,#4 + jz c6done + dec al + jz c6done + sub al,#2 + jz c6done + dec al + jz c6done +c2fail: xor bp,bp +c6done: mov al,#0x0a + mov ah,bl + out dx,ax + ret + +cirrus6_md: + .byte 0 + .ascii "Cirrus Logic 64XX" + .byte 0 + +! Everex / Trident + +everex_test: + mov ax,#0x7000 + xor bx,bx + int 0x10 + cmp al,#0x70 + jne noevrx + shr dx,#4 + cmp dx,#0x678 + je evtrid + cmp dx,#0x236 + jne evrxok +evtrid: lea bp,trident_md +evrxok: ret + +noevrx: xor bp,bp + ret + +everex_md: + .byte 0x03, 0x22, 0x50 + .byte 0x04, 0x3c, 0x50 + .byte 0x07, 0x2b, 0x64 + .byte 0x08, 0x4b, 0x64 + .byte 0x0a, 0x19, 0x84 + .byte 0x0b, 0x2c, 0x84 + .byte 0x16, 0x1e, 0x50 + .byte 0x18, 0x1b, 0x64 + .byte 0x21, 0x40, 0xa0 + .byte 0x40, 0x1e, 0x84 + .byte 0 + .ascii "Everex/Trident" + .byte 0 + +! Genoa. + +genoa_test: + lea si,idgenoa ! Check Genoa 'clues' + xor ax,ax + seg es + mov al,[0x37] + mov di,ax + mov cx,#0x04 + dec si + dec di +l1: inc si + inc di + mov al,(si) + test al,al + jz l2 + seg es + cmp al,(di) +l2: loope l1 + or cx,cx + je isgen + xor bp,bp +isgen: ret + +idgenoa: .byte 0x77, 0x00, 0x99, 0x66 + +genoa_md: + .byte 0x58, 0x20, 0x50 + .byte 0x5a, 0x2a, 0x64 + .byte 0x60, 0x19, 0x84 + .byte 0x61, 0x1d, 0x84 + .byte 0x62, 0x20, 0x84 + .byte 0x63, 0x2c, 0x84 + .byte 0x64, 0x3c, 0x84 + .byte 0x6b, 0x4f, 0x64 + .byte 0x72, 0x3c, 0x50 + .byte 0x74, 0x42, 0x50 + .byte 0x78, 0x4b, 0x64 + .byte 0 + .ascii "Genoa" + .byte 0 + +! OAK + +oak_test: + lea si,idoakvga + mov di,#0x08 + mov cx,#0x08 + repe + cmpsb + je isoak + xor bp,bp +isoak: ret + +idoakvga: .ascii "OAK VGA " + +oak_md: .byte 0x4e, 0x3c, 0x50 + .byte 0x4f, 0x3c, 0x84 + .byte 0x50, 0x19, 0x84 + .byte 0x51, 0x2b, 0x84 + .byte 0 + .ascii "OAK" + .byte 0 + +! WD Paradise. + +paradise_test: + lea si,idparadise + mov di,#0x7d + mov cx,#0x04 + repe + cmpsb + je ispara + xor bp,bp +ispara: ret + +idparadise: .ascii "VGA=" + +paradise_md: + .byte 0x41, 0x22, 0x50 + .byte 0x47, 0x1c, 0x84 + .byte 0x55, 0x19, 0x84 + .byte 0x54, 0x2c, 0x84 + .byte 0 + .ascii "Paradise" + .byte 0 + +! Trident. + +trident_test: + mov dx,#0x3c4 + mov al,#0x0e + out dx,al + inc dx + in al,dx + xchg ah,al + xor al,al + out dx,al + in al,dx + xchg al,ah + mov bl,al ! Strange thing ... in the book this wasn't + and bl,#0x02 ! necessary but it worked on my card which + jz setb2 ! is a trident. Without it the screen goes + and al,#0xfd ! blurred ... + jmp clrb2 ! +setb2: or al,#0x02 ! +clrb2: out dx,al + and ah,#0x0f + cmp ah,#0x02 + je istrid + xor bp,bp +istrid: ret + +trident_md: + .byte 0x50, 0x1e, 0x50 + .byte 0x51, 0x2b, 0x50 + .byte 0x52, 0x3c, 0x50 + .byte 0x57, 0x19, 0x84 + .byte 0x58, 0x1e, 0x84 + .byte 0x59, 0x2b, 0x84 + .byte 0x5a, 0x3c, 0x84 + .byte 0 + .ascii "Trident" + .byte 0 + +! Tseng. + +tseng_test: + mov dx,#0x3cd + in al,dx ! Could things be this simple ! :-) + mov bl,al + mov al,#0x55 + out dx,al + in al,dx + mov ah,al + mov al,bl + out dx,al + cmp ah,#0x55 + je istsen +isnot: xor bp,bp +istsen: ret + +tseng_md: + .byte 0x26, 0x3c, 0x50 + .byte 0x2a, 0x28, 0x64 + .byte 0x23, 0x19, 0x84 + .byte 0x24, 0x1c, 0x84 + .byte 0x22, 0x2c, 0x84 + .byte 0x21, 0x3c, 0x84 + .byte 0 + .ascii "Tseng" + .byte 0 + +! Video7. + +video7_test: + mov dx,#0x3cc + in al,dx + mov dx,#0x3b4 + and al,#0x01 + jz even7 + mov dx,#0x3d4 +even7: mov al,#0x0c + out dx,al + inc dx + in al,dx + mov bl,al + mov al,#0x55 + out dx,al + in al,dx + dec dx + mov al,#0x1f + out dx,al + inc dx + in al,dx + mov bh,al + dec dx + mov al,#0x0c + out dx,al + inc dx + mov al,bl + out dx,al + mov al,#0x55 + xor al,#0xea + cmp al,bh + jne isnot + movb [svga_prefix],#VIDEO_FIRST_V7>>8 ! Use special mode switching + ret + +video7_md: + .byte 0x40, 0x2b, 0x50 + .byte 0x43, 0x3c, 0x50 + .byte 0x44, 0x3c, 0x64 + .byte 0x41, 0x19, 0x84 + .byte 0x42, 0x2c, 0x84 + .byte 0x45, 0x1c, 0x84 + .byte 0 + .ascii "Video 7" + .byte 0 + +! Realtek VGA + +realtek_test: + lea si,idrtvga + mov di,#0x45 + mov cx,#0x0b + repe + cmpsb + je isrt + xor bp,bp +isrt: ret + +idrtvga: .ascii "REALTEK VGA" + +realtek_md: + .byte 0x1a, 0x3c, 0x50 + .byte 0x1b, 0x19, 0x84 + .byte 0x1c, 0x1e, 0x84 + .byte 0x1d, 0x2b, 0x84 + .byte 0x1e, 0x3c, 0x84 + .byte 0 + .ascii "REALTEK" + .byte 0 + +#endif /* CONFIG_VIDEO_SVGA */ + +! +! User-defined local mode table (VGA only) +! + +#ifdef CONFIG_VIDEO_LOCAL + +local_modes: + lea si,local_mode_table +locm1: lodsw + or ax,ax + jz locm2 + stosw + movsw + jmp locm1 +locm2: ret + +! This is the table of local video modes which can be supplied manually +! by the user. Each entry consists of mode ID (word) and dimensions +! (byte for column count and another byte for row count). These modes +! are placed before all SVGA and VESA modes and override them if table +! compacting is enabled. The table must end with a zero word followed +! by NUL-terminated video adapter name. + +local_mode_table: + .word 0x0100 ! Example: 40x25 + .byte 25,40 + .word 0 + .ascii "Local" + .byte 0 + +#endif /* CONFIG_VIDEO_LOCAL */ + +! +! Read a key and return the ASCII code in al, scan code in ah +! + +getkey: xor ah,ah + int 0x16 + ret + +! +! Read a key with a timeout of 30 seconds. The hardware clock is used to get +! the time. +! + +getkt: call gettime + add al,#30 ! Wait 30 seconds + cmp al,#60 + jl lminute + sub al,#60 +lminute: + mov cl,al +again: mov ah,#0x01 + int 0x16 + jnz getkey ! key pressed, so get it + call gettime + cmp al,cl + jne again + mov al,#0x20 ! timeout, return default char `space' + ret + +! +! Flush the keyboard buffer +! + +flush: mov ah,#0x01 + int 0x16 + jz empty + xor ah,ah + int 0x16 + jmp flush +empty: ret + +! +! Print hexadecimal number. +! + +prthw: push ax + mov al,ah + call prthb + pop ax +prthb: push ax + shr al,#4 + call prthn + pop ax + and al,#0x0f +prthn: cmp al,#0x0a + jc prth1 + add al,#0x07 +prth1: add al,#0x30 + br prtchr + +! +! Print decimal number (AL). +! + +prtdec: push ax + push cx + xor ah,ah ! Clear ah + mov cl,#0x0a + idiv cl + cmp al,#0x09 + jbe lt100 + call prtdec + jmp skip10 +lt100: add al,#0x30 + call prtchr +skip10: mov al,ah + add al,#0x30 + call prtchr + pop cx + pop ax + ret + +! +! VIDEO_SELECT-only variables +! + +mt_end: .word 0 ! End of video mode table if built +edit_buf: .space 6 ! Line editor buffer +card_name: .word 0 ! Pointer to adapter name +scanning: .byte 0 ! Performing mode scan +do_restore: .byte 0 ! Screen contents altered during mode change +svga_prefix: .byte VIDEO_FIRST_BIOS>>8 ! Default prefix for BIOS modes + +! +! Messages: +! + +keymsg: .ascii "Press <RETURN> to see video modes available, " + .ascii "<SPACE> to continue or wait 30 secs" + db 0x0d, 0x0a, 0 +listhdr: db 0x0d, 0x0a + .ascii "Mode: COLSxROWS:" +crlft: db 0x0d, 0x0a, 0 +prompt: db 0x0d, 0x0a + .ascii "Enter mode number: " + db 0 +unknt: .ascii "Unknown mode ID. Try again." + db 0 +badmdt: .ascii "You passed an undefined mode number to setup." + db 0x0d, 0x0a, 0 +vesaer: .ascii "Error: Scanning of VESA modes failed. Please " + .ascii "report to <mj@k332.feld.cvut.cz>." + db 0x0d, 0x0a, 0 +old_name: .ascii "CGA/MDA/HGA" + db 0 +ega_name: .ascii "EGA" + db 0 +svga_name: .ascii " " +vga_name: .ascii "VGA" + db 0 +vesa_name: .ascii "VESA" + db 0 +name_bann: .ascii "Video adapter: " + db 0 + +#endif /* CONFIG_VIDEO_SELECT */ + +! +! Other variables: +! + +adapter: .byte 0 ! Video adapter: 0=CGA/MDA/HGA,1=EGA,2=VGA +video_segment: .word 0xb800 ! Video memory segment +force_size: .word 0 ! Use this size instead of the one in BIOS vars diff --git a/arch/i386/config.in b/arch/i386/config.in index bfbf42acb..a5b72a4d0 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -2,293 +2,113 @@ # For a description of the syntax of this configuration file, # see the Configure script. # +mainmenu_name "Linux Kernel Configuration" -comment 'General setup' - -bool 'Kernel math emulation' CONFIG_MATH_EMULATION n -bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y -bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 y -if [ "$CONFIG_ST506" = "y" ]; then - comment 'Please see drivers/block/README.ide for help/info on IDE drives' - bool ' Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD n - if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then - bool ' Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n - else - bool ' Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE y - fi - if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then - bool ' Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD n - fi -fi - -bool 'XT harddisk support' CONFIG_BLK_DEV_XD n -bool 'Networking support' CONFIG_NET y -bool 'Limit memory to low 16MB' CONFIG_MAX_16M n -bool 'PCI bios support' CONFIG_PCI y -if [ "$CONFIG_PCI" = "y" ]; then - bool ' PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE y -fi -bool 'System V IPC' CONFIG_SYSVIPC y -bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y -#bool 'Use -mpentium flag for Pentium-specific optimizations' CONFIG_M586 n -#if [ "$CONFIG_M586" = "n" ]; then -bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y -#fi +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu +mainmenu_option next_comment comment 'Loadable module support' -bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n - -if [ "$CONFIG_NET" = "y" ]; then -comment 'Networking options' -bool 'TCP/IP networking' CONFIG_INET y -if [ "$CONFIG_INET" = "y" ]; then -bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD n -bool 'IP: multicasting' CONFIG_IP_MULTICAST n -bool 'IP: firewalling' CONFIG_IP_FIREWALL n -bool 'IP: accounting' CONFIG_IP_ACCT n -bool 'IP: tunneling' CONFIG_NET_IPIP n -if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then - bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE y - bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE n -fi -comment '(it is safe to leave these untouched)' -bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP n -bool 'IP: Reverse ARP' CONFIG_INET_RARP n -bool 'IP: Assume subnets are local' CONFIG_INET_SNARL y -bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n -bool 'IP: Drop source routed frames' CONFIG_IP_NOSR y -fi -bool 'The IPX protocol' CONFIG_IPX n -bool 'Appletalk DDP' CONFIG_ATALK n -bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n -if [ "$CONFIG_AX25" = "y" ]; then - bool 'Amateur Radio NET/ROM' CONFIG_NETROM n -fi +bool 'Enable loadable module support' CONFIG_MODULES +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS + bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD fi +endmenu -comment 'SCSI support' - -bool 'SCSI support?' CONFIG_SCSI y - -if [ "$CONFIG_SCSI" = "n" ]; then - -comment 'Skipping SCSI configuration options...' - -else - -comment 'SCSI support type (disk, tape, CDrom)' - -bool 'SCSI disk support' CONFIG_BLK_DEV_SD y -bool 'SCSI tape support' CONFIG_CHR_DEV_ST n -bool 'SCSI CDROM support' CONFIG_BLK_DEV_SR y -bool 'SCSI generic support' CONFIG_CHR_DEV_SG n - -comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' - -bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN n - -comment 'SCSI low-level drivers' +mainmenu_option next_comment +comment 'General setup' -bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y -bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 n -bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y -bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X n -bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n -bool 'EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support' CONFIG_SCSI_EATA_DMA n -bool 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n -bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n -bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n +bool 'Kernel math emulation' CONFIG_MATH_EMULATION +bool 'Networking support' CONFIG_NET +bool 'Limit memory to low 16MB' CONFIG_MAX_16M +bool 'PCI bios support' CONFIG_PCI if [ "$CONFIG_PCI" = "y" ]; then - bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE + fi fi -bool 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 n -bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n -bool 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC n -bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n -bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n -bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n -bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n -#bool 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n -#bool 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n +bool 'System V IPC' CONFIG_SYSVIPC +tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA fi +choice 'Processor type' \ + "386 CONFIG_M386 \ + 486 CONFIG_M486 \ + Pentium CONFIG_M586 \ + PPro CONFIG_M686" Pentium +bool 'Video mode selection support' CONFIG_VIDEO_SELECT +endmenu -if [ "$CONFIG_NET" = "y" ]; then +source drivers/block/Config.in -comment 'Network device support' +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi -bool 'Network device support?' CONFIG_NETDEVICES y -if [ "$CONFIG_NETDEVICES" = "n" ]; then +mainmenu_option next_comment +comment 'SCSI support' -comment 'Skipping network driver configuration options...' +tristate 'SCSI support' CONFIG_SCSI -else -bool 'Dummy net driver support' CONFIG_DUMMY y -bool 'SLIP (serial line) support' CONFIG_SLIP n -if [ "$CONFIG_SLIP" = "y" ]; then - bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y - bool ' 16 channels instead of 4' SL_SLIP_LOTS n -fi -bool 'PPP (point-to-point) support' CONFIG_PPP n -if [ "$CONFIG_PPP" = "y" ]; then - bool ' 16 channels instead of 4' CONFIG_PPP_LOTS n -fi -if [ "$CONFIG_AX25" = "y" ]; then - bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC y -fi -bool 'PLIP (parallel port) support' CONFIG_PLIP n -bool 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n -bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n -bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n -if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then - bool 'WD80*3 support' CONFIG_WD80x3 n - bool 'SMC Ultra support' CONFIG_ULTRA n -fi -bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n -bool '3COM cards' CONFIG_NET_VENDOR_3COM y -if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then - bool '3c501 support' CONFIG_EL1 n - bool '3c503 support' CONFIG_EL2 n - if [ "$CONFIG_NET_ALPHA" = "y" ]; then - bool '3c505 support' CONFIG_ELPLUS n - bool '3c507 support' CONFIG_EL16 n - fi - bool '3c509/3c579 support' CONFIG_EL3 y -fi -bool 'Other ISA cards' CONFIG_NET_ISA n -if [ "$CONFIG_NET_ISA" = "y" ]; then - bool 'Arcnet support' CONFIG_ARCNET n - bool 'Cabletron E21xx support' CONFIG_E2100 n - bool 'DEPCA support' CONFIG_DEPCA n - bool 'EtherWorks 3 support' CONFIG_EWRK3 n - if [ "$CONFIG_NET_ALPHA" = "y" ]; then - bool 'AT1700 support' CONFIG_AT1700 n -# bool 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n - bool 'EtherExpress support' CONFIG_EEXPRESS n - bool 'NI5210 support' CONFIG_NI52 n - bool 'NI6510 support' CONFIG_NI65 n - bool 'WaveLAN support' CONFIG_WAVELAN n - fi - bool 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS n - bool 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN n - bool 'NE2000/NE1000 support' CONFIG_NE2000 y - if [ "$CONFIG_AX25" = "y" ]; then - bool 'Ottawa PI and PI/2 support' CONFIG_PI y - fi - bool 'SK_G16 support' CONFIG_SK_G16 n -fi -bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA n -if [ "$CONFIG_NET_EISA" = "y" ]; then - if [ "$CONFIG_NET_ALPHA" = "y" ]; then - bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n - fi - bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n - bool 'DE425, DE434, DE435 support' CONFIG_DE4X5 n -# bool 'DEC 21040 PCI support' CONFIG_DEC_ELCP n -# bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n -# bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n - bool 'Zenith Z-Note support' CONFIG_ZNET y -fi -bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n -if [ "$CONFIG_NET_POCKET" = "y" ]; then - bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n - bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n - bool 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n -# bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA n -# bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN n -# bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 n -fi -bool 'Token Ring driver support' CONFIG_TR n -if [ "$CONFIG_TR" = "y" ]; then - bool 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR y -fi -fi +if [ "$CONFIG_SCSI" != "n" ]; then + source drivers/scsi/Config.in fi +endmenu -comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' +if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' -bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n -bool 'Mitsumi (not IDE/ATAPI) CDROM driver support' CONFIG_MCD n -bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n -if [ "$CONFIG_SBPCD" = "y" ]; then - bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n - if [ "$CONFIG_SBPCD2" = "y" ]; then - bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n - if [ "$CONFIG_SBPCD3" = "y" ]; then - bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n - fi + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + source drivers/net/Config.in fi + endmenu fi -bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n -bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n -comment 'Filesystems' +mainmenu_option next_comment +comment 'ISDN subsystem' -bool 'Standard (minix) fs support' CONFIG_MINIX_FS y -bool 'Extended fs support' CONFIG_EXT_FS n -bool 'Second extended fs support' CONFIG_EXT2_FS y -bool 'xiafs filesystem support' CONFIG_XIA_FS n -bool 'msdos fs support' CONFIG_MSDOS_FS y -if [ "$CONFIG_MSDOS_FS" = "y" ]; then -#bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n -comment 'Umsdos is not supported in 1.3.0: wait for 1.3.1' +tristate 'ISDN support' CONFIG_ISDN +if [ "$CONFIG_ISDN" != "n" ]; then + source drivers/isdn/Config.in fi -bool '/proc filesystem support' CONFIG_PROC_FS y -if [ "$CONFIG_INET" = "y" ]; then -bool 'NFS filesystem support' CONFIG_NFS_FS y -fi -if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_AZTCD" = "y" -o "$CONFIG_CDU535" = "y" ]; then - bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y -else - bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n -fi -bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n -bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n +endmenu -comment 'character devices' +mainmenu_option next_comment +comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' -bool 'Cyclades async mux support' CONFIG_CYCLADES n -bool 'Parallel printer support' CONFIG_PRINTER n -bool 'Logitech busmouse support' CONFIG_BUSMOUSE n -bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n -if [ "$CONFIG_PSMOUSE" = "y" ]; then -bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y +bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI +if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/cdrom/Config.in fi -bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n -bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n - +endmenu -bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n -if [ "$CONFIG_QIC02_TAPE" = "y" ]; then -bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y -if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then +source fs/Config.in -comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!' - -else - -comment '>>> Setting runtime QIC-02 configuration is done with qic02conf' -comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/' - -fi -fi - -bool 'QIC-117 tape support' CONFIG_FTAPE n -if [ "$CONFIG_FTAPE" = "y" ]; then -int ' number of ftape buffers' NR_FTAPE_BUFFERS 3 -fi +source drivers/char/Config.in +mainmenu_option next_comment comment 'Sound' -bool 'Sound card support' CONFIG_SOUND n +tristate 'Sound card support' CONFIG_SOUND +if [ "$CONFIG_SOUND" != "n" ]; then + source drivers/sound/Config.in +fi +endmenu +mainmenu_option next_comment comment 'Kernel hacking' -#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n -bool 'Kernel profiling support' CONFIG_PROFILE y +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi -if [ "$CONFIG_SCSI" = "y" ]; then -bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y -fi +endmenu diff --git a/arch/i386/defconfig b/arch/i386/defconfig new file mode 100644 index 000000000..0e30c600a --- /dev/null +++ b/arch/i386/defconfig @@ -0,0 +1,176 @@ +# +# Automatically generated make config: don't edit +# + +# +# Code maturity level options +# +# CONFIG_EXPERIMENTAL is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KERNELD is not set + +# +# General setup +# +# CONFIG_MATH_EMULATION is not set +CONFIG_NET=y +# CONFIG_MAX_16M is not set +CONFIG_PCI=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +# CONFIG_M386 is not set +# CONFIG_M486 is not set +CONFIG_M586=y +# CONFIG_M686 is not set +# CONFIG_VIDEO_SELECT is not set + +# +# Floppy, IDE, and other block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +CONFIG_BLK_DEV_CMD640=y +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +CONFIG_BLK_DEV_RZ1000=y +# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_IDE_CHIPSETS is not set + +# +# Additional Block Devices +# +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_HD is not set + +# +# Networking options +# +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +CONFIG_INET=y +# CONFIG_IP_FORWARD is not set +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ACCT is not set + +# +# (it is safe to leave these untouched) +# +# CONFIG_INET_PCTCP is not set +# CONFIG_INET_RARP is not set +# CONFIG_NO_PATH_MTU_DISCOVERY is not set +CONFIG_IP_NOSR=y +CONFIG_SKB_LARGE=y + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +CONFIG_DUMMY=m +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_EL1 is not set +# CONFIG_EL2 is not set +CONFIG_EL3=y +# CONFIG_VORTEX is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_EISA is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_NET_RADIO is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +CONFIG_MINIX_FS=y +# CONFIG_EXT_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_XIA_FS is not set +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_VFAT_FS is not set +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +# CONFIG_ROOT_NFS is not set +# CONFIG_SMB_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_UFS_FS is not set + +# +# Character devices +# +CONFIG_SERIAL=y +# CONFIG_DIGI is not set +# CONFIG_CYCLADES is not set +# CONFIG_STALDRV is not set +# CONFIG_RISCOM8 is not set +# CONFIG_PRINTER is not set +# CONFIG_MOUSE is not set +# CONFIG_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_FTAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Kernel hacking +# +# CONFIG_PROFILE is not set diff --git a/arch/i386/ibcs/Makefile b/arch/i386/ibcs/Makefile deleted file mode 100644 index 7b81ff2bc..000000000 --- a/arch/i386/ibcs/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# -# Makefile for the iBCS emulator files -# -# 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). -# -# Note 2! The CFLAGS definitions are now in the main makefile... - -.S.s: - $(CPP) -traditional $< -o $*.s -.c.s: - $(CC) $(CFLAGS) -S $< -.s.o: - $(AS) -c -o $*.o $< -.c.o: - $(CC) $(CFLAGS) -c $< - -SUBDIRS = - -OBJS = emulate.o - -ibcs.o: $(OBJS) - $(LD) -r -o ibcs.o $(OBJS) - sync - -dep: - $(CPP) -M *.c > .depend - -dummy: - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif diff --git a/arch/i386/ibcs/binfmt_coff.c b/arch/i386/ibcs/binfmt_coff.c deleted file mode 100644 index 1a7c760cd..000000000 --- a/arch/i386/ibcs/binfmt_coff.c +++ /dev/null @@ -1,784 +0,0 @@ -/* - * These are the functions used to load COFF IBSC style executables. - * Information on COFF format may be obtained in either the Intel Binary - * Compatibility Specification 2 or O'Rilley's book on COFF. The shared - * libraries are defined only the in the Intel book. - * - * This file is based upon code written by Eric Youngdale for the ELF object - * file format. - * - * Author: Al Longyear (longyear@sii.com) - * - * Latest Revision: - * 3 February 1994 - * Al Longyear (longyear@sii.com) - * Cleared first page of bss section using put_fs_byte. - */ - -#include <linux/fs.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/mman.h> -#include <linux/a.out.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/binfmts.h> -#include <asm/segment.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> -#include <linux/coff.h> -#include <linux/malloc.h> - -asmlinkage int sys_exit (int exit_code); -asmlinkage int sys_close (unsigned fd); -asmlinkage int sys_open (const char *, int, int); -asmlinkage int sys_uselib(const char * library); - -static int preload_library (struct linux_binprm *exe_bprm, - COFF_SCNHDR * sect, - struct file *fp); - -static int load_object (struct linux_binprm *bprm, - struct pt_regs *regs, - int lib_ok); - -/* - * Small procedure to test for the proper file alignment. - */ - -static inline int -is_properly_aligned (COFF_SCNHDR *sect) -{ - long scnptr = COFF_LONG (sect->s_scnptr); - long vaddr = COFF_LONG (sect->s_vaddr); -/* - * Print the section information if needed - */ - -#ifdef COFF_DEBUG - printk ("%s, scnptr = %d, vaddr = %d\n", - sect->s_name, - scnptr, vaddr); -#endif - -/* - * Return the error code if the section is not properly aligned. - */ - -#ifdef COFF_DEBUG - if (((vaddr - scnptr) & ~PAGE_MASK) != 0) - printk ("bad alignment in %s\n", sect->s_name); -#endif - return ((((vaddr - scnptr) & ~PAGE_MASK) != 0) ? -ENOEXEC : 0); -} - -/* - * Clear the bytes in the last page of data. - */ - -static -int clear_memory (unsigned long addr, unsigned long size) -{ - int status; - - size = (PAGE_SIZE - (addr & ~PAGE_MASK)) & ~PAGE_MASK; - if (size == 0) - status = 0; - else { - -#ifdef COFF_DEBUG - printk ("un-initialized storage in last page %d\n", size); -#endif - - status = verify_area (VERIFY_WRITE, - (void *) addr, size); -#ifdef COFF_DEBUG - printk ("result from verify_area = %d\n", status); -#endif - - if (status >= 0) - while (size-- != 0) - put_fs_byte (0, addr++); - } - return status; -} - -/* - * Helper function to process the load operation. - */ - -static int -load_object (struct linux_binprm * bprm, struct pt_regs *regs, int lib_ok) -{ - COFF_FILHDR *coff_hdr = (COFF_FILHDR *) bprm->buf; /* COFF Header */ - COFF_SCNHDR *sect_bufr; /* Pointer to section table */ - COFF_SCNHDR *text_sect; /* Pointer to the text section */ - COFF_SCNHDR *data_sect; /* Pointer to the data section */ - COFF_SCNHDR *bss_sect; /* Pointer to the bss section */ - int text_count; /* Number of text sections */ - int data_count; /* Number of data sections */ - int bss_count; /* Number of bss sections */ - int lib_count; /* Number of lib sections */ - unsigned int start_addr = 0;/* Starting location for program */ - int status = 0; /* Result status register */ - int fd = -1; /* Open file descriptor */ - struct file *fp = NULL; /* Pointer to the file at "fd" */ - short int sections = 0; /* Number of sections in the file */ - short int aout_size = 0; /* Size of the a.out header area */ - short int flags; /* Flag bits from the COFF header */ - -#ifdef COFF_DEBUG - printk ("binfmt_coff entry: %s\n", bprm->filename); -#endif - -/* - * Validate the magic value for the object file. - */ - do { - if (COFF_I386BADMAG (*coff_hdr)) { -#ifdef COFF_DEBUG - printk ("bad filehdr magic\n"); -#endif - status = -ENOEXEC; - break; - } -/* - * The object file should have 32 BIT little endian format. Do not allow - * it to have the 16 bit object file flag set as Linux is not able to run - * on the 80286/80186/8086. - */ - flags = COFF_SHORT (coff_hdr->f_flags); - if ((flags & (COFF_F_AR32WR | COFF_F_AR16WR)) != COFF_F_AR32WR) { -#ifdef COFF_DEBUG - printk ("invalid f_flags bits\n"); -#endif - status = -ENOEXEC; - break; - } -/* - * Extract the header information which we need. - */ - sections = COFF_SHORT (coff_hdr->f_nscns); /* Number of sections */ - aout_size = COFF_SHORT (coff_hdr->f_opthdr); /* Size of opt. headr */ -/* - * If the file is not executable then reject the execution. This means - * that there must not be external references. - */ - if ((flags & COFF_F_EXEC) == 0) { -#ifdef COFF_DEBUG - printk ("not executable bit\n"); -#endif - status = -ENOEXEC; - break; - } -/* - * There must be at least one section. - */ - if (sections == 0) { -#ifdef COFF_DEBUG - printk ("no sections\n"); -#endif - status = -ENOEXEC; - break; - } -/* - * Do some additional consistency checks. - * The system requires mapping for this loader. If you try - * to use a file system with no mapping, the format is not valid. - */ - if (!bprm->inode->i_op || - !bprm->inode->i_op->default_file_ops->mmap) { -#ifdef COFF_DEBUG - printk ("no mmap in fs\n"); -#endif - status = -ENOEXEC; - } - } - while (0); -/* - * Allocate a buffer to hold the entire coff section list. - */ - if (status >= 0) { - int nbytes = sections * COFF_SCNHSZ; - - sect_bufr = (COFF_SCNHDR *) kmalloc (nbytes, GFP_KERNEL); - if (0 == sect_bufr) { -#ifdef COFF_DEBUG - printk ("kmalloc failed\n"); -#endif - status = -ENOEXEC; - } -/* - * Read the section list from the disk file. - */ - else { - int old_fs = get_fs (); - set_fs (get_ds ()); /* Make it point to the proper location */ - status = read_exec (bprm->inode, /* INODE for file */ - aout_size + COFF_FILHSZ, /* Offset in the file */ - (char *) sect_bufr, /* Buffer for read */ - nbytes); /* Byte count reqd. */ - set_fs (old_fs); /* Restore the selector */ -#ifdef COFF_DEBUG - if (status < 0) - printk ("read aout hdr, status = %d\n", status); -#endif - } - } - else - sect_bufr = NULL; /* Errors do not have a section buffer */ -/* - * Count the number of sections for the required types and store the location - * of the last section for the three primary types. - */ - text_count = 0; - data_count = 0; - bss_count = 0; - lib_count = 0; - - text_sect = NULL; - data_sect = NULL; - bss_sect = NULL; -/* - * Loop through the sections and find the various types - */ - if (status >= 0) { - int nIndex; - COFF_SCNHDR *sect_ptr = sect_bufr; - - for (nIndex = 0; nIndex < sections; ++nIndex) { - long int sect_flags = COFF_LONG (sect_ptr->s_flags); - - switch (sect_flags) { - case COFF_STYP_TEXT: - text_sect = sect_ptr; - ++text_count; - status = is_properly_aligned (sect_ptr); - break; - - case COFF_STYP_DATA: - data_sect = sect_ptr; - ++data_count; - status = is_properly_aligned (sect_ptr); - break; - - case COFF_STYP_BSS: - bss_sect = sect_ptr; - ++bss_count; - break; - - case COFF_STYP_LIB: -#ifdef COFF_DEBUG - printk (".lib section found\n"); -#endif - ++lib_count; - break; - - default: - break; - } - sect_ptr = (COFF_SCNHDR *) & ((char *) sect_ptr)[COFF_SCNHSZ]; - } -/* - * Ensure that there are the required sections. There must be one text - * sections and one each of the data and bss sections for an executable. - * A library may or may not have a data / bss section. - */ - if (text_count != 1) { - status = -ENOEXEC; -#ifdef COFF_DEBUG - printk ("no text sections\n"); -#endif - } - else { - if (lib_ok) { - if (data_count != 1 || bss_count != 1) { - status = -ENOEXEC; -#ifdef COFF_DEBUG - printk ("no .data nor .bss sections\n"); -#endif - } - } - } - } -/* - * If there is no additional header then assume the file starts at - * the first byte of the text section. This may not be the proper place, - * so the best solution is to include the optional header. A shared library - * __MUST__ have an optional header to indicate that it is a shared library. - */ - if (status >= 0) { - if (aout_size == 0) { - if (!lib_ok) { - status = -ENOEXEC; -#ifdef COFF_DEBUG - printk ("no header in library\n"); -#endif - } - start_addr = COFF_LONG (text_sect->s_vaddr); - } -/* - * There is some header. Ensure that it is sufficient. - */ - else { - if (aout_size < COFF_AOUTSZ) { - status = -ENOEXEC; -#ifdef COFF_DEBUG - printk ("header too small\n"); -#endif - } - else { - COFF_AOUTHDR *aout_hdr = /* Pointer to a.out header */ - (COFF_AOUTHDR *) & ((char *) coff_hdr)[COFF_FILHSZ]; - short int aout_magic = COFF_SHORT (aout_hdr->magic); /* id */ -/* - * Validate the magic number in the a.out header. If it is valid then - * update the starting symbol location. Do not accept these file formats - * when loading a shared library. - */ - switch (aout_magic) { - case COFF_OMAGIC: - case COFF_ZMAGIC: - case COFF_STMAGIC: - if (!lib_ok) { - status = -ENOEXEC; -#ifdef COFF_DEBUG - printk ("wrong a.out header magic\n"); -#endif - } - start_addr = (unsigned int) COFF_LONG (aout_hdr->entry); - break; -/* - * Magic value for a shared library. This is valid only when loading a - * shared library. (There is no need for a start_addr. It won't be used.) - */ - case COFF_SHMAGIC: - if (lib_ok) { -#ifdef COFF_DEBUG - printk ("wrong a.out header magic\n"); -#endif - status = -ENOEXEC; - } - break; - - default: -#ifdef COFF_DEBUG - printk ("wrong a.out header magic\n"); -#endif - status = -ENOEXEC; - break; - } - } - } - } -/* - * Fetch a file pointer to the executable. - */ - if (status >= 0) { - fd = open_inode (bprm->inode, O_RDONLY); - if (fd < 0) { -#ifdef COFF_DEBUG - printk ("can not open inode, result = %d\n", fd); -#endif - status = fd; - } - else - fp = current->files->fd[fd]; - } - else - fd = -1; /* Invalidate the open file descriptor */ -/* - * Generate the proper values for the text fields - * - * THIS IS THE POINT OF NO RETURN. THE NEW PROCESS WILL TRAP OUT SHOULD - * SOMETHING FAIL IN THE LOAD SEQUENCE FROM THIS POINT ONWARD. - */ - if (status >= 0) { - long text_scnptr = COFF_LONG (text_sect->s_scnptr); - long text_size = COFF_LONG (text_sect->s_size); - long text_vaddr = COFF_LONG (text_sect->s_vaddr); - - long data_scnptr; - long data_size; - long data_vaddr; - - long bss_size; - long bss_vaddr; -/* - * Generate the proper values for the data fields - */ - if (data_sect != NULL) { - data_scnptr = COFF_LONG (data_sect->s_scnptr); - data_size = COFF_LONG (data_sect->s_size); - data_vaddr = COFF_LONG (data_sect->s_vaddr); - } - else { - data_scnptr = 0; - data_size = 0; - data_vaddr = 0; - } -/* - * Generate the proper values for the bss fields - */ - if (bss_sect != NULL) { - bss_size = COFF_LONG (bss_sect->s_size); - bss_vaddr = COFF_LONG (bss_sect->s_vaddr); - } - else { - bss_size = 0; - bss_vaddr = 0; - } -/* - * Flush the executable from memory. At this point the executable is - * committed to being defined or a segmentation violation will occur. - */ - if (lib_ok) { -#ifdef COFF_DEBUG - printk ("flushing executable\n"); -#endif - flush_old_exec (bprm); -/* - * Define the initial locations for the various items in the new process - */ - current->mm->mmap = NULL; - current->mm->rss = 0; -/* - * Construct the parameter and environment string table entries. - */ - bprm->p += change_ldt (0, bprm->page); - bprm->p -= MAX_ARG_PAGES*PAGE_SIZE; - bprm->p = (unsigned long) create_tables ((char *) bprm->p, - bprm->argc, - bprm->envc, - 1); -/* - * Do the end processing once the stack has been constructed - */ - current->mm->start_code = text_vaddr & PAGE_MASK; - current->mm->end_code = text_vaddr + text_size; - current->mm->end_data = data_vaddr + data_size; - current->mm->start_brk = - current->mm->brk = bss_vaddr + bss_size; - current->suid = - current->euid = bprm->e_uid; - current->sgid = - current->egid = bprm->e_gid; - current->executable = bprm->inode; /* Store inode for file */ - ++bprm->inode->i_count; /* Count the open inode */ - regs->eip = start_addr; /* Current EIP register */ - regs->esp = - current->mm->start_stack = bprm->p; - } -/* - * Map the text pages - */ - -#ifdef COFF_DEBUG - printk (".text: vaddr = %d, size = %d, scnptr = %d\n", - text_vaddr, - text_size, - text_scnptr); -#endif - status = do_mmap (fp, - text_vaddr & PAGE_MASK, - text_size + (text_vaddr & ~PAGE_MASK), - PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_SHARED, - text_scnptr & PAGE_MASK); - - status = (status == (text_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC; -/* - * Map the data pages - */ - if (status >= 0 && data_size != 0) { -#ifdef COFF_DEBUG - printk (".data: vaddr = %d, size = %d, scnptr = %d\n", - data_vaddr, - data_size, - data_scnptr); -#endif - status = do_mmap (fp, - data_vaddr & PAGE_MASK, - data_size + (data_vaddr & ~PAGE_MASK), - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, - data_scnptr & PAGE_MASK); - - status = (status == (data_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC; - } -/* - * Construct the bss data for the process. The bss ranges from the - * end of the data (which may not be on a page boundary) to the end - * of the bss section. Allocate any necessary pages for the data. - */ - if (status >= 0 && bss_size != 0) { -#ifdef COFF_DEBUG - printk (".bss: vaddr = %d, size = %d\n", - bss_vaddr, - bss_size); -#endif - zeromap_page_range (PAGE_ALIGN (bss_vaddr), - PAGE_ALIGN (bss_size), - PAGE_COPY); - - status = clear_memory (bss_vaddr, bss_size); - } -/* - * Load any shared library for the executable. - */ - if (status >= 0 && lib_ok && lib_count != 0) { - int nIndex; - COFF_SCNHDR *sect_ptr = sect_bufr; -/* - * Find the library sections. (There should be at least one. It was counted - * earlier.) This will eventually recurse to our code and load the shared - * library with our own procedures. - */ - for (nIndex = 0; nIndex < sections; ++nIndex) { - long int sect_flags = COFF_LONG (sect_ptr->s_flags); - if (sect_flags == COFF_STYP_LIB) { - status = preload_library (bprm, sect_ptr, fp); - if (status != 0) - break; - } - sect_ptr = (COFF_SCNHDR *) &((char *) sect_ptr) [COFF_SCNHSZ]; - } - } -/* - * Generate any needed trap for this process. If an error occurred then - * generate a segmentation violation. If the process is being debugged - * then generate the load trap. (Note: If this is a library load then - * do not generate the trap here. Pass the error to the caller who - * will do it for the process in the outer lay of this procedure call.) - */ - if (lib_ok) { - if (status < 0) - send_sig (SIGSEGV, current, 0); /* Generate the error trap */ - else { - if (current->flags & PF_PTRACED) - send_sig (SIGTRAP, current, 0); - } - status = 0; /* We are committed. It can't fail */ - } - } -/* - * Do any cleanup processing - */ - if (fd >= 0) - sys_close (fd); /* Close unused code file */ - - if (sect_bufr != NULL) - kfree (sect_bufr); /* Release section list buffer */ -/* - * Return the completion status. - */ -#ifdef COFF_DEBUG - printk ("binfmt_coff: result = %d\n", status); -#endif - return (status); -} - -/* - * This procedure will load the library listed in the file name given - * as the parameter. The result will be non-zero should something fail - * to load. - */ - -static int -preload_this_library (struct linux_binprm *exe_bprm, char *lib_name) -{ - int status; - int old_fs = get_fs(); -/* - * If debugging then print "we have arrived" - */ -#ifdef COFF_DEBUG - printk ("%s loading shared library %s\n", - exe_bprm->filename, - lib_name); -#endif -/* - * Change the FS register to the proper kernel address space and attempt - * to load the library. The library name is allocated from the kernel - * pool. - */ - set_fs (get_ds ()); - status = sys_uselib (lib_name); - set_fs (old_fs); -/* - * Return the success/failure to the caller. - */ - return (status); -} - -/* - * This procedure is called to load a library section. The various - * libraries are loaded from the list given in the section data. - */ - -static int -preload_library (struct linux_binprm *exe_bprm, - COFF_SCNHDR * sect, struct file *fp) -{ - int status = 0; /* Completion status */ - long nbytes; /* Count of bytes in the header area */ -/* - * Fetch the size of the section. There must be enough room for at least - * one entry. - */ - nbytes = COFF_LONG (sect->s_size); - if (nbytes < COFF_SLIBSZ) { - status = -ENOEXEC; -#ifdef COFF_DEBUG - printk ("library section too small\n"); -#endif - } -/* - * Allocate a buffer to hold the section data - */ - else { - COFF_SLIBHD *phdr; - char *buffer = (char *) kmalloc (nbytes, GFP_KERNEL); - - if (0 == buffer) { - status = -ENOEXEC; -#ifdef COFF_DEBUG - printk ("kmalloc failed\n"); -#endif - } - else { - int old_fs = get_fs (); -/* - * Read the section data from the disk file. - */ - set_fs (get_ds ()); /* Make it point to the proper location */ - status = read_exec (exe_bprm->inode, /* INODE for file */ - COFF_LONG (sect->s_scnptr), /* Disk location */ - buffer, /* Buffer for read */ - nbytes); /* Byte count reqd. */ - set_fs (old_fs); /* Restore the selector */ -/* - * Check the result. The value returned is the byte count actually read. - */ - if (status >= 0 && status != nbytes) { -#ifdef COFF_DEBUG - printk ("read of lib section was short\n"); -#endif - status = -ENOEXEC; - } - } -/* - * At this point, go through the list of libraries in the data area. - */ - phdr = (COFF_SLIBHD *) buffer; - while (status >= 0 && nbytes > COFF_SLIBSZ) { - int entry_size = COFF_LONG (phdr->sl_entsz) * sizeof (long); - int header_size = COFF_LONG (phdr->sl_pathndx) * sizeof (long); -/* - * Validate the sizes of the various items. I don't trust the linker!! - */ - if ((unsigned) header_size >= (unsigned) nbytes || - entry_size <= 0 || - (unsigned) entry_size <= (unsigned) header_size) { - status = -ENOEXEC; -#ifdef COFF_DEBUG - printk ("header count is invalid\n"); -#endif - } -/* - * Load the library. Stop the load process on the first error. - */ - else { - status = preload_this_library (exe_bprm, - &((char *) phdr)[header_size]); -#ifdef COFF_DEBUG - printk ("preload_this_library result = %d\n", status); -#endif - } -/* - * Point to the next library in the section data. - */ - nbytes -= entry_size; - phdr = (COFF_SLIBHD *) &((char *) phdr)[entry_size]; - } -/* - * Release the space for the library list. - */ - if (buffer != NULL) - kfree (buffer); - } -/* - * Return the resulting status to the caller. - */ - return (status); -} - -/* - * This procedure is called by the main load sequence. It will load - * the executable and prepare it for execution. It provides the additional - * parameters used by the recursive coff loader and tells the loader that - * this is the main executable. How simple it is . . . . - */ - -int -load_coff_binary (struct linux_binprm *bprm, struct pt_regs *regs) -{ - return (load_object (bprm, regs, 1)); -} - -/* - * Load the image for any shared library. - * - * This is called when we need to load a library based upon a file name. - */ - -int -load_coff_library (int fd) -{ - struct linux_binprm *bprm; /* Parameters for the load operation */ - int status; /* Status of the request */ -/* - * Read the first portion of the file. - */ - bprm = (struct linux_binprm *) kmalloc (sizeof (struct linux_binprm), - GFP_KERNEL); - if (0 == bprm) { -#ifdef COFF_DEBUG - printk ("kmalloc failed\n"); -#endif - status = -ENOEXEC; - } - else { - struct file *file; /* Pointer to the file table */ - struct pt_regs regs; /* Register work area */ - int old_fs = get_fs (); /* Previous FS register value */ - - memset (bprm, '\0', sizeof (struct linux_binprm)); - - file = current->files->fd[fd]; - bprm->inode = file->f_inode; /* The only item _really_ needed */ - bprm->filename = ""; /* Make it a legal string */ -/* - * Read the section list from the disk file. - */ - set_fs (get_ds ()); /* Make it point to the proper location */ - status = read_exec (bprm->inode, /* INODE for file */ - 0L, /* Offset in the file */ - bprm->buf, /* Buffer for read */ - sizeof (bprm->buf)); /* Size of the buffer */ - set_fs (old_fs); /* Restore the selector */ -/* - * Try to load the library. - */ - status = load_object (bprm, ®s, 0); -/* - * Release the work buffer and return the result. - */ - kfree (bprm); /* Release the buffer area */ - } -/* - * Return the result of the load operation - */ - return (status); -} diff --git a/arch/i386/ibcs/binfmt_elf.c b/arch/i386/ibcs/binfmt_elf.c deleted file mode 100644 index ab6d36915..000000000 --- a/arch/i386/ibcs/binfmt_elf.c +++ /dev/null @@ -1,655 +0,0 @@ -/* - * linux/fs/binfmt_elf.c - * - * These are the functions used to load ELF format executables as used - * on SVr4 machines. Information on the format may be found in the book - * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support - * Tools". - * - * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). - */ -#include <linux/fs.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/mman.h> -#include <linux/a.out.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/binfmts.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> -#include <linux/malloc.h> -#include <linux/shm.h> -#include <linux/personality.h> - -#include <asm/segment.h> - -asmlinkage int sys_exit(int exit_code); -asmlinkage int sys_close(unsigned fd); -asmlinkage int sys_open(const char *, int, int); -asmlinkage int sys_brk(unsigned long); - -#define DLINFO_ITEMS 8 - -#include <linux/elf.h> - -/* We need to explicitly zero any fractional pages - after the data section (i.e. bss). This would - contain the junk from the file that should not - be in memory */ - -static void padzero(int elf_bss){ - unsigned int fpnt, nbyte; - - if(elf_bss & 0xfff) { - - nbyte = (PAGE_SIZE - (elf_bss & 0xfff)) & 0xfff; - if(nbyte){ - verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte); - - fpnt = elf_bss; - while(fpnt & 0xfff) put_fs_byte(0, fpnt++); - }; - }; -} - -unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exec, unsigned int load_addr, int ibcs) -{ - unsigned long *argv,*envp, *dlinfo; - unsigned long * sp; - struct vm_area_struct *mpnt; - - mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); - if (mpnt) { - mpnt->vm_task = current; - mpnt->vm_start = PAGE_MASK & (unsigned long) p; - mpnt->vm_end = TASK_SIZE; - mpnt->vm_page_prot = PAGE_COPY; - mpnt->vm_flags = VM_STACK_FLAGS; - mpnt->vm_ops = NULL; - mpnt->vm_inode = NULL; - mpnt->vm_offset = 0; - mpnt->vm_pte = 0; - insert_vm_struct(current, mpnt); - } - sp = (unsigned long *) (0xfffffffc & (unsigned long) p); - if(exec) sp -= DLINFO_ITEMS*2; - dlinfo = sp; - sp -= envc+1; - envp = sp; - sp -= argc+1; - argv = sp; - if (!ibcs) { - put_fs_long((unsigned long)envp,--sp); - put_fs_long((unsigned long)argv,--sp); - } - - /* The constant numbers (0-9) that we are writing here are - described in the header file sys/auxv.h on at least - some versions of SVr4 */ - if(exec) { /* Put this here for an ELF program interpreter */ - struct elf_phdr * eppnt; - eppnt = (struct elf_phdr *) exec->e_phoff; - put_fs_long(3,dlinfo++); put_fs_long(load_addr + exec->e_phoff,dlinfo++); - put_fs_long(4,dlinfo++); put_fs_long(sizeof(struct elf_phdr),dlinfo++); - put_fs_long(5,dlinfo++); put_fs_long(exec->e_phnum,dlinfo++); - put_fs_long(9,dlinfo++); put_fs_long((unsigned long) exec->e_entry,dlinfo++); - put_fs_long(7,dlinfo++); put_fs_long(SHM_RANGE_START,dlinfo++); - put_fs_long(8,dlinfo++); put_fs_long(0,dlinfo++); - put_fs_long(6,dlinfo++); put_fs_long(PAGE_SIZE,dlinfo++); - put_fs_long(0,dlinfo++); put_fs_long(0,dlinfo++); - }; - - put_fs_long((unsigned long)argc,--sp); - current->mm->arg_start = (unsigned long) p; - while (argc-->0) { - put_fs_long((unsigned long) p,argv++); - while (get_fs_byte(p++)) /* nothing */ ; - } - put_fs_long(0,argv); - current->mm->arg_end = current->mm->env_start = (unsigned long) p; - while (envc-->0) { - put_fs_long((unsigned long) p,envp++); - while (get_fs_byte(p++)) /* nothing */ ; - } - put_fs_long(0,envp); - current->mm->env_end = (unsigned long) p; - return sp; -} - - -/* This is much more generalized than the library routine read function, - so we keep this separate. Technically the library read function - is only provided so that we can read a.out libraries that have - an ELF header */ - -static unsigned int load_elf_interp(struct elfhdr * interp_elf_ex, - struct inode * interpreter_inode) -{ - struct file * file; - struct elf_phdr *elf_phdata = NULL; - struct elf_phdr *eppnt; - unsigned int len; - unsigned int load_addr; - int elf_exec_fileno; - int elf_bss; - int old_fs, retval; - unsigned int last_bss; - int error; - int i, k; - - elf_bss = 0; - last_bss = 0; - error = load_addr = 0; - - /* First of all, some simple consistency checks */ - if((interp_elf_ex->e_type != ET_EXEC && - interp_elf_ex->e_type != ET_DYN) || - (interp_elf_ex->e_machine != EM_386 && interp_elf_ex->e_machine != EM_486) || - (!interpreter_inode->i_op || - !interpreter_inode->i_op->default_file_ops->mmap)){ - return 0xffffffff; - }; - - /* Now read in all of the header information */ - - if(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE) - return 0xffffffff; - - elf_phdata = (struct elf_phdr *) - kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, GFP_KERNEL); - if(!elf_phdata) return 0xffffffff; - - old_fs = get_fs(); - set_fs(get_ds()); - retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, (char *) elf_phdata, - sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); - set_fs(old_fs); - - elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY); - if (elf_exec_fileno < 0) return 0xffffffff; - file = current->files->fd[elf_exec_fileno]; - - eppnt = elf_phdata; - for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) - if(eppnt->p_type == PT_LOAD) { - error = do_mmap(file, - eppnt->p_vaddr & 0xfffff000, - eppnt->p_filesz + (eppnt->p_vaddr & 0xfff), - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | (interp_elf_ex->e_type == ET_EXEC ? MAP_FIXED : 0), - eppnt->p_offset & 0xfffff000); - - if(!load_addr && interp_elf_ex->e_type == ET_DYN) - load_addr = error; - k = load_addr + eppnt->p_vaddr + eppnt->p_filesz; - if(k > elf_bss) elf_bss = k; - if(error < 0 && error > -1024) break; /* Real error */ - k = load_addr + eppnt->p_memsz + eppnt->p_vaddr; - if(k > last_bss) last_bss = k; - } - - /* Now use mmap to map the library into memory. */ - - - sys_close(elf_exec_fileno); - if(error < 0 && error > -1024) { - kfree(elf_phdata); - return 0xffffffff; - } - - padzero(elf_bss); - len = (elf_bss + 0xfff) & 0xfffff000; /* What we have mapped so far */ - - /* Map the last of the bss segment */ - if (last_bss > len) - do_mmap(NULL, len, last_bss-len, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - kfree(elf_phdata); - - return ((unsigned int) interp_elf_ex->e_entry) + load_addr; -} - -static unsigned int load_aout_interp(struct exec * interp_ex, - struct inode * interpreter_inode) -{ - int retval; - unsigned int elf_entry; - - current->mm->brk = interp_ex->a_bss + - (current->mm->end_data = interp_ex->a_data + - (current->mm->end_code = interp_ex->a_text)); - elf_entry = interp_ex->a_entry; - - - if (N_MAGIC(*interp_ex) == OMAGIC) { - do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_inode, 32, (char *) 0, - interp_ex->a_text+interp_ex->a_data); - } else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) { - do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_inode, - N_TXTOFF(*interp_ex) , - (char *) N_TXTADDR(*interp_ex), - interp_ex->a_text+interp_ex->a_data); - } else - retval = -1; - - if(retval >= 0) - do_mmap(NULL, (interp_ex->a_text + interp_ex->a_data + 0xfff) & - 0xfffff000, interp_ex->a_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - if(retval < 0) return 0xffffffff; - return elf_entry; -} - -/* - * These are the functions used to load ELF style executables and shared - * libraries. There is no binary dependent code anywhere else. - */ - -#define INTERPRETER_NONE 0 -#define INTERPRETER_AOUT 1 -#define INTERPRETER_ELF 2 - -static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) -{ - struct elfhdr elf_ex; - struct elfhdr interp_elf_ex; - struct file * file; - struct exec interp_ex; - struct inode *interpreter_inode; - unsigned int load_addr; - unsigned int interpreter_type = INTERPRETER_NONE; - int i; - int old_fs; - int error; - struct elf_phdr * elf_ppnt, *elf_phdata; - int elf_exec_fileno; - unsigned int elf_bss, k, elf_brk; - int retval; - char * elf_interpreter; - unsigned int elf_entry; - int status; - unsigned int start_code, end_code, end_data; - unsigned int elf_stack; - char passed_fileno[6]; - - status = 0; - load_addr = 0; - elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ - - if (elf_ex.e_ident[0] != 0x7f || - strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) - return -ENOEXEC; - - - /* First of all, some simple consistency checks */ - if(elf_ex.e_type != ET_EXEC || - (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) || - (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)){ - return -ENOEXEC; - }; - - /* Now read in all of the header information */ - - elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize * - elf_ex.e_phnum, GFP_KERNEL); - - old_fs = get_fs(); - set_fs(get_ds()); - retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata, - elf_ex.e_phentsize * elf_ex.e_phnum); - set_fs(old_fs); - if (retval < 0) { - kfree (elf_phdata); - return retval; - } - - elf_ppnt = elf_phdata; - - elf_bss = 0; - elf_brk = 0; - - elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); - - if (elf_exec_fileno < 0) { - kfree (elf_phdata); - return elf_exec_fileno; - } - - file = current->files->fd[elf_exec_fileno]; - - elf_stack = 0xffffffff; - elf_interpreter = NULL; - start_code = 0; - end_code = 0; - end_data = 0; - - old_fs = get_fs(); - set_fs(get_ds()); - - for(i=0;i < elf_ex.e_phnum; i++){ - if(elf_ppnt->p_type == PT_INTERP) { - /* This is the program interpreter used for shared libraries - - for now assume that this is an a.out format binary */ - - elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz, - GFP_KERNEL); - - retval = read_exec(bprm->inode,elf_ppnt->p_offset,elf_interpreter, - elf_ppnt->p_filesz); -#if 0 - printk("Using ELF interpreter %s\n", elf_interpreter); -#endif - if(retval >= 0) - retval = namei(elf_interpreter, &interpreter_inode); - if(retval >= 0) - retval = read_exec(interpreter_inode,0,bprm->buf,128); - - if(retval >= 0){ - interp_ex = *((struct exec *) bprm->buf); /* exec-header */ - interp_elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ - - }; - if(retval < 0) { - kfree (elf_phdata); - kfree(elf_interpreter); - return retval; - }; - }; - elf_ppnt++; - }; - - set_fs(old_fs); - - /* Some simple consistency checks for the interpreter */ - if(elf_interpreter){ - interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; - if(retval < 0) { - kfree(elf_interpreter); - kfree(elf_phdata); - return -ELIBACC; - }; - /* Now figure out which format our binary is */ - if((N_MAGIC(interp_ex) != OMAGIC) && - (N_MAGIC(interp_ex) != ZMAGIC) && - (N_MAGIC(interp_ex) != QMAGIC)) - interpreter_type = INTERPRETER_ELF; - - if (interp_elf_ex.e_ident[0] != 0x7f || - strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0) - interpreter_type &= ~INTERPRETER_ELF; - - if(!interpreter_type) - { - kfree(elf_interpreter); - kfree(elf_phdata); - return -ELIBBAD; - }; - } - - /* OK, we are done with that, now set up the arg stuff, - and then start this sucker up */ - - if (!bprm->sh_bang) { - char * passed_p; - - if(interpreter_type == INTERPRETER_AOUT) { - sprintf(passed_fileno, "%d", elf_exec_fileno); - passed_p = passed_fileno; - - if(elf_interpreter) { - bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p,2); - bprm->argc++; - }; - }; - if (!bprm->p) { - if(elf_interpreter) { - kfree(elf_interpreter); - } - kfree (elf_phdata); - return -E2BIG; - } - } - - /* OK, This is the point of no return */ - flush_old_exec(bprm); - - current->mm->end_data = 0; - current->mm->end_code = 0; - current->mm->start_mmap = ELF_START_MMAP; - current->mm->mmap = NULL; - elf_entry = (unsigned int) elf_ex.e_entry; - - /* Do this so that we can load the interpreter, if need be. We will - change some of these later */ - current->mm->rss = 0; - bprm->p += change_ldt(0, bprm->page); - current->mm->start_stack = bprm->p; - - /* Now we do a little grungy work by mmaping the ELF image into - the correct location in memory. At this point, we assume that - the image should be loaded at fixed address, not at a variable - address. */ - - old_fs = get_fs(); - set_fs(get_ds()); - - elf_ppnt = elf_phdata; - for(i=0;i < elf_ex.e_phnum; i++){ - - if(elf_ppnt->p_type == PT_INTERP) { - /* Set these up so that we are able to load the interpreter */ - /* Now load the interpreter into user address space */ - set_fs(old_fs); - - if(interpreter_type & 1) elf_entry = - load_aout_interp(&interp_ex, interpreter_inode); - - if(interpreter_type & 2) elf_entry = - load_elf_interp(&interp_elf_ex, interpreter_inode); - - old_fs = get_fs(); - set_fs(get_ds()); - - iput(interpreter_inode); - kfree(elf_interpreter); - - if(elf_entry == 0xffffffff) { - printk("Unable to load interpreter\n"); - kfree(elf_phdata); - send_sig(SIGSEGV, current, 0); - return 0; - }; - }; - - - if(elf_ppnt->p_type == PT_LOAD) { - error = do_mmap(file, - elf_ppnt->p_vaddr & 0xfffff000, - elf_ppnt->p_filesz + (elf_ppnt->p_vaddr & 0xfff), - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, - elf_ppnt->p_offset & 0xfffff000); - -#ifdef LOW_ELF_STACK - if(elf_ppnt->p_vaddr & 0xfffff000 < elf_stack) - elf_stack = elf_ppnt->p_vaddr & 0xfffff000; -#endif - - if(!load_addr) - load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset; - k = elf_ppnt->p_vaddr; - if(k > start_code) start_code = k; - k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; - if(k > elf_bss) elf_bss = k; - if((elf_ppnt->p_flags | PROT_WRITE) && end_code < k) - end_code = k; - if(end_data < k) end_data = k; - k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; - if(k > elf_brk) elf_brk = k; - }; - elf_ppnt++; - }; - set_fs(old_fs); - - kfree(elf_phdata); - - if(interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno); - - /* The following 3 lines need a little bit of work if we are loading - an iBCS2 binary. We should initially load it this way, and if - we get a lcall7, then we should look to see if the iBCS2 execution - profile is present. If it is, then switch to that, otherwise - bomb. */ - current->personality = PER_LINUX; - current->lcall7 = no_lcall7; - current->signal_map = current->signal_invmap = ident_map; - - current->executable = bprm->inode; - bprm->inode->i_count++; -#ifdef LOW_ELF_STACK - current->start_stack = p = elf_stack - 4; -#endif - bprm->p -= MAX_ARG_PAGES*PAGE_SIZE; - bprm->p = (unsigned long) - create_elf_tables((char *)bprm->p, - bprm->argc, - bprm->envc, - (interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL), - load_addr, - (interpreter_type == INTERPRETER_AOUT ? 0 : 1)); - if(interpreter_type == INTERPRETER_AOUT) - current->mm->arg_start += strlen(passed_fileno) + 1; - current->mm->start_brk = current->mm->brk = elf_brk; - current->mm->end_code = end_code; - current->mm->start_code = start_code; - current->mm->end_data = end_data; - current->mm->start_stack = bprm->p; - current->suid = current->euid = bprm->e_uid; - current->sgid = current->egid = bprm->e_gid; - - /* Calling sys_brk effectively mmaps the pages that we need for the bss and break - sections */ - current->mm->brk = (elf_bss + 0xfff) & 0xfffff000; - sys_brk((elf_brk + 0xfff) & 0xfffff000); - - padzero(elf_bss); - - /* Why this, you ask??? Well SVr4 maps page 0 as read-only, - and some applications "depend" upon this behavior. - Since we do not have the power to recompile these, we - emulate the SVr4 behavior. Sigh. */ - error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, 0); - - regs->eip = elf_entry; /* eip, magic happens :-) */ - regs->esp = bprm->p; /* stack pointer */ - if (current->flags & PF_PTRACED) - send_sig(SIGTRAP, current, 0); - return 0; -} - -/* This is really simpleminded and specialized - we are loading an - a.out library that is given an ELF header. */ - -static int load_elf_library(int fd){ - struct file * file; - struct elfhdr elf_ex; - struct elf_phdr *elf_phdata = NULL; - struct inode * inode; - unsigned int len; - int elf_bss; - int old_fs, retval; - unsigned int bss; - int error; - int i,j, k; - - len = 0; - file = current->files->fd[fd]; - inode = file->f_inode; - elf_bss = 0; - - set_fs(KERNEL_DS); - if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) { - sys_close(fd); - return -EACCES; - } - set_fs(USER_DS); - - if (elf_ex.e_ident[0] != 0x7f || - strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) - return -ENOEXEC; - - /* First of all, some simple consistency checks */ - if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || - (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) || - (!inode->i_op || - !inode->i_op->default_file_ops->mmap)){ - return -ENOEXEC; - }; - - /* Now read in all of the header information */ - - if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) - return -ENOEXEC; - - elf_phdata = (struct elf_phdr *) - kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL); - - old_fs = get_fs(); - set_fs(get_ds()); - retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata, - sizeof(struct elf_phdr) * elf_ex.e_phnum); - set_fs(old_fs); - - j = 0; - for(i=0; i<elf_ex.e_phnum; i++) - if((elf_phdata + i)->p_type == PT_LOAD) j++; - - if(j != 1) { - kfree(elf_phdata); - return -ENOEXEC; - }; - - while(elf_phdata->p_type != PT_LOAD) elf_phdata++; - - /* Now use mmap to map the library into memory. */ - error = do_mmap(file, - elf_phdata->p_vaddr & 0xfffff000, - elf_phdata->p_filesz + (elf_phdata->p_vaddr & 0xfff), - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, - elf_phdata->p_offset & 0xfffff000); - - k = elf_phdata->p_vaddr + elf_phdata->p_filesz; - if(k > elf_bss) elf_bss = k; - - sys_close(fd); - if (error != elf_phdata->p_vaddr & 0xfffff000) { - kfree(elf_phdata); - return error; - } - - padzero(elf_bss); - - len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000; - bss = elf_phdata->p_memsz + elf_phdata->p_vaddr; - if (bss > len) - do_mmap(NULL, len, bss-len, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - kfree(elf_phdata); - return 0; -} - -struct linux_binfmt elf_format = { NULL, load_elf_binary, load_elf_library }; diff --git a/arch/i386/ibcs/emulate.c b/arch/i386/ibcs/emulate.c deleted file mode 100644 index dc3b3d576..000000000 --- a/arch/i386/ibcs/emulate.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * linux/abi/emulate.c - * - * Copyright (C) 1993 Linus Torvalds - */ - -/* - * Yes, sir, this file is completely empty, waiting for some real code.. - * I still copyright it, silly me. - */ diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 701926d26..123dd1207 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -7,42 +7,61 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -.c.s: - $(CC) $(CFLAGS) -S $< -.s.o: - $(AS) -o $*.o $< -.c.o: - $(CC) $(CFLAGS) -c $< -.S.s: - $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s +#.S.s: +# $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s + +ifdef SMP +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o +else .S.o: $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -OBJS = process.o signal.o entry.o traps.o irq.o vm86.o bios32.o ptrace.o ioport.o ldt.o setup.o +endif all: kernel.o head.o -head.o: head.s +O_TARGET := kernel.o +O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o bios32.o \ + ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o ksyms.o -head.s: head.S $(TOPDIR)/include/linux/tasks.h - $(CPP) -traditional -o $*.s $< +ifdef SMP -kernel.o: $(OBJS) - $(LD) -r -o kernel.o $(OBJS) - sync +O_OBJS += smp.o -dep: - $(CPP) -M *.c > .depend +head.o: head.S $(TOPDIR)/include/linux/tasks.h + $(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $*.S -o $*.o -modules: +else -dummy: +head.o: head.S $(TOPDIR)/include/linux/tasks.h + $(CC) -D__ASSEMBLY__ -traditional -c $*.S -o $*.o -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend endif +hexify: + $(HOSTCC) hexify.c -o hexify + +smp.c: trampoline.hex + +trampoline.hex: trampoline hexify + (dd if=trampoline bs=1 skip=32 | ./hexify >trampoline.hex ) + +trampoline: trampoline.o trampoline32.o + $(LD86) -s -o $@ trampoline.o trampoline32.o + +trampoline.o: trampoline.s + $(AS86) -o $@ $< + +trampoline32.o: trampoline32.s + $(AS386) -o $@ $< + +trampoline.s: trampoline.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile + $(CPP) -D__SMP__ -traditional $< -o $@ + +trampoline32.s: trampoline32.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile + $(CPP) -D__SMP__ -traditional $< -o $@ + +clean: + rm -f trampoline hexify +include $(TOPDIR)/Rules.make diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index 8cc3d76fa..a1b49a3ae 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -55,6 +55,7 @@ #include <linux/bios32.h> #include <linux/pci.h> +#include <asm/page.h> #include <asm/segment.h> #define PCIBIOS_PCI_FUNCTION_ID 0xb1XX @@ -164,7 +165,7 @@ extern unsigned long check_pcibios(unsigned long memory_start, unsigned long mem int pack; if ((pcibios_entry = bios32_service(PCI_SERVICE))) { - pci_indirect.address = pcibios_entry; + pci_indirect.address = pcibios_entry | PAGE_OFFSET; __asm__("lcall (%%edi)\n\t" "jc 1f\n\t" @@ -363,7 +364,7 @@ int pcibios_write_config_dword (unsigned char bus, return (int) (ret & 0xff00) >> 8; } -char *pcibios_strerror (int error) +const char *pcibios_strerror (int error) { static char buf[80]; @@ -383,6 +384,12 @@ char *pcibios_strerror (int error) case PCIBIOS_BAD_REGISTER_NUMBER: return "BAD_REGISTER_NUMBER"; + case PCIBIOS_SET_FAILED: + return "SET_FAILED"; + + case PCIBIOS_BUFFER_TOO_SMALL: + return "BUFFER_TOO_SMALL"; + default: sprintf (buf, "UNKNOWN RETURN 0x%x", error); return buf; @@ -411,7 +418,9 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) * */ - for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) { + for (check = (union bios32 *) __va(0xe0000); + check <= (union bios32 *) __va(0xffff0); + ++check) { if (check->fields.signature != BIOS32_SIGNATURE) continue; length = check->fields.length * 16; @@ -432,17 +441,10 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) if (check->fields.entry >= 0x100000) { printk("pcibios_init: entry in high memory, unable to access\n"); } else { - bios32_indirect.address = bios32_entry = check->fields.entry; + bios32_entry = check->fields.entry; printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); + bios32_indirect.address = bios32_entry + PAGE_OFFSET; } - } else { - printk ("pcibios_init : multiple entries, mail drew@colorado.edu\n"); - /* - * Jeremy Fitzhardinge reports at least one PCI BIOS - * with two different service directories, and as both - * worked for him, we'll just mention the fact, and - * not actually disallow it.. - */ } } #ifdef CONFIG_PCI @@ -452,6 +454,3 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) #endif return memory_start; } - - - diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 1bdf062c8..d421f45f1 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -30,18 +30,21 @@ * 18(%esp) - %eax * 1C(%esp) - %ds * 20(%esp) - %es - * 24(%esp) - %fs - * 28(%esp) - %gs - * 2C(%esp) - orig_eax - * 30(%esp) - %eip - * 34(%esp) - %cs - * 38(%esp) - %eflags - * 3C(%esp) - %oldesp - * 40(%esp) - %oldss + * 24(%esp) - orig_eax + * 28(%esp) - %eip + * 2C(%esp) - %cs + * 30(%esp) - %eflags + * 34(%esp) - %oldesp + * 38(%esp) - %oldss + * + * "current" is in register %ebx during any slow entries. */ #include <linux/sys.h> +#include <linux/linkage.h> #include <asm/segment.h> +#define ASSEMBLY +#include <asm/smp.h> EBX = 0x00 ECX = 0x04 @@ -52,14 +55,12 @@ EBP = 0x14 EAX = 0x18 DS = 0x1C ES = 0x20 -FS = 0x24 -GS = 0x28 -ORIG_EAX = 0x2C -EIP = 0x30 -CS = 0x34 -EFLAGS = 0x38 -OLDESP = 0x3C -OLDSS = 0x40 +ORIG_EAX = 0x24 +EIP = 0x28 +CS = 0x2C +EFLAGS = 0x30 +OLDESP = 0x34 +OLDSS = 0x38 CF_MASK = 0x00000001 IF_MASK = 0x00000200 @@ -75,26 +76,14 @@ priority = 8 signal = 12 blocked = 16 flags = 20 -errno = 24 dbgreg6 = 52 dbgreg7 = 56 exec_domain = 60 ENOSYS = 38 -.globl _system_call,_lcall7 -.globl _device_not_available, _coprocessor_error -.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,_reserved -.globl _alignment_check,_page_fault -.globl ret_from_sys_call, _sys_call_table - #define SAVE_ALL \ cld; \ - push %gs; \ - push %fs; \ push %es; \ push %ds; \ pushl %eax; \ @@ -106,17 +95,123 @@ ENOSYS = 38 pushl %ebx; \ movl $(KERNEL_DS),%edx; \ mov %dx,%ds; \ - mov %dx,%es; \ - movl $(USER_DS),%edx; \ - mov %dx,%fs; + mov %dx,%es; + +#ifdef __SMP__ + +#define GET_PROCESSOR_ID \ + movl SYMBOL_NAME(apic_reg), %edx; \ + movl 32(%edx), %eax;\ + movl %eax,SYMBOL_NAME(apic_retval); \ + shrl $24,%eax; \ + andb $0x0F,%al; + +/* + * Get the processor ID multiplied by 4 + */ + +#define GET_PROCESSOR_OFFSET(x) \ + movl SYMBOL_NAME(apic_reg), x ; \ + movl 32( x ), x ; \ + shrl $22, x ; \ + andl $0x3C, x ; + +/* macro LEAVE_KERNEL decrements kernel_counter and resets kernel_flag and + saves processor variables if zero */ +#define LEAVE_KERNEL \ + pushfl; \ + cli; \ + GET_PROCESSOR_ID \ + btrl $ SMP_FROM_SYSCALL,SYMBOL_NAME(smp_proc_in_lock)(,%eax,4); \ + decl SYMBOL_NAME(syscall_count); \ + decl SYMBOL_NAME(kernel_counter); \ + jnz 1f; \ + movb $(NO_PROC_ID), SYMBOL_NAME(active_kernel_processor); \ + lock; \ + btrl $0, SYMBOL_NAME(kernel_flag); \ +1: popfl; + +/* macro ENTER_KERNEL waits for entering the kernel, increments + kernel_counter, and reloads the processor variables if necessary + uses : %eax, %edx (pushed and popped) + + Note: We go to great pains to minimise the number of locked operations. + We want to spin without locking, and lock when we attempt an update. + The pentium has a MESI cache so the spin without lock will exit when + another CPU write invalidates our cache, and the lock is avoided when + possible so we don't play ping-pong games with the cache line. + +*/ + +#ifndef __SMP_PROF__ + +#define SMP_PROF_A +#define SMP_PROF_B + +#else + +#define SMP_PROF_A movl $0,SYMBOL_NAME(smp_spins_syscall_cur)(,%eax,4); +#define SMP_PROF_B incl SYMBOL_NAME(smp_spins_syscall)(,%eax,4); \ + incl SYMBOL_NAME(smp_spins_syscall_cur)(,%eax,4); +#endif + +#define ENTER_KERNEL \ + pushl %eax; \ + pushl %edx; \ + pushfl; \ + cli; \ + GET_PROCESSOR_ID \ + btsl $ SMP_FROM_SYSCALL,SYMBOL_NAME(smp_proc_in_lock)(,%eax,4); \ + SMP_PROF_A \ +1: lock; \ + btsl $0, SYMBOL_NAME(kernel_flag); \ + jnc 3f; \ + cmpb SYMBOL_NAME(active_kernel_processor), %al; \ + je 4f; \ +2: SMP_PROF_B \ + btl %al, SYMBOL_NAME(smp_invalidate_needed); \ + jnc 5f; \ + lock; \ + btrl %al, SYMBOL_NAME(smp_invalidate_needed); \ + jnc 5f; \ + movl %cr3,%edx; \ + movl %edx,%cr3; \ +5: btl $0, SYMBOL_NAME(kernel_flag); \ + jc 2b; \ + jmp 1b; \ +3: movb %al, SYMBOL_NAME(active_kernel_processor); \ +4: incl SYMBOL_NAME(kernel_counter); \ + incl SYMBOL_NAME(syscall_count); \ + popfl; \ + popl %edx; \ + popl %eax; + + +#define RESTORE_ALL \ + LEAVE_KERNEL \ + popl %ebx; \ + popl %ecx; \ + popl %edx; \ + popl %esi; \ + popl %edi; \ + popl %ebp; \ + popl %eax; \ + pop %ds; \ + pop %es; \ + addl $4,%esp; \ + iret + +#define GET_CURRENT \ + GET_PROCESSOR_OFFSET(%ebx) \ + movl SYMBOL_NAME(current_set)(%ebx),%ebx + +#else + +#define GET_CURRENT \ + movl SYMBOL_NAME(current_set),%ebx #define RESTORE_ALL \ - cmpw $(KERNEL_CS),CS(%esp); \ - je 1f; \ - movl _current,%eax; \ - movl dbgreg7(%eax),%ebx; \ - movl %ebx,%db7; \ -1: popl %ebx; \ + popl %ebx; \ popl %ecx; \ popl %edx; \ popl %esi; \ @@ -125,16 +220,19 @@ ENOSYS = 38 popl %eax; \ pop %ds; \ pop %es; \ - pop %fs; \ - pop %gs; \ addl $4,%esp; \ iret +#endif + -.align 4 -_lcall7: +ENTRY(lcall7) pushfl # We get a different stack layout with call gates, pushl %eax # which has to be cleaned up later.. SAVE_ALL + GET_CURRENT +#ifdef __SMP__ + ENTER_KERNEL +#endif movl EIP(%esp),%eax # due to call gates, this is eflags, not eip.. movl CS(%esp),%edx # this is eip.. movl EFLAGS(%esp),%ecx # and this is cs.. @@ -142,7 +240,12 @@ _lcall7: movl %edx,EIP(%esp) # Now we move them to their "normal" places movl %ecx,CS(%esp) # movl %esp,%eax - movl _current,%edx +#ifdef __SMP__ + GET_PROCESSOR_OFFSET(%edx) # Processor offset into edx + movl SYMBOL_NAME(current_set)(,%edx),%edx +#else + movl SYMBOL_NAME(current_set),%edx +#endif pushl %eax movl exec_domain(%edx),%edx # Get the execution domain movl 4(%edx),%edx # Get the lcall7 handler for the domain @@ -150,398 +253,378 @@ _lcall7: popl %eax jmp ret_from_sys_call -.align 4 + ALIGN handle_bottom_half: - pushfl - incl _intr_count - sti - call _do_bottom_half - popfl - decl _intr_count + incl SYMBOL_NAME(intr_count) + call SYMBOL_NAME(do_bottom_half) + decl SYMBOL_NAME(intr_count) jmp 9f -.align 4 + ALIGN reschedule: pushl $ret_from_sys_call - jmp _schedule -.align 4 -_system_call: + jmp SYMBOL_NAME(schedule) # test + +ENTRY(system_call) pushl %eax # save orig_eax SAVE_ALL - movl $-ENOSYS,EAX(%esp) + GET_CURRENT +#ifdef __SMP__ + ENTER_KERNEL +#endif cmpl $(NR_syscalls),%eax - jae ret_from_sys_call - movl _sys_call_table(,%eax,4),%eax - testl %eax,%eax - je ret_from_sys_call - movl _current,%ebx - andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors - movl $0,errno(%ebx) - movl %db6,%edx - movl %edx,dbgreg6(%ebx) # save current hardware debugging status + jae badsys testb $0x20,flags(%ebx) # PF_TRACESYS - jne 1f - call *%eax - movl %eax,EAX(%esp) # save the return value - movl errno(%ebx),%edx - negl %edx - je ret_from_sys_call - movl %edx,EAX(%esp) - orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error - jmp ret_from_sys_call -.align 4 -1: call _syscall_trace - movl ORIG_EAX(%esp),%eax - call _sys_call_table(,%eax,4) + jne tracesys + call SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # save the return value - movl _current,%eax - movl errno(%eax),%edx - negl %edx - je 1f - movl %edx,EAX(%esp) - orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error -1: call _syscall_trace - - .align 4,0x90 + ALIGN + .globl ret_from_sys_call ret_from_sys_call: - cmpl $0,_intr_count - jne 2f -9: movl _bh_mask,%eax - andl _bh_active,%eax - jne handle_bottom_half - movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are - testl $(VM_MASK),%eax # different then + cmpl $0,SYMBOL_NAME(intr_count) jne 1f - cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ? - je 2f -1: sti - orl $(IF_MASK),%eax # these just try to make sure - andl $~NT_MASK,%eax # the program doesn't do anything - movl %eax,EFLAGS(%esp) # stupid - cmpl $0,_need_resched - jne reschedule - movl _current,%eax - cmpl _task,%eax # task[0] cannot have signals - je 2f - cmpl $0,state(%eax) # state +9: movl SYMBOL_NAME(bh_mask),%eax + andl SYMBOL_NAME(bh_active),%eax + jne handle_bottom_half + movl EFLAGS(%esp),%eax # mix EFLAGS and CS + movb CS(%esp),%al + testl $(VM_MASK | 3),%eax # return to VM86 mode or non-supervisor? + je 1f + cmpl $0,SYMBOL_NAME(need_resched) jne reschedule - cmpl $0,counter(%eax) # counter - je reschedule - movl blocked(%eax),%ecx - movl %ecx,%ebx # save blocked in %ebx for signal handling - notl %ecx - andl signal(%eax),%ecx + movl blocked(%ebx),%eax + movl %eax,%esi # save blocked in %esi for signal handling + notl %eax + andl signal(%ebx),%eax jne signal_return -2: RESTORE_ALL -.align 4 +1: RESTORE_ALL + ALIGN signal_return: - movl %esp,%ecx - pushl %ecx - testl $(VM_MASK),EFLAGS(%ecx) + testl $(VM_MASK),EFLAGS(%esp) + pushl %esp jne v86_signal_return - pushl %ebx - call _do_signal - popl %ebx - popl %ebx + pushl %esi + call SYMBOL_NAME(do_signal) + addl $8,%esp RESTORE_ALL -.align 4 + ALIGN v86_signal_return: - call _save_v86_state + call SYMBOL_NAME(save_v86_state) movl %eax,%esp pushl %eax - pushl %ebx - call _do_signal - popl %ebx - popl %ebx + pushl %esi + call SYMBOL_NAME(do_signal) + addl $8,%esp RESTORE_ALL + ALIGN +tracesys: + movl $-ENOSYS,EAX(%esp) + call SYMBOL_NAME(syscall_trace) + movl ORIG_EAX(%esp),%eax + call SYMBOL_NAME(sys_call_table)(,%eax,4) + movl %eax,EAX(%esp) # save the return value + call SYMBOL_NAME(syscall_trace) + jmp ret_from_sys_call +badsys: + movl $-ENOSYS,EAX(%esp) + jmp ret_from_sys_call -.align 4 -_divide_error: + +ENTRY(divide_error) pushl $0 # no error code - pushl $_do_divide_error -.align 4,0x90 + pushl $ SYMBOL_NAME(do_divide_error) + ALIGN error_code: - push %fs - push %es push %ds pushl %eax + xorl %eax,%eax pushl %ebp pushl %edi pushl %esi pushl %edx + decl %eax # eax = -1 pushl %ecx pushl %ebx - movl $0,%eax - movl %eax,%db7 # disable hardware debugging... + xorl %ecx,%ecx # zero ecx cld - movl $-1, %eax + mov %es,%cx # get the lower order bits of es xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. ) - xorl %ebx,%ebx # zero ebx - mov %gs,%bx # get the lower order bits of gs - xchgl %ebx, GS(%esp) # get the address and save gs. + movl %esp,%edx + xchgl %ecx, ES(%esp) # get the address and save es. pushl %eax # push the error code - lea 4(%esp),%edx pushl %edx movl $(KERNEL_DS),%edx mov %dx,%ds mov %dx,%es - movl $(USER_DS),%edx - mov %dx,%fs - pushl %eax - movl _current,%eax - movl %db6,%edx - movl %edx,dbgreg6(%eax) # save current hardware debugging status - popl %eax - call *%ebx + GET_CURRENT +#ifdef __SMP__ + ENTER_KERNEL +#endif + call *%ecx addl $8,%esp jmp ret_from_sys_call -.align 4 -_coprocessor_error: +ENTRY(coprocessor_error) pushl $0 - pushl $_do_coprocessor_error + pushl $ SYMBOL_NAME(do_coprocessor_error) jmp error_code -.align 4 -_device_not_available: +ENTRY(device_not_available) pushl $-1 # mark this as an int SAVE_ALL + GET_CURRENT +#ifdef __SMP__ + ENTER_KERNEL +#endif pushl $ret_from_sys_call movl %cr0,%eax testl $0x4,%eax # EM (math emulation bit) - je _math_state_restore + je SYMBOL_NAME(math_state_restore) pushl $0 # temporary storage for ORIG_EIP - call _math_emulate + call SYMBOL_NAME(math_emulate) addl $4,%esp ret -.align 4 -_debug: +ENTRY(debug) pushl $0 - pushl $_do_debug + pushl $ SYMBOL_NAME(do_debug) jmp error_code -.align 4 -_nmi: +ENTRY(nmi) pushl $0 - pushl $_do_nmi + pushl $ SYMBOL_NAME(do_nmi) jmp error_code -.align 4 -_int3: +ENTRY(int3) pushl $0 - pushl $_do_int3 + pushl $ SYMBOL_NAME(do_int3) jmp error_code -.align 4 -_overflow: +ENTRY(overflow) pushl $0 - pushl $_do_overflow + pushl $ SYMBOL_NAME(do_overflow) jmp error_code -.align 4 -_bounds: +ENTRY(bounds) pushl $0 - pushl $_do_bounds + pushl $ SYMBOL_NAME(do_bounds) jmp error_code -.align 4 -_invalid_op: +ENTRY(invalid_op) pushl $0 - pushl $_do_invalid_op + pushl $ SYMBOL_NAME(do_invalid_op) jmp error_code -.align 4 -_coprocessor_segment_overrun: +ENTRY(coprocessor_segment_overrun) pushl $0 - pushl $_do_coprocessor_segment_overrun + pushl $ SYMBOL_NAME(do_coprocessor_segment_overrun) jmp error_code -.align 4 -_reserved: +ENTRY(reserved) pushl $0 - pushl $_do_reserved + pushl $ SYMBOL_NAME(do_reserved) + jmp error_code + +ENTRY(double_fault) + pushl $ SYMBOL_NAME(do_double_fault) jmp error_code -.align 4 -_double_fault: - pushl $_do_double_fault +ENTRY(invalid_TSS) + pushl $ SYMBOL_NAME(do_invalid_TSS) jmp error_code -.align 4 -_invalid_TSS: - pushl $_do_invalid_TSS +ENTRY(segment_not_present) + pushl $ SYMBOL_NAME(do_segment_not_present) jmp error_code -.align 4 -_segment_not_present: - pushl $_do_segment_not_present +ENTRY(stack_segment) + pushl $ SYMBOL_NAME(do_stack_segment) jmp error_code -.align 4 -_stack_segment: - pushl $_do_stack_segment +ENTRY(general_protection) + pushl $ SYMBOL_NAME(do_general_protection) jmp error_code -.align 4 -_general_protection: - pushl $_do_general_protection +ENTRY(alignment_check) + pushl $ SYMBOL_NAME(do_alignment_check) jmp error_code -.align 4 -_alignment_check: - pushl $_do_alignment_check +ENTRY(page_fault) + pushl $ SYMBOL_NAME(do_page_fault) jmp error_code -.align 4 -_page_fault: - pushl $_do_page_fault +ENTRY(spurious_interrupt_bug) + pushl $0 + pushl $ SYMBOL_NAME(do_spurious_interrupt_bug) jmp error_code .data -.align 4 -_sys_call_table: - .long _sys_setup /* 0 */ - .long _sys_exit - .long _sys_fork - .long _sys_read - .long _sys_write - .long _sys_open /* 5 */ - .long _sys_close - .long _sys_waitpid - .long _sys_creat - .long _sys_link - .long _sys_unlink /* 10 */ - .long _sys_execve - .long _sys_chdir - .long _sys_time - .long _sys_mknod - .long _sys_chmod /* 15 */ - .long _sys_chown - .long _sys_break - .long _sys_stat - .long _sys_lseek - .long _sys_getpid /* 20 */ - .long _sys_mount - .long _sys_umount - .long _sys_setuid - .long _sys_getuid - .long _sys_stime /* 25 */ - .long _sys_ptrace - .long _sys_alarm - .long _sys_fstat - .long _sys_pause - .long _sys_utime /* 30 */ - .long _sys_stty - .long _sys_gtty - .long _sys_access - .long _sys_nice - .long _sys_ftime /* 35 */ - .long _sys_sync - .long _sys_kill - .long _sys_rename - .long _sys_mkdir - .long _sys_rmdir /* 40 */ - .long _sys_dup - .long _sys_pipe - .long _sys_times - .long _sys_prof - .long _sys_brk /* 45 */ - .long _sys_setgid - .long _sys_getgid - .long _sys_signal - .long _sys_geteuid - .long _sys_getegid /* 50 */ - .long _sys_acct - .long _sys_phys - .long _sys_lock - .long _sys_ioctl - .long _sys_fcntl /* 55 */ - .long _sys_mpx - .long _sys_setpgid - .long _sys_ulimit - .long _sys_olduname - .long _sys_umask /* 60 */ - .long _sys_chroot - .long _sys_ustat - .long _sys_dup2 - .long _sys_getppid - .long _sys_getpgrp /* 65 */ - .long _sys_setsid - .long _sys_sigaction - .long _sys_sgetmask - .long _sys_ssetmask - .long _sys_setreuid /* 70 */ - .long _sys_setregid - .long _sys_sigsuspend - .long _sys_sigpending - .long _sys_sethostname - .long _sys_setrlimit /* 75 */ - .long _sys_getrlimit - .long _sys_getrusage - .long _sys_gettimeofday - .long _sys_settimeofday - .long _sys_getgroups /* 80 */ - .long _sys_setgroups - .long _old_select - .long _sys_symlink - .long _sys_lstat - .long _sys_readlink /* 85 */ - .long _sys_uselib - .long _sys_swapon - .long _sys_reboot - .long _old_readdir - .long _sys_mmap /* 90 */ - .long _sys_munmap - .long _sys_truncate - .long _sys_ftruncate - .long _sys_fchmod - .long _sys_fchown /* 95 */ - .long _sys_getpriority - .long _sys_setpriority - .long _sys_profil - .long _sys_statfs - .long _sys_fstatfs /* 100 */ - .long _sys_ioperm - .long _sys_socketcall - .long _sys_syslog - .long _sys_setitimer - .long _sys_getitimer /* 105 */ - .long _sys_newstat - .long _sys_newlstat - .long _sys_newfstat - .long _sys_uname - .long _sys_iopl /* 110 */ - .long _sys_vhangup - .long _sys_idle - .long _sys_vm86 - .long _sys_wait4 - .long _sys_swapoff /* 115 */ - .long _sys_sysinfo - .long _sys_ipc - .long _sys_fsync - .long _sys_sigreturn - .long _sys_clone /* 120 */ - .long _sys_setdomainname - .long _sys_newuname - .long _sys_modify_ldt - .long _sys_adjtimex - .long _sys_mprotect /* 125 */ - .long _sys_sigprocmask - .long _sys_create_module - .long _sys_init_module - .long _sys_delete_module - .long _sys_get_kernel_syms /* 130 */ - .long _sys_quotactl - .long _sys_getpgid - .long _sys_fchdir - .long _sys_bdflush - .long _sys_sysfs /* 135 */ - .long _sys_personality - .long 0 /* for afs_syscall */ - .long _sys_setfsuid - .long _sys_setfsgid - .long _sys_llseek /* 140 */ - .long _sys_getdents - .long _sys_select - .long _sys_flock - .space (NR_syscalls-140)*4 +ENTRY(sys_call_table) + .long SYMBOL_NAME(sys_setup) /* 0 */ + .long SYMBOL_NAME(sys_exit) + .long SYMBOL_NAME(sys_fork) + .long SYMBOL_NAME(sys_read) + .long SYMBOL_NAME(sys_write) + .long SYMBOL_NAME(sys_open) /* 5 */ + .long SYMBOL_NAME(sys_close) + .long SYMBOL_NAME(sys_waitpid) + .long SYMBOL_NAME(sys_creat) + .long SYMBOL_NAME(sys_link) + .long SYMBOL_NAME(sys_unlink) /* 10 */ + .long SYMBOL_NAME(sys_execve) + .long SYMBOL_NAME(sys_chdir) + .long SYMBOL_NAME(sys_time) + .long SYMBOL_NAME(sys_mknod) + .long SYMBOL_NAME(sys_chmod) /* 15 */ + .long SYMBOL_NAME(sys_chown) + .long SYMBOL_NAME(sys_break) + .long SYMBOL_NAME(sys_stat) + .long SYMBOL_NAME(sys_lseek) + .long SYMBOL_NAME(sys_getpid) /* 20 */ + .long SYMBOL_NAME(sys_mount) + .long SYMBOL_NAME(sys_umount) + .long SYMBOL_NAME(sys_setuid) + .long SYMBOL_NAME(sys_getuid) + .long SYMBOL_NAME(sys_stime) /* 25 */ + .long SYMBOL_NAME(sys_ptrace) + .long SYMBOL_NAME(sys_alarm) + .long SYMBOL_NAME(sys_fstat) + .long SYMBOL_NAME(sys_pause) + .long SYMBOL_NAME(sys_utime) /* 30 */ + .long SYMBOL_NAME(sys_stty) + .long SYMBOL_NAME(sys_gtty) + .long SYMBOL_NAME(sys_access) + .long SYMBOL_NAME(sys_nice) + .long SYMBOL_NAME(sys_ftime) /* 35 */ + .long SYMBOL_NAME(sys_sync) + .long SYMBOL_NAME(sys_kill) + .long SYMBOL_NAME(sys_rename) + .long SYMBOL_NAME(sys_mkdir) + .long SYMBOL_NAME(sys_rmdir) /* 40 */ + .long SYMBOL_NAME(sys_dup) + .long SYMBOL_NAME(sys_pipe) + .long SYMBOL_NAME(sys_times) + .long SYMBOL_NAME(sys_prof) + .long SYMBOL_NAME(sys_brk) /* 45 */ + .long SYMBOL_NAME(sys_setgid) + .long SYMBOL_NAME(sys_getgid) + .long SYMBOL_NAME(sys_signal) + .long SYMBOL_NAME(sys_geteuid) + .long SYMBOL_NAME(sys_getegid) /* 50 */ + .long SYMBOL_NAME(sys_acct) + .long SYMBOL_NAME(sys_phys) + .long SYMBOL_NAME(sys_lock) + .long SYMBOL_NAME(sys_ioctl) + .long SYMBOL_NAME(sys_fcntl) /* 55 */ + .long SYMBOL_NAME(sys_mpx) + .long SYMBOL_NAME(sys_setpgid) + .long SYMBOL_NAME(sys_ulimit) + .long SYMBOL_NAME(sys_olduname) + .long SYMBOL_NAME(sys_umask) /* 60 */ + .long SYMBOL_NAME(sys_chroot) + .long SYMBOL_NAME(sys_ustat) + .long SYMBOL_NAME(sys_dup2) + .long SYMBOL_NAME(sys_getppid) + .long SYMBOL_NAME(sys_getpgrp) /* 65 */ + .long SYMBOL_NAME(sys_setsid) + .long SYMBOL_NAME(sys_sigaction) + .long SYMBOL_NAME(sys_sgetmask) + .long SYMBOL_NAME(sys_ssetmask) + .long SYMBOL_NAME(sys_setreuid) /* 70 */ + .long SYMBOL_NAME(sys_setregid) + .long SYMBOL_NAME(sys_sigsuspend) + .long SYMBOL_NAME(sys_sigpending) + .long SYMBOL_NAME(sys_sethostname) + .long SYMBOL_NAME(sys_setrlimit) /* 75 */ + .long SYMBOL_NAME(sys_getrlimit) + .long SYMBOL_NAME(sys_getrusage) + .long SYMBOL_NAME(sys_gettimeofday) + .long SYMBOL_NAME(sys_settimeofday) + .long SYMBOL_NAME(sys_getgroups) /* 80 */ + .long SYMBOL_NAME(sys_setgroups) + .long SYMBOL_NAME(old_select) + .long SYMBOL_NAME(sys_symlink) + .long SYMBOL_NAME(sys_lstat) + .long SYMBOL_NAME(sys_readlink) /* 85 */ + .long SYMBOL_NAME(sys_uselib) + .long SYMBOL_NAME(sys_swapon) + .long SYMBOL_NAME(sys_reboot) + .long SYMBOL_NAME(old_readdir) + .long SYMBOL_NAME(old_mmap) /* 90 */ + .long SYMBOL_NAME(sys_munmap) + .long SYMBOL_NAME(sys_truncate) + .long SYMBOL_NAME(sys_ftruncate) + .long SYMBOL_NAME(sys_fchmod) + .long SYMBOL_NAME(sys_fchown) /* 95 */ + .long SYMBOL_NAME(sys_getpriority) + .long SYMBOL_NAME(sys_setpriority) + .long SYMBOL_NAME(sys_profil) + .long SYMBOL_NAME(sys_statfs) + .long SYMBOL_NAME(sys_fstatfs) /* 100 */ + .long SYMBOL_NAME(sys_ioperm) + .long SYMBOL_NAME(sys_socketcall) + .long SYMBOL_NAME(sys_syslog) + .long SYMBOL_NAME(sys_setitimer) + .long SYMBOL_NAME(sys_getitimer) /* 105 */ + .long SYMBOL_NAME(sys_newstat) + .long SYMBOL_NAME(sys_newlstat) + .long SYMBOL_NAME(sys_newfstat) + .long SYMBOL_NAME(sys_uname) + .long SYMBOL_NAME(sys_iopl) /* 110 */ + .long SYMBOL_NAME(sys_vhangup) + .long SYMBOL_NAME(sys_idle) + .long SYMBOL_NAME(sys_vm86) + .long SYMBOL_NAME(sys_wait4) + .long SYMBOL_NAME(sys_swapoff) /* 115 */ + .long SYMBOL_NAME(sys_sysinfo) + .long SYMBOL_NAME(sys_ipc) + .long SYMBOL_NAME(sys_fsync) + .long SYMBOL_NAME(sys_sigreturn) + .long SYMBOL_NAME(sys_clone) /* 120 */ + .long SYMBOL_NAME(sys_setdomainname) + .long SYMBOL_NAME(sys_newuname) + .long SYMBOL_NAME(sys_modify_ldt) + .long SYMBOL_NAME(sys_adjtimex) + .long SYMBOL_NAME(sys_mprotect) /* 125 */ + .long SYMBOL_NAME(sys_sigprocmask) + .long SYMBOL_NAME(sys_create_module) + .long SYMBOL_NAME(sys_init_module) + .long SYMBOL_NAME(sys_delete_module) + .long SYMBOL_NAME(sys_get_kernel_syms) /* 130 */ + .long SYMBOL_NAME(sys_quotactl) + .long SYMBOL_NAME(sys_getpgid) + .long SYMBOL_NAME(sys_fchdir) + .long SYMBOL_NAME(sys_bdflush) + .long SYMBOL_NAME(sys_sysfs) /* 135 */ + .long SYMBOL_NAME(sys_personality) + .long SYMBOL_NAME(sys_ni_syscall) /* for afs_syscall */ + .long SYMBOL_NAME(sys_setfsuid) + .long SYMBOL_NAME(sys_setfsgid) + .long SYMBOL_NAME(sys_llseek) /* 140 */ + .long SYMBOL_NAME(sys_getdents) + .long SYMBOL_NAME(sys_select) + .long SYMBOL_NAME(sys_flock) + .long SYMBOL_NAME(sys_msync) + .long SYMBOL_NAME(sys_readv) /* 145 */ + .long SYMBOL_NAME(sys_writev) + .long SYMBOL_NAME(sys_getsid) + .long SYMBOL_NAME(sys_fdatasync) + .long SYMBOL_NAME(sys_sysctl) + .long SYMBOL_NAME(sys_mlock) /* 150 */ + .long SYMBOL_NAME(sys_munlock) + .long SYMBOL_NAME(sys_mlockall) + .long SYMBOL_NAME(sys_munlockall) + .long SYMBOL_NAME(sys_sched_setparam) + .long SYMBOL_NAME(sys_sched_getparam) /* 155 */ + .long SYMBOL_NAME(sys_sched_setscheduler) + .long SYMBOL_NAME(sys_sched_getscheduler) + .long SYMBOL_NAME(sys_sched_yield) + .long SYMBOL_NAME(sys_sched_get_priority_max) + .long SYMBOL_NAME(sys_sched_get_priority_min) /* 160 */ + .long SYMBOL_NAME(sys_sched_rr_get_interval) + .long SYMBOL_NAME(sys_nanosleep) + .long SYMBOL_NAME(sys_mremap) + .long SYMBOL_NAME(sys_setresuid) + .long SYMBOL_NAME(sys_getresuid) + .rept NR_syscalls-165 + .long SYMBOL_NAME(sys_ni_syscall) + .endr diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index a3e0df213..fdf38c818 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -9,17 +9,10 @@ */ .text -.globl _idt,_gdt,_stext,__stext -.globl _swapper_pg_dir,_pg0 -.globl _empty_bad_page -.globl _empty_bad_page_table -.globl _empty_zero_page -.globl _floppy_track_buffer - -#define __ASSEMBLY__ #include <linux/tasks.h> -#include <linux/fd.h> +#include <linux/linkage.h> #include <asm/segment.h> +#include <linux/config.h> #define CL_MAGIC_ADDR 0x90020 #define CL_MAGIC 0xA33F @@ -27,25 +20,56 @@ #define CL_OFFSET 0x90022 /* - * swapper_pg_dir is the main page directory, address 0x00001000 (or at - * address 0x00101000 for a compressed boot). + * swapper_pg_dir is the main page directory, address 0x00101000 */ -_stext: -__stext: +ENTRY(stext) +ENTRY(_stext) startup_32: +/* + * Set segments to known values + */ cld movl $(KERNEL_DS),%eax mov %ax,%ds mov %ax,%es mov %ax,%fs mov %ax,%gs +/* + * Setup paging (the tables are already set up, just switch them on) + */ + movl $0x101000,%eax + movl %eax,%cr3 /* set the page table pointer.. */ + movl %cr0,%eax + orl $0x80000000,%eax + movl %eax,%cr0 /* ..and set paging (PG) bit */ + jmp 1f /* flush the prefetch-queue */ +1: + movl $1f,%eax + jmp *%eax /* make sure eip is relocated */ +1: + +#ifdef __SMP__ + orw %bx,%bx + jz 1f /* Initial CPU cleans BSS */ +/* + * Set up the stack + */ + mov %ax,%ss + xorl %eax,%eax + movw %cx, %ax + movl %eax,%esp + pushl $0 + popfl + jmp checkCPUtype +1: lss stack_start,%esp +#endif __SMP__ /* * Clear BSS first so that there are no surprises... */ xorl %eax,%eax - movl $__edata,%edi - movl $__end,%ecx + movl $ SYMBOL_NAME(_edata),%edi + movl $ SYMBOL_NAME(_end),%ecx subl %edi,%ecx cld rep @@ -73,7 +97,7 @@ startup_32: * is for the command line. */ movl $0x90000,%esi - movl $_empty_zero_page,%edi + movl $ SYMBOL_NAME(empty_zero_page),%edi movl $512,%ecx cld rep @@ -84,20 +108,24 @@ startup_32: stosl cmpw $(CL_MAGIC),CL_MAGIC_ADDR jne 1f - movl $_empty_zero_page+2048,%edi + movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi movzwl CL_OFFSET,%esi addl $(CL_BASE_ADDR),%esi movl $2048,%ecx rep movsb 1: +#ifdef __SMP__ +checkCPUtype: +#endif + /* check if it is 486 or 386. */ /* * XXX - this does a lot of unnecessary setup. Alignment checks don't * apply at our cpl of 0 and the stack ought to be aligned already, and * we don't need to preserve eflags. */ - movl $3,_x86 + movl $3, SYMBOL_NAME(x86) pushfl # push EFLAGS popl %eax # get EFLAGS movl %eax,%ecx # save original EFLAGS @@ -109,7 +137,7 @@ startup_32: xorl %ecx,%eax # change in flags andl $0x40000,%eax # check if AC bit changed je is386 - movl $4,_x86 + movl $4,SYMBOL_NAME(x86) movl %ecx,%eax xorl $0x200000,%eax # check ID flag pushl %eax @@ -121,24 +149,25 @@ startup_32: je is486 isnew: pushl %ecx # restore original EFLAGS popfl + incl SYMBOL_NAME(have_cpuid) # we have CPUID /* get processor type */ movl $1, %eax # Use the CPUID instruction to .byte 0x0f, 0xa2 # check the processor type movb %al, %cl # save reg for future use andb $0x0f,%ah # mask processor family - movb %ah, _x86 + movb %ah,SYMBOL_NAME(x86) andb $0xf0, %eax # mask model shrb $4, %al - movb %al, _x86_model + movb %al,SYMBOL_NAME(x86_model) andb $0x0f, %cl # mask mask revision - movb %cl, _x86_mask - movl %edx, _x86_capability + movb %cl,SYMBOL_NAME(x86_mask) + movl %edx,SYMBOL_NAME(x86_capability) /* get vendor info */ xorl %eax, %eax # call CPUID with 0 -> return vendor ID .byte 0x0f, 0xa2 # CPUID - movl %ebx, _x86_vendor_id # lo 4 chars - movl %edx, _x86_vendor_id+4 # next 4 chars - movl %ecx, _x86_vendor_id+8 # last 4 chars + movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars + movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars + movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars movl %cr0,%eax # 486+ andl $0x80000011,%eax # Save PG,PE,ET @@ -157,7 +186,26 @@ is386: pushl %ecx # restore original EFLAGS orl $2,%eax # set MP 2: movl %eax,%cr0 call check_x87 - call setup_paging +#ifdef __SMP__ + movb ready,%eax + orb %eax,%eax + jz 3f +#ifdef GAS_KNOWS_CR4 + movl %cr4,%eax + orl $16,%eax + movl %eax,%cr4 +#else + .byte 0x0f,0x20,0xe0 + orl $16,%eax + .byte 0x0f,0x22,0xe0 +#endif + jmp 4f +#endif +3: +#ifdef __SMP__ + incb ready +#endif +4: lgdt gdt_descr lidt idt_descr ljmp $(KERNEL_CS),$1f @@ -166,23 +214,32 @@ is386: pushl %ecx # restore original EFLAGS mov %ax,%es mov %ax,%fs mov %ax,%gs - lss stack_start,%esp +#ifdef __SMP__ + movl $(KERNEL_DS), %eax + mov %ax,%ss # Reload the stack pointer (segment only) +#else + lss stack_start,%esp # Load processor stack +#endif xorl %eax,%eax lldt %ax pushl %eax # These are the parameters to main :-) pushl %eax pushl %eax cld # gcc2 wants the direction flag cleared at all times - call _start_kernel + call SYMBOL_NAME(start_kernel) L6: jmp L6 # main should never return here, but # just in case, we know what happens. +#ifdef __SMP__ +ready: .byte 0 +#endif + /* * We depend on ET to be correct. This checks for 287/387. */ check_x87: - movb $0,_hard_math + movb $0,SYMBOL_NAME(hard_math) clts fninit fstsw %ax @@ -192,8 +249,8 @@ check_x87: xorl $4,%eax /* set EM */ movl %eax,%cr0 ret -.align 2 -1: movb $1,_hard_math + ALIGN +1: movb $1,SYMBOL_NAME(hard_math) .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ ret @@ -213,7 +270,7 @@ setup_idt: movw %dx,%ax /* selector = 0x0010 = cs */ movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ - lea _idt,%edi + lea SYMBOL_NAME(idt),%edi mov $256,%ecx rp_sidt: movl %eax,(%edi) @@ -225,86 +282,179 @@ rp_sidt: /* - * Setup_paging - * - * This routine sets up paging by setting the page bit - * in cr0. The page tables are set up, identity-mapping - * the first 4MB. The rest are initialized later. - * - * (ref: added support for up to 32mb, 17Apr92) -- Rik Faith - * (ref: update, 25Sept92) -- croutons@crunchy.uucp - * (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit) - */ -.align 2 -setup_paging: - movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */ - xorl %eax,%eax - movl $_swapper_pg_dir,%edi /* swapper_pg_dir is at 0x1000 */ - cld;rep;stosl -/* Identity-map the kernel in low 4MB memory for ease of transition */ - movl $_pg0+7,_swapper_pg_dir /* set present bit/user r/w */ -/* But the real place is at 0xC0000000 */ - movl $_pg0+7,_swapper_pg_dir+3072 /* set present bit/user r/w */ - movl $_pg0+4092,%edi - movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */ - std -1: stosl /* fill the page backwards - more efficient :-) */ - subl $0x1000,%eax - jge 1b - cld - movl $_swapper_pg_dir,%eax - movl %eax,%cr3 /* cr3 - page directory start */ - movl %cr0,%eax - orl $0x80000000,%eax - movl %eax,%cr0 /* set paging (PG) bit */ - ret /* this also flushes the prefetch-queue */ - -/* * page 0 is made non-existent, so that kernel NULL pointer references get - * caught. Thus the swapper page directory has been moved to 0x1000 - * - * XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte, + * caught. Thus the swapper page directory has been moved to 0x101000 * with the introduction of the compressed boot code. Theoretically, * the original design of overlaying the startup code with the swapper * page directory is still possible --- it would reduce the size of the kernel * by 2-3k. This would be a good thing to do at some point..... + * + * This is initialized to create a identity-mapping at 0-4M (for bootup + * purposes) and another mapping of the 0-4M area at virtual address + * 0xC0000000. */ .org 0x1000 -_swapper_pg_dir: +ENTRY(swapper_pg_dir) + .long 0x00102007 + .fill 767,4,0 + .long 0x00102007 + .fill 127,4,0 + /* * The page tables are initialized to only 4MB here - the final page - * tables are set up later depending on memory size. + * tables are set up later depending on memory size. The "007" at the + * end doesn't mean with right to kill, but PRESENT+RW+USER */ .org 0x2000 -_pg0: +ENTRY(pg0) + .long 0x000007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007 + .long 0x008007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007 + .long 0x010007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007 + .long 0x018007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007 + .long 0x020007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007 + .long 0x028007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007 + .long 0x030007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007 + .long 0x038007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007 + .long 0x040007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007 + .long 0x048007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007 + .long 0x050007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007 + .long 0x058007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007 + .long 0x060007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007 + .long 0x068007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007 + .long 0x070007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007 + .long 0x078007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007 + .long 0x080007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007 + .long 0x088007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007 + .long 0x090007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007 + .long 0x098007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007 + .long 0x0a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007 + .long 0x0a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007 + .long 0x0b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007 + .long 0x0b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007 + .long 0x0c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007 + .long 0x0c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007 + .long 0x0d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007 + .long 0x0d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007 + .long 0x0e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007 + .long 0x0e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007 + .long 0x0f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007 + .long 0x0f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007 + .long 0x100007,0x101007,0x102007,0x103007,0x104007,0x105007,0x106007,0x107007 + .long 0x108007,0x109007,0x10a007,0x10b007,0x10c007,0x10d007,0x10e007,0x10f007 + .long 0x110007,0x111007,0x112007,0x113007,0x114007,0x115007,0x116007,0x117007 + .long 0x118007,0x119007,0x11a007,0x11b007,0x11c007,0x11d007,0x11e007,0x11f007 + .long 0x120007,0x121007,0x122007,0x123007,0x124007,0x125007,0x126007,0x127007 + .long 0x128007,0x129007,0x12a007,0x12b007,0x12c007,0x12d007,0x12e007,0x12f007 + .long 0x130007,0x131007,0x132007,0x133007,0x134007,0x135007,0x136007,0x137007 + .long 0x138007,0x139007,0x13a007,0x13b007,0x13c007,0x13d007,0x13e007,0x13f007 + .long 0x140007,0x141007,0x142007,0x143007,0x144007,0x145007,0x146007,0x147007 + .long 0x148007,0x149007,0x14a007,0x14b007,0x14c007,0x14d007,0x14e007,0x14f007 + .long 0x150007,0x151007,0x152007,0x153007,0x154007,0x155007,0x156007,0x157007 + .long 0x158007,0x159007,0x15a007,0x15b007,0x15c007,0x15d007,0x15e007,0x15f007 + .long 0x160007,0x161007,0x162007,0x163007,0x164007,0x165007,0x166007,0x167007 + .long 0x168007,0x169007,0x16a007,0x16b007,0x16c007,0x16d007,0x16e007,0x16f007 + .long 0x170007,0x171007,0x172007,0x173007,0x174007,0x175007,0x176007,0x177007 + .long 0x178007,0x179007,0x17a007,0x17b007,0x17c007,0x17d007,0x17e007,0x17f007 + .long 0x180007,0x181007,0x182007,0x183007,0x184007,0x185007,0x186007,0x187007 + .long 0x188007,0x189007,0x18a007,0x18b007,0x18c007,0x18d007,0x18e007,0x18f007 + .long 0x190007,0x191007,0x192007,0x193007,0x194007,0x195007,0x196007,0x197007 + .long 0x198007,0x199007,0x19a007,0x19b007,0x19c007,0x19d007,0x19e007,0x19f007 + .long 0x1a0007,0x1a1007,0x1a2007,0x1a3007,0x1a4007,0x1a5007,0x1a6007,0x1a7007 + .long 0x1a8007,0x1a9007,0x1aa007,0x1ab007,0x1ac007,0x1ad007,0x1ae007,0x1af007 + .long 0x1b0007,0x1b1007,0x1b2007,0x1b3007,0x1b4007,0x1b5007,0x1b6007,0x1b7007 + .long 0x1b8007,0x1b9007,0x1ba007,0x1bb007,0x1bc007,0x1bd007,0x1be007,0x1bf007 + .long 0x1c0007,0x1c1007,0x1c2007,0x1c3007,0x1c4007,0x1c5007,0x1c6007,0x1c7007 + .long 0x1c8007,0x1c9007,0x1ca007,0x1cb007,0x1cc007,0x1cd007,0x1ce007,0x1cf007 + .long 0x1d0007,0x1d1007,0x1d2007,0x1d3007,0x1d4007,0x1d5007,0x1d6007,0x1d7007 + .long 0x1d8007,0x1d9007,0x1da007,0x1db007,0x1dc007,0x1dd007,0x1de007,0x1df007 + .long 0x1e0007,0x1e1007,0x1e2007,0x1e3007,0x1e4007,0x1e5007,0x1e6007,0x1e7007 + .long 0x1e8007,0x1e9007,0x1ea007,0x1eb007,0x1ec007,0x1ed007,0x1ee007,0x1ef007 + .long 0x1f0007,0x1f1007,0x1f2007,0x1f3007,0x1f4007,0x1f5007,0x1f6007,0x1f7007 + .long 0x1f8007,0x1f9007,0x1fa007,0x1fb007,0x1fc007,0x1fd007,0x1fe007,0x1ff007 + .long 0x200007,0x201007,0x202007,0x203007,0x204007,0x205007,0x206007,0x207007 + .long 0x208007,0x209007,0x20a007,0x20b007,0x20c007,0x20d007,0x20e007,0x20f007 + .long 0x210007,0x211007,0x212007,0x213007,0x214007,0x215007,0x216007,0x217007 + .long 0x218007,0x219007,0x21a007,0x21b007,0x21c007,0x21d007,0x21e007,0x21f007 + .long 0x220007,0x221007,0x222007,0x223007,0x224007,0x225007,0x226007,0x227007 + .long 0x228007,0x229007,0x22a007,0x22b007,0x22c007,0x22d007,0x22e007,0x22f007 + .long 0x230007,0x231007,0x232007,0x233007,0x234007,0x235007,0x236007,0x237007 + .long 0x238007,0x239007,0x23a007,0x23b007,0x23c007,0x23d007,0x23e007,0x23f007 + .long 0x240007,0x241007,0x242007,0x243007,0x244007,0x245007,0x246007,0x247007 + .long 0x248007,0x249007,0x24a007,0x24b007,0x24c007,0x24d007,0x24e007,0x24f007 + .long 0x250007,0x251007,0x252007,0x253007,0x254007,0x255007,0x256007,0x257007 + .long 0x258007,0x259007,0x25a007,0x25b007,0x25c007,0x25d007,0x25e007,0x25f007 + .long 0x260007,0x261007,0x262007,0x263007,0x264007,0x265007,0x266007,0x267007 + .long 0x268007,0x269007,0x26a007,0x26b007,0x26c007,0x26d007,0x26e007,0x26f007 + .long 0x270007,0x271007,0x272007,0x273007,0x274007,0x275007,0x276007,0x277007 + .long 0x278007,0x279007,0x27a007,0x27b007,0x27c007,0x27d007,0x27e007,0x27f007 + .long 0x280007,0x281007,0x282007,0x283007,0x284007,0x285007,0x286007,0x287007 + .long 0x288007,0x289007,0x28a007,0x28b007,0x28c007,0x28d007,0x28e007,0x28f007 + .long 0x290007,0x291007,0x292007,0x293007,0x294007,0x295007,0x296007,0x297007 + .long 0x298007,0x299007,0x29a007,0x29b007,0x29c007,0x29d007,0x29e007,0x29f007 + .long 0x2a0007,0x2a1007,0x2a2007,0x2a3007,0x2a4007,0x2a5007,0x2a6007,0x2a7007 + .long 0x2a8007,0x2a9007,0x2aa007,0x2ab007,0x2ac007,0x2ad007,0x2ae007,0x2af007 + .long 0x2b0007,0x2b1007,0x2b2007,0x2b3007,0x2b4007,0x2b5007,0x2b6007,0x2b7007 + .long 0x2b8007,0x2b9007,0x2ba007,0x2bb007,0x2bc007,0x2bd007,0x2be007,0x2bf007 + .long 0x2c0007,0x2c1007,0x2c2007,0x2c3007,0x2c4007,0x2c5007,0x2c6007,0x2c7007 + .long 0x2c8007,0x2c9007,0x2ca007,0x2cb007,0x2cc007,0x2cd007,0x2ce007,0x2cf007 + .long 0x2d0007,0x2d1007,0x2d2007,0x2d3007,0x2d4007,0x2d5007,0x2d6007,0x2d7007 + .long 0x2d8007,0x2d9007,0x2da007,0x2db007,0x2dc007,0x2dd007,0x2de007,0x2df007 + .long 0x2e0007,0x2e1007,0x2e2007,0x2e3007,0x2e4007,0x2e5007,0x2e6007,0x2e7007 + .long 0x2e8007,0x2e9007,0x2ea007,0x2eb007,0x2ec007,0x2ed007,0x2ee007,0x2ef007 + .long 0x2f0007,0x2f1007,0x2f2007,0x2f3007,0x2f4007,0x2f5007,0x2f6007,0x2f7007 + .long 0x2f8007,0x2f9007,0x2fa007,0x2fb007,0x2fc007,0x2fd007,0x2fe007,0x2ff007 + .long 0x300007,0x301007,0x302007,0x303007,0x304007,0x305007,0x306007,0x307007 + .long 0x308007,0x309007,0x30a007,0x30b007,0x30c007,0x30d007,0x30e007,0x30f007 + .long 0x310007,0x311007,0x312007,0x313007,0x314007,0x315007,0x316007,0x317007 + .long 0x318007,0x319007,0x31a007,0x31b007,0x31c007,0x31d007,0x31e007,0x31f007 + .long 0x320007,0x321007,0x322007,0x323007,0x324007,0x325007,0x326007,0x327007 + .long 0x328007,0x329007,0x32a007,0x32b007,0x32c007,0x32d007,0x32e007,0x32f007 + .long 0x330007,0x331007,0x332007,0x333007,0x334007,0x335007,0x336007,0x337007 + .long 0x338007,0x339007,0x33a007,0x33b007,0x33c007,0x33d007,0x33e007,0x33f007 + .long 0x340007,0x341007,0x342007,0x343007,0x344007,0x345007,0x346007,0x347007 + .long 0x348007,0x349007,0x34a007,0x34b007,0x34c007,0x34d007,0x34e007,0x34f007 + .long 0x350007,0x351007,0x352007,0x353007,0x354007,0x355007,0x356007,0x357007 + .long 0x358007,0x359007,0x35a007,0x35b007,0x35c007,0x35d007,0x35e007,0x35f007 + .long 0x360007,0x361007,0x362007,0x363007,0x364007,0x365007,0x366007,0x367007 + .long 0x368007,0x369007,0x36a007,0x36b007,0x36c007,0x36d007,0x36e007,0x36f007 + .long 0x370007,0x371007,0x372007,0x373007,0x374007,0x375007,0x376007,0x377007 + .long 0x378007,0x379007,0x37a007,0x37b007,0x37c007,0x37d007,0x37e007,0x37f007 + .long 0x380007,0x381007,0x382007,0x383007,0x384007,0x385007,0x386007,0x387007 + .long 0x388007,0x389007,0x38a007,0x38b007,0x38c007,0x38d007,0x38e007,0x38f007 + .long 0x390007,0x391007,0x392007,0x393007,0x394007,0x395007,0x396007,0x397007 + .long 0x398007,0x399007,0x39a007,0x39b007,0x39c007,0x39d007,0x39e007,0x39f007 + .long 0x3a0007,0x3a1007,0x3a2007,0x3a3007,0x3a4007,0x3a5007,0x3a6007,0x3a7007 + .long 0x3a8007,0x3a9007,0x3aa007,0x3ab007,0x3ac007,0x3ad007,0x3ae007,0x3af007 + .long 0x3b0007,0x3b1007,0x3b2007,0x3b3007,0x3b4007,0x3b5007,0x3b6007,0x3b7007 + .long 0x3b8007,0x3b9007,0x3ba007,0x3bb007,0x3bc007,0x3bd007,0x3be007,0x3bf007 + .long 0x3c0007,0x3c1007,0x3c2007,0x3c3007,0x3c4007,0x3c5007,0x3c6007,0x3c7007 + .long 0x3c8007,0x3c9007,0x3ca007,0x3cb007,0x3cc007,0x3cd007,0x3ce007,0x3cf007 + .long 0x3d0007,0x3d1007,0x3d2007,0x3d3007,0x3d4007,0x3d5007,0x3d6007,0x3d7007 + .long 0x3d8007,0x3d9007,0x3da007,0x3db007,0x3dc007,0x3dd007,0x3de007,0x3df007 + .long 0x3e0007,0x3e1007,0x3e2007,0x3e3007,0x3e4007,0x3e5007,0x3e6007,0x3e7007 + .long 0x3e8007,0x3e9007,0x3ea007,0x3eb007,0x3ec007,0x3ed007,0x3ee007,0x3ef007 + .long 0x3f0007,0x3f1007,0x3f2007,0x3f3007,0x3f4007,0x3f5007,0x3f6007,0x3f7007 + .long 0x3f8007,0x3f9007,0x3fa007,0x3fb007,0x3fc007,0x3fd007,0x3fe007,0x3ff007 .org 0x3000 -_empty_bad_page: +ENTRY(empty_bad_page) .org 0x4000 -_empty_bad_page_table: +ENTRY(empty_bad_page_table) .org 0x5000 -_empty_zero_page: +ENTRY(empty_zero_page) .org 0x6000 -/* - * floppy_track_buffer is used to buffer one track of floppy data: it - * has to be separate from the tmp_floppy area, as otherwise a single- - * sector read/write can mess it up. It can contain one full cylinder (sic) of - * data (36*2*512 bytes). - */ -_floppy_track_buffer: - .fill 512*2*MAX_BUFFER_SECTORS,1,0 - + stack_start: - .long _init_user_stack+4096 + .long SYMBOL_NAME(init_user_stack)+4096 .long KERNEL_DS /* This is the default interrupt "handler" :-) */ int_msg: .asciz "Unknown interrupt\n" -.align 2 + ALIGN ignore_int: cld pushl %eax @@ -318,7 +468,7 @@ ignore_int: mov %ax,%es mov %ax,%fs pushl $int_msg - call _printk + call SYMBOL_NAME(printk) popl %eax pop %fs pop %es @@ -331,34 +481,41 @@ ignore_int: /* * The interrupt descriptor table has room for 256 idt's */ -.align 4 + ALIGN .word 0 idt_descr: .word 256*8-1 # idt contains 256 entries - .long 0xc0000000+_idt + .long SYMBOL_NAME(idt) -.align 4 -_idt: +ENTRY(idt) .fill 256,8,0 # idt is uninitialized -.align 4 + ALIGN .word 0 gdt_descr: +#ifdef CONFIG_APM + .word (11+2*NR_TASKS)*8-1 +#else .word (8+2*NR_TASKS)*8-1 - .long 0xc0000000+_gdt +#endif + .long SYMBOL_NAME(gdt) /* * This gdt setup gives the kernel a 1GB address space at virtual * address 0xC0000000 - space enough for expansion, I hope. */ -.align 4 -_gdt: +ENTRY(gdt) .quad 0x0000000000000000 /* NULL descriptor */ .quad 0x0000000000000000 /* not used */ - .quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */ - .quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */ + .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ + .quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */ .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */ .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */ .quad 0x0000000000000000 /* not used */ .quad 0x0000000000000000 /* not used */ .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ +#ifdef CONFIG_APM + .quad 0x00c09a0000000000 /* APM CS code */ + .quad 0x00809a0000000000 /* APM CS 16 code (16 bit) */ + .quad 0x00c0920000000000 /* APM DS data */ +#endif diff --git a/arch/i386/kernel/hexify.c b/arch/i386/kernel/hexify.c new file mode 100644 index 000000000..daa331fec --- /dev/null +++ b/arch/i386/kernel/hexify.c @@ -0,0 +1,31 @@ +#include <stdio.h> + + +void main() +{ + int c; + int comma=0; + int count=0; + while((c=getchar())!=EOF) + { + unsigned char x=c; + if(comma) + printf(","); + else + comma=1; + if(count==8) + { + count=0; + printf("\n"); + } + if(count==0) + printf("\t"); + printf("0x%02X",c); + count++; + } + if(count) + printf("\n"); + exit(0); +} + + diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c index c949f70cd..bcefeef9b 100644 --- a/arch/i386/kernel/ioport.c +++ b/arch/i386/kernel/ioport.c @@ -75,8 +75,8 @@ unsigned int *stack; */ asmlinkage int sys_iopl(long ebx,long ecx,long edx, long esi, long edi, long ebp, long eax, long ds, - long es, long fs, long gs, long orig_eax, - long eip,long cs,long eflags,long esp,long ss) + long es, long orig_eax, long eip, long cs, + long eflags, long esp, long ss) { unsigned int level = ebx; diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 23085dc6c..e131e5a45 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -23,54 +23,68 @@ #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/timex.h> +#include <linux/malloc.h> +#include <linux/random.h> #include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/bitops.h> +#include <asm/smp.h> #define CR0_NE 32 static unsigned char cache_21 = 0xff; static unsigned char cache_A1 = 0xff; -void disable_irq(unsigned int irq_nr) +#ifdef __SMP_PROF__ +static unsigned int int_count[NR_CPUS][NR_IRQS] = {{0},}; +#endif + +static inline void mask_irq(unsigned int irq_nr) { - unsigned long flags; unsigned char mask; mask = 1 << (irq_nr & 7); - save_flags(flags); if (irq_nr < 8) { - cli(); cache_21 |= mask; outb(cache_21,0x21); - restore_flags(flags); - return; + } else { + cache_A1 |= mask; + outb(cache_A1,0xA1); } - cli(); - cache_A1 |= mask; - outb(cache_A1,0xA1); - restore_flags(flags); } -void enable_irq(unsigned int irq_nr) +static inline void unmask_irq(unsigned int irq_nr) { - unsigned long flags; unsigned char mask; mask = ~(1 << (irq_nr & 7)); - save_flags(flags); if (irq_nr < 8) { - cli(); cache_21 &= mask; outb(cache_21,0x21); - restore_flags(flags); - return; + } else { + cache_A1 &= mask; + outb(cache_A1,0xA1); } +} + +void disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + + save_flags(flags); + cli(); + mask_irq(irq_nr); + restore_flags(flags); +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + save_flags(flags); cli(); - cache_A1 &= mask; - outb(cache_A1,0xA1); + unmask_irq(irq_nr); restore_flags(flags); } @@ -88,8 +102,16 @@ void enable_irq(unsigned int irq_nr) * atomic. The specific handler is chosen depending on the SA_INTERRUPT * flag when installing a handler. Finally, one "bad interrupt" handler, that * is used when no handler is present. + * + * The timer interrupt is handled specially to insure that the jiffies + * variable is updated at all times. Specifically, the timer interrupt is + * just like the complete handlers except that it is invoked with interrupts + * disabled and should never re-enable them. If other interrupts were + * allowed to be processed while the timer interrupt is active, then the + * other interrupts would have to avoid using the jiffies variable for delay + * and interval timing operations to avoid hanging the system. */ -BUILD_IRQ(FIRST,0,0x01) +BUILD_TIMER_IRQ(FIRST,0,0x01) BUILD_IRQ(FIRST,1,0x02) BUILD_IRQ(FIRST,2,0x04) BUILD_IRQ(FIRST,3,0x08) @@ -102,19 +124,29 @@ BUILD_IRQ(SECOND,9,0x02) BUILD_IRQ(SECOND,10,0x04) BUILD_IRQ(SECOND,11,0x08) BUILD_IRQ(SECOND,12,0x10) +#ifdef __SMP__ +BUILD_MSGIRQ(SECOND,13,0x20) +#else BUILD_IRQ(SECOND,13,0x20) +#endif BUILD_IRQ(SECOND,14,0x40) BUILD_IRQ(SECOND,15,0x80) +#ifdef __SMP__ +BUILD_RESCHEDIRQ(16) +#endif /* * Pointers to the low-level handlers: first the general ones, then the * fast ones, then the bad ones. */ -static void (*interrupt[16])(void) = { +static void (*interrupt[17])(void) = { IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt, IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, - IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt + IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt +#ifdef __SMP__ + ,IRQ16_interrupt +#endif }; static void (*fast_interrupt[16])(void) = { @@ -142,39 +174,164 @@ static void (*bad_interrupt[16])(void) = { /* * Initial irq handlers. */ -struct irqaction { - void (*handler)(int, struct pt_regs *); - unsigned long flags; - unsigned long mask; - const char *name; -}; -static struct irqaction irq_action[16] = { - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL } +static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } + +#ifdef __SMP__ + +/* + * On SMP boards, irq13 is used for interprocessor interrupts (IPI's). + */ +static struct irqaction irq13 = { smp_message_irq, SA_INTERRUPT, 0, "IPI", NULL, NULL }; + +#else + +/* + * Note that on a 486, we don't want to do a SIGFPE on an irq13 + * as the irq is unreliable, and exception 16 works correctly + * (ie as explained in the intel literature). On a 386, you + * can't use exception 16 due to bad IBM design, so we have to + * rely on the less exact irq13. + * + * Careful.. Not only is IRQ13 unreliable, but it is also + * leads to races. IBM designers who came up with it should + * be shot. + */ + + +static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) +{ + outb(0,0xF0); + if (ignore_irq13 || !hard_math) + return; + math_error(); +} + +static struct irqaction irq13 = { math_error_irq, 0, 0, "math error", NULL, NULL }; + +#endif + +/* + * IRQ2 is cascade interrupt to second interrupt controller + */ +static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL}; + +static struct irqaction *irq_action[16] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL }; int get_irq_list(char *buf) { int i, len = 0; - struct irqaction * action = irq_action; + struct irqaction * action; - for (i = 0 ; i < 16 ; i++, action++) { - if (!action->handler) + for (i = 0 ; i < 16 ; i++) { + action = irq_action[i]; + if (!action) continue; - len += sprintf(buf+len, "%2d: %8d %c %s\n", + len += sprintf(buf+len, "%2d: %10u %c %s", i, kstat.interrupts[i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + len += sprintf(buf+len, "\n"); + } +/* + * Linus - should you add NMI counts here ????? + */ +#ifdef __SMP_PROF__ + len+=sprintf(buf+len, "IPI: %8lu received\n", + ipi_count); +#endif + return len; +} + +#ifdef __SMP_PROF__ + +int get_smp_prof_list(char *buf) { + int i,j, len = 0; + struct irqaction * action; + unsigned long sum_spins = 0; + unsigned long sum_spins_syscall = 0; + unsigned long sum_spins_sys_idle = 0; + unsigned long sum_smp_idle_count = 0; + + for (i=0;i<smp_num_cpus;i++) { + int cpunum = cpu_logical_map[i]; + sum_spins+=smp_spins[cpunum]; + sum_spins_syscall+=smp_spins_syscall[cpunum]; + sum_spins_sys_idle+=smp_spins_sys_idle[cpunum]; + sum_smp_idle_count+=smp_idle_count[cpunum]; + } + + len += sprintf(buf+len,"CPUS: %10i \n", smp_num_cpus); + len += sprintf(buf+len," SUM "); + for (i=0;i<smp_num_cpus;i++) + len += sprintf(buf+len," P%1d ",cpu_logical_map[i]); + len += sprintf(buf+len,"\n"); + for (i = 0 ; i < NR_IRQS ; i++) { + action = *(i + irq_action); + if (!action || !action->handler) + continue; + len += sprintf(buf+len, "%3d: %10d ", + i, kstat.interrupts[i]); + for (j=0;j<smp_num_cpus;j++) + len+=sprintf(buf+len, "%10d ", + int_count[cpu_logical_map[j]][i]); + len += sprintf(buf+len, "%c %s\n", + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } } + len+=sprintf(buf+len, "LCK: %10lu", + sum_spins); + + for (i=0;i<smp_num_cpus;i++) + len+=sprintf(buf+len," %10lu",smp_spins[cpu_logical_map[i]]); + + len +=sprintf(buf+len," spins from int\n"); + + len+=sprintf(buf+len, "LCK: %10lu", + sum_spins_syscall); + + for (i=0;i<smp_num_cpus;i++) + len+=sprintf(buf+len," %10lu",smp_spins_syscall[cpu_logical_map[i]]); + + len +=sprintf(buf+len," spins from syscall\n"); + + len+=sprintf(buf+len, "LCK: %10lu", + sum_spins_sys_idle); + + for (i=0;i<smp_num_cpus;i++) + len+=sprintf(buf+len," %10lu",smp_spins_sys_idle[cpu_logical_map[i]]); + + len +=sprintf(buf+len," spins from sysidle\n"); + len+=sprintf(buf+len,"IDLE %10lu",sum_smp_idle_count); + + for (i=0;i<smp_num_cpus;i++) + len+=sprintf(buf+len," %10lu",smp_idle_count[cpu_logical_map[i]]); + + len +=sprintf(buf+len," idle ticks\n"); + + len+=sprintf(buf+len, "IPI: %10lu received\n", + ipi_count); + return len; } +#endif + + /* * do_IRQ handles IRQ's that have been installed without the @@ -185,10 +342,25 @@ int get_irq_list(char *buf) */ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { - struct irqaction * action = irq + irq_action; + struct irqaction * action = *(irq + irq_action); + int do_random = 0; + +#ifdef __SMP__ + if(smp_threads_ready && active_kernel_processor!=smp_processor_id()) + panic("IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id()); +#endif kstat.interrupts[irq]++; - action->handler(irq, regs); +#ifdef __SMP_PROF__ + int_count[smp_processor_id()][irq]++; +#endif + while (action) { + do_random |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); } /* @@ -198,145 +370,159 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) */ asmlinkage void do_fast_IRQ(int irq) { - struct irqaction * action = irq + irq_action; + struct irqaction * action = *(irq + irq_action); + int do_random = 0; + +#ifdef __SMP__ + /* IRQ 13 is allowed - that's a flush tlb */ + if(smp_threads_ready && active_kernel_processor!=smp_processor_id() && irq!=13) + panic("fast_IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id()); +#endif kstat.interrupts[irq]++; - action->handler(irq, NULL); +#ifdef __SMP_PROF__ + int_count[smp_processor_id()][irq]++; +#endif + while (action) { + do_random |= action->flags; + action->handler(irq, action->dev_id, NULL); + action = action->next; + } + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); } -#define SA_PROBE SA_ONESHOT +int setup_x86_irq(int irq, struct irqaction * new) +{ + int shared = 0; + struct irqaction *old, **p; + unsigned long flags; + + p = irq_action + irq; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) + return -EBUSY; + + /* Can't share interrupts unless both are same type */ + if ((old->flags ^ new->flags) & SA_INTERRUPT) + return -EBUSY; + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + if (new->flags & SA_SAMPLE_RANDOM) + rand_initialize_irq(irq); + + save_flags(flags); + cli(); + *p = new; + + if (!shared) { + if (new->flags & SA_INTERRUPT) + set_intr_gate(0x20+irq,fast_interrupt[irq]); + else + set_intr_gate(0x20+irq,interrupt[irq]); + unmask_irq(irq); + } + restore_flags(flags); + return 0; +} -int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), - unsigned long irqflags, const char * devname) +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) { + int retval; struct irqaction * action; - unsigned long flags; if (irq > 15) return -EINVAL; - action = irq + irq_action; - if (action->handler) - return -EBUSY; if (!handler) return -EINVAL; - save_flags(flags); - cli(); + + action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; - if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */ - if (action->flags & SA_INTERRUPT) - set_intr_gate(0x20+irq,fast_interrupt[irq]); - else - set_intr_gate(0x20+irq,interrupt[irq]); - } - if (irq < 8) { - cache_21 &= ~(1<<irq); - outb(cache_21,0x21); - } else { - cache_21 &= ~(1<<2); - cache_A1 &= ~(1<<(irq-8)); - outb(cache_21,0x21); - outb(cache_A1,0xA1); - } - restore_flags(flags); - return 0; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_x86_irq(irq, action); + + if (retval) + kfree(action); + return retval; } -void free_irq(unsigned int irq) +void free_irq(unsigned int irq, void *dev_id) { - struct irqaction * action = irq + irq_action; + struct irqaction * action, **p; unsigned long flags; if (irq > 15) { printk("Trying to free IRQ%d\n",irq); return; } - if (!action->handler) { - printk("Trying to free free IRQ%d\n",irq); - return; - } - save_flags(flags); - cli(); - if (irq < 8) { - cache_21 |= 1 << irq; - outb(cache_21,0x21); - } else { - cache_A1 |= 1 << (irq-8); - outb(cache_A1,0xA1); - } - set_intr_gate(0x20+irq,bad_interrupt[irq]); - action->handler = NULL; - action->flags = 0; - action->mask = 0; - action->name = NULL; - restore_flags(flags); -} + for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; -/* - * Note that on a 486, we don't want to do a SIGFPE on a irq13 - * as the irq is unreliable, and exception 16 works correctly - * (ie as explained in the intel literature). On a 386, you - * can't use exception 16 due to bad IBM design, so we have to - * rely on the less exact irq13. - * - * Careful.. Not only is IRQ13 unreliable, but it is also - * leads to races. IBM designers who came up with it should - * be shot. - */ -static void math_error_irq(int cpl, struct pt_regs *regs) -{ - outb(0,0xF0); - if (ignore_irq13 || !hard_math) + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + if (!irq[irq_action]) { + mask_irq(irq); + set_intr_gate(0x20+irq,bad_interrupt[irq]); + } + restore_flags(flags); + kfree(action); return; - math_error(); + } + printk("Trying to free free IRQ%d\n",irq); } -static void no_action(int cpl, struct pt_regs * regs) { } - -unsigned int probe_irq_on (void) +unsigned long probe_irq_on (void) { unsigned int i, irqs = 0, irqmask; unsigned long delay; - /* first, snaffle up any unassigned irqs */ + /* first, enable any unassigned irqs */ for (i = 15; i > 0; i--) { - if (!request_irq(i, no_action, SA_PROBE, "probe")) { + if (!irq_action[i]) { enable_irq(i); irqs |= (1 << i); } } /* wait for spurious interrupts to mask themselves out again */ - for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */ + for (delay = jiffies + HZ/10; delay > jiffies; ) + /* about 100ms delay */; /* now filter out any obviously spurious interrupts */ irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; - for (i = 15; i > 0; i--) { - if (irqs & (1 << i) & irqmask) { - irqs ^= (1 << i); - free_irq(i); - } - } -#ifdef DEBUG - printk("probe_irq_on: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); -#endif - return irqs; + return irqs & ~irqmask; } -int probe_irq_off (unsigned int irqs) +int probe_irq_off (unsigned long irqs) { unsigned int i, irqmask; irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; - for (i = 15; i > 0; i--) { - if (irqs & (1 << i)) { - free_irq(i); - } - } #ifdef DEBUG - printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); + printk("probe_irq_off: irqs=0x%04lx irqmask=0x%04x\n", irqs, irqmask); #endif irqs &= irqmask; if (!irqs) @@ -350,6 +536,10 @@ int probe_irq_off (unsigned int irqs) void init_IRQ(void) { int i; + static unsigned char smptrap=0; + if(smptrap) + return; + smptrap=1; /* set the clock to 100 Hz */ outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ @@ -357,10 +547,13 @@ void init_IRQ(void) outb(LATCH >> 8 , 0x40); /* MSB */ for (i = 0; i < 16 ; i++) set_intr_gate(0x20+i,bad_interrupt[i]); - if (request_irq(2, no_action, SA_INTERRUPT, "cascade")) - printk("Unable to get IRQ2 for cascade\n"); - if (request_irq(13,math_error_irq, 0, "math error")) - printk("Unable to get IRQ13 for math-error handler\n"); + /* This bit is a hack because we don't send timer messages to all processors yet */ + /* It has to be here .. it doesn't work if you put it down the bottom - assembler explodes 8) */ +#ifdef __SMP__ + set_intr_gate(0x20+i, interrupt[i]); /* IRQ '16' - IPI for rescheduling */ +#endif request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); + setup_x86_irq(2, &irq2); + setup_x86_irq(13, &irq13); } diff --git a/arch/i386/kernel/ksyms.c b/arch/i386/kernel/ksyms.c new file mode 100644 index 000000000..bdb6e8005 --- /dev/null +++ b/arch/i386/kernel/ksyms.c @@ -0,0 +1,41 @@ +#include <linux/module.h> +#include <linux/smp.h> +#include <linux/user.h> +#include <linux/elfcore.h> + +#include <asm/semaphore.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/io.h> + +extern void dump_thread(struct pt_regs *, struct user *); +extern int dump_fpu(elf_fpregset_t *); + +static struct symbol_table arch_symbol_table = { +#include <linux/symtab_begin.h> + /* platform dependent support */ + X(EISA_bus), + X(wp_works_ok), + X(__verify_write), + X(dump_thread), + X(dump_fpu), + X(ioremap), + X(iounmap), + XNOVERS(__down_failed), + XNOVERS(__up_wakeup), +#ifdef __SMP__ + X(apic_reg), /* Needed internally for the I386 inlines */ + X(cpu_data), + X(syscall_count), + X(kernel_flag), + X(kernel_counter), + X(active_kernel_processor), + X(smp_invalidate_needed), +#endif +#include <linux/symtab_end.h> +}; + +void arch_syms_export(void) +{ + register_symtab(&arch_symbol_table); +} diff --git a/arch/i386/kernel/ldt.c b/arch/i386/kernel/ldt.c index bace95f4e..9e2f5e0cd 100644 --- a/arch/i386/kernel/ldt.c +++ b/arch/i386/kernel/ldt.c @@ -8,9 +8,11 @@ #include <linux/sched.h> #include <linux/string.h> #include <linux/mm.h> -#include <asm/segment.h> +#include <linux/vmalloc.h> + +#include <asm/uaccess.h> #include <asm/system.h> -#include <linux/ldt.h> +#include <asm/ldt.h> static int read_ldt(void * ptr, unsigned long bytecount) { @@ -30,15 +32,39 @@ static int read_ldt(void * ptr, unsigned long bytecount) error = verify_area(VERIFY_WRITE, ptr, size); if (error) return error; - memcpy_tofs(ptr, address, size); + copy_to_user(ptr, address, size); return size; } +static inline int limits_ok(struct modify_ldt_ldt_s *ldt_info) +{ + unsigned long base, limit; + /* linear address of first and last accessible byte */ + unsigned long first, last; + + base = ldt_info->base_addr; + limit = ldt_info->limit; + if (ldt_info->limit_in_pages) + limit = limit * PAGE_SIZE + PAGE_SIZE - 1; + + first = base; + last = limit + base; + + /* segment grows down? */ + if (ldt_info->contents == 1) { + /* data segment grows down */ + first = base+limit+1; + last = base+65535; + if (ldt_info->seg_32bit) + last = base-1; + } + return (last >= first && last < TASK_SIZE); +} + static int write_ldt(void * ptr, unsigned long bytecount) { struct modify_ldt_ldt_s ldt_info; unsigned long *lp; - unsigned long base, limit; int error, i; if (bytecount != sizeof(ldt_info)) @@ -47,18 +73,12 @@ static int write_ldt(void * ptr, unsigned long bytecount) if (error) return error; - memcpy_fromfs(&ldt_info, ptr, sizeof(ldt_info)); + copy_from_user(&ldt_info, ptr, sizeof(ldt_info)); if (ldt_info.contents == 3 || ldt_info.entry_number >= LDT_ENTRIES) return -EINVAL; - limit = ldt_info.limit; - base = ldt_info.base_addr; - if (ldt_info.limit_in_pages) - limit *= PAGE_SIZE; - - limit += base; - if (limit < base || limit >= 0xC0000000) + if (!limits_ok(&ldt_info)) return -EINVAL; if (!current->ldt) { diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index a5e8777bf..a26a2c6a3 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -8,6 +8,9 @@ * This file handles the architecture-dependent parts of process handling.. */ +#define __KERNEL_SYSCALLS__ +#include <stdarg.h> + #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -16,19 +19,32 @@ #include <linux/unistd.h> #include <linux/ptrace.h> #include <linux/malloc.h> -#include <linux/ldt.h> +#include <linux/vmalloc.h> #include <linux/user.h> #include <linux/a.out.h> +#include <linux/interrupt.h> +#include <linux/config.h> +#include <linux/unistd.h> +#include <linux/delay.h> +#include <linux/smp.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/system.h> #include <asm/io.h> +#include <asm/ldt.h> asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); +#ifdef CONFIG_APM +extern int apm_do_idle(void); +extern void apm_do_busy(void); +#endif + static int hlt_counter=0; +#define HARD_IDLE_TIMEOUT (HZ / 3) + void disable_hlt(void) { hlt_counter++; @@ -39,53 +55,145 @@ void enable_hlt(void) hlt_counter--; } -asmlinkage int sys_pipe(unsigned long * fildes) -{ - int fd[2]; - int error; +#ifndef __SMP__ - error = verify_area(VERIFY_WRITE,fildes,8); - if (error) - return error; - error = do_pipe(fd); - if (error) - return error; - put_fs_long(fd[0],0+fildes); - put_fs_long(fd[1],1+fildes); - return 0; +static void hard_idle(void) +{ + while (!need_resched) { + if (hlt_works_ok && !hlt_counter) { +#ifdef CONFIG_APM + /* If the APM BIOS is not enabled, or there + is an error calling the idle routine, we + should hlt if possible. We need to check + need_resched again because an interrupt + may have occurred in apm_do_idle(). */ + start_bh_atomic(); + if (!apm_do_idle() && !need_resched) + __asm__("hlt"); + end_bh_atomic(); +#else + __asm__("hlt"); +#endif + } + if (need_resched) + break; + schedule(); + } +#ifdef CONFIG_APM + apm_do_busy(); +#endif } /* - * The idle loop on a i386.. + * The idle loop on a uniprocessor i386.. */ + asmlinkage int sys_idle(void) { - int i; - pmd_t * pmd; + unsigned long start_idle = 0; if (current->pid != 0) return -EPERM; - - /* Map out the low memory: it's no longer needed */ - pmd = pmd_offset(swapper_pg_dir, 0); - for (i = 0 ; i < 768 ; i++) - pmd_clear(pmd++); - /* endless idle loop with no priority at all */ current->counter = -100; - for (;;) { - if (hlt_works_ok && !hlt_counter && !need_resched) - __asm__("hlt"); + for (;;) + { + /* + * We are locked at this point. So we can safely call + * the APM bios knowing only one CPU at a time will do + * so. + */ + if (!start_idle) + start_idle = jiffies; + if (jiffies - start_idle > HARD_IDLE_TIMEOUT) + { + hard_idle(); + } + else + { + if (hlt_works_ok && !hlt_counter && !need_resched) + __asm__("hlt"); + } + if (need_resched) + start_idle = 0; schedule(); } } +#else + +/* + * In the SMP world we hlt outside of kernel syscall rather than within + * so as to get the right locking semantics. + */ + +asmlinkage int sys_idle(void) +{ + if(current->pid != 0) + return -EPERM; +#ifdef __SMP_PROF__ + smp_spins_sys_idle[smp_processor_id()]+= + smp_spins_syscall_cur[smp_processor_id()]; +#endif + current->counter= -100; + schedule(); + return 0; +} + +/* + * This is being executed in task 0 'user space'. + */ + +int cpu_idle(void *unused) +{ + while(1) + { + if(cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched) + __asm("hlt"); + if(0==(0x7fffffff & smp_process_available)) + continue; + while(0x80000000 & smp_process_available); + cli(); + while(set_bit(31,&smp_process_available)) + while(test_bit(31,&smp_process_available)) + { + /* + * Oops.. This is kind of important in some cases... + */ + if(clear_bit(smp_processor_id(), &smp_invalidate_needed)) + local_flush_tlb(); + } + if (0==(0x7fffffff & smp_process_available)){ + clear_bit(31,&smp_process_available); + sti(); + continue; + } + smp_process_available--; + clear_bit(31,&smp_process_available); + sti(); + idle(); + } +} + +#endif + /* * This routine reboots the machine by asking the keyboard * controller to pulse the reset-line low. We try that for a while, * and if it doesn't work, we do some other stupid things. */ static long no_idt[2] = {0, 0}; +static int reboot_mode = 0; + +void reboot_setup(char *str, int *ints) +{ + int mode = 0; + + /* "w" for "warm" reboot (no memory testing etc) */ + if (str[0] == 'w') + mode = 0x1234; + reboot_mode = mode; +} static inline void kb_wait(void) { @@ -101,15 +209,14 @@ void hard_reset_now(void) int i, j; sti(); -/* rebooting needs to touch the page at absolute addr 0 */ - pg0[0] = 7; - *((unsigned short *)0x472) = 0x1234; + *((unsigned short *)__va(0x472)) = reboot_mode; for (;;) { for (i=0; i<100; i++) { kb_wait(); for(j = 0; j < 100000 ; j++) /* nothing */; outb(0xfe,0x64); /* pulse reset low */ + udelay(100); } __asm__ __volatile__("\tlidt %0": "=m" (no_idt)); } @@ -118,24 +225,27 @@ void hard_reset_now(void) void show_regs(struct pt_regs * regs) { printk("\n"); - printk("EIP: %04x:%08lx",0xffff & regs->cs,regs->eip); - if (regs->cs & 3) - printk(" ESP: %04x:%08lx",0xffff & regs->ss,regs->esp); + printk("EIP: %04x:[<%08lx>]",0xffff & regs->xcs,regs->eip); + if (regs->xcs & 3) + printk(" ESP: %04x:%08lx",0xffff & regs->xss,regs->esp); printk(" EFLAGS: %08lx\n",regs->eflags); printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", regs->eax,regs->ebx,regs->ecx,regs->edx); printk("ESI: %08lx EDI: %08lx EBP: %08lx", regs->esi, regs->edi, regs->ebp); - printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n", - 0xffff & regs->ds,0xffff & regs->es, - 0xffff & regs->fs,0xffff & regs->gs); + printk(" DS: %04x ES: %04x\n", + 0xffff & regs->xds,0xffff & regs->xes); } /* * Free current thread data structures etc.. */ + void exit_thread(void) { + /* forget lazy i387 state */ + if (last_task_used_math == current) + last_task_used_math = NULL; /* forget local segments */ __asm__ __volatile__("mov %w0,%%fs ; mov %w0,%%gs ; lldt %w0" : /* no outputs */ @@ -166,6 +276,26 @@ void flush_thread(void) for (i=0 ; i<8 ; i++) current->debugreg[i] = 0; + + /* + * Forget coprocessor state.. + */ +#ifdef __SMP__ + if (current->flags & PF_USEDFPU) { + stts(); + } +#else + if (last_task_used_math == current) { + last_task_used_math = NULL; + stts(); + } +#endif + current->used_math = 0; + current->flags &= ~PF_USEDFPU; +} + +void release_thread(struct task_struct *dead_task) +{ } void copy_thread(int nr, unsigned long clone_flags, unsigned long esp, @@ -186,6 +316,7 @@ void copy_thread(int nr, unsigned long clone_flags, unsigned long esp, childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1; p->tss.esp = (unsigned long) childregs; p->tss.eip = (unsigned long) ret_from_sys_call; + p->tss.ebx = (unsigned long) p; *childregs = *regs; childregs->eax = 0; childregs->esp = esp; @@ -210,6 +341,31 @@ void copy_thread(int nr, unsigned long clone_flags, unsigned long esp, } /* + * fill in the fpu structure for a core dump.. + */ +int dump_fpu (struct pt_regs * regs, struct user_i387_struct* fpu) +{ + int fpvalid; + +/* Flag indicating the math stuff is valid. We don't support this for the + soft-float routines yet */ + if (hard_math) { + if ((fpvalid = current->used_math) != 0) { + if (last_task_used_math == current) + __asm__("clts ; fnsave %0": :"m" (*fpu)); + else + memcpy(fpu,¤t->tss.i387.hard,sizeof(*fpu)); + } + } else { + /* we should dump the emulator state here, but we need to + convert it into standard 387 format first.. */ + fpvalid = 0; + } + + return fpvalid; +} + +/* * fill in the user structure for a core dump.. */ void dump_thread(struct pt_regs * regs, struct user * dump) @@ -220,55 +376,52 @@ void dump_thread(struct pt_regs * regs, struct user * dump) dump->magic = CMAGIC; dump->start_code = 0; dump->start_stack = regs->esp & ~(PAGE_SIZE - 1); - dump->u_tsize = ((unsigned long) current->mm->end_code) >> 12; - dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> 12; + dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; + dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; dump->u_dsize -= dump->u_tsize; dump->u_ssize = 0; for (i = 0; i < 8; i++) dump->u_debugreg[i] = current->debugreg[i]; if (dump->start_stack < TASK_SIZE) - dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> 12; - - dump->regs = *regs; - -/* Flag indicating the math stuff is valid. We don't support this for the - soft-float routines yet */ - if (hard_math) { - if ((dump->u_fpvalid = current->used_math) != 0) { - if (last_task_used_math == current) - __asm__("clts ; fnsave %0": :"m" (dump->i387)); - else - memcpy(&dump->i387,¤t->tss.i387.hard,sizeof(dump->i387)); - } - } else { - /* we should dump the emulator state here, but we need to - convert it into standard 387 format first.. */ - dump->u_fpvalid = 0; - } + dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; + + dump->regs.ebx = regs->ebx; + dump->regs.ecx = regs->ecx; + dump->regs.edx = regs->edx; + dump->regs.esi = regs->esi; + dump->regs.edi = regs->edi; + dump->regs.ebp = regs->ebp; + dump->regs.eax = regs->eax; + dump->regs.ds = regs->xds; + dump->regs.es = regs->xes; + __asm__("mov %%fs,%0":"=r" (dump->regs.fs)); + __asm__("mov %%gs,%0":"=r" (dump->regs.gs)); + dump->regs.orig_eax = regs->orig_eax; + dump->regs.eip = regs->eip; + dump->regs.cs = regs->xcs; + dump->regs.eflags = regs->eflags; + dump->regs.esp = regs->esp; + dump->regs.ss = regs->xss; + + dump->u_fpvalid = dump_fpu (regs, &dump->i387); } asmlinkage int sys_fork(struct pt_regs regs) { - return do_fork(COPYVM | SIGCHLD, regs.esp, ®s); + return do_fork(SIGCHLD, regs.esp, ®s); } asmlinkage int sys_clone(struct pt_regs regs) { -#ifdef CLONE_ACTUALLY_WORKS_OK unsigned long clone_flags; unsigned long newsp; - newsp = regs.ebx; - clone_flags = regs.ecx; + clone_flags = regs.ebx; + newsp = regs.ecx; if (!newsp) newsp = regs.esp; - if (newsp == regs.esp) - clone_flags |= COPYVM; return do_fork(clone_flags, newsp, ®s); -#else - return -ENOSYS; -#endif } /* diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index f32035edc..5ea636092 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -11,7 +11,7 @@ #include <linux/user.h> #include <linux/debugreg.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -28,10 +28,9 @@ #define TRAP_FLAG 0x100 /* - * this is the number to subtract from the top of the stack. To find - * the local frame. + * Offset of eflags on child stack.. */ -#define MAGICNUMBER 68 +#define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs)) /* change a pid into a task struct. */ static inline struct task_struct * get_task(int pid) @@ -83,7 +82,8 @@ static inline int put_stack_long(struct task_struct *task, int offset, * and that it is in the task area before calling this: this routine does * no checking. */ -static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr) +static unsigned long get_long(struct task_struct * tsk, + struct vm_area_struct * vma, unsigned long addr) { pgd_t * pgdir; pmd_t * pgmiddle; @@ -91,9 +91,9 @@ static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr) unsigned long page; repeat: - pgdir = pgd_offset(vma->vm_task, addr); + pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - do_no_page(vma, addr, 0); + do_no_page(tsk, vma, addr, 0); goto repeat; } if (pgd_bad(*pgdir)) { @@ -103,7 +103,7 @@ repeat: } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - do_no_page(vma, addr, 0); + do_no_page(tsk, vma, addr, 0); goto repeat; } if (pmd_bad(*pgmiddle)) { @@ -113,12 +113,12 @@ repeat: } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - do_no_page(vma, addr, 0); + do_no_page(tsk, vma, addr, 0); goto repeat; } page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ - if (page >= high_memory) + if (MAP_NR(page) >= max_mapnr) return 0; page += addr & ~PAGE_MASK; return *(unsigned long *) page; @@ -133,7 +133,7 @@ repeat: * Now keeps R/W state of page so that a text page stays readonly * even if a debugger scribbles breakpoints into it. -M.U- */ -static void put_long(struct vm_area_struct * vma, unsigned long addr, +static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr, unsigned long data) { pgd_t *pgdir; @@ -142,9 +142,9 @@ static void put_long(struct vm_area_struct * vma, unsigned long addr, unsigned long page; repeat: - pgdir = pgd_offset(vma->vm_task, addr); + pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - do_no_page(vma, addr, 1); + do_no_page(tsk, vma, addr, 1); goto repeat; } if (pgd_bad(*pgdir)) { @@ -154,7 +154,7 @@ repeat: } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - do_no_page(vma, addr, 1); + do_no_page(tsk, vma, addr, 1); goto repeat; } if (pmd_bad(*pgmiddle)) { @@ -164,23 +164,21 @@ repeat: } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - do_no_page(vma, addr, 1); + do_no_page(tsk, vma, addr, 1); goto repeat; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - do_wp_page(vma, addr, 1); + do_wp_page(tsk, vma, addr, 1); goto repeat; } /* this is a hack for non-kernel-mapped video buffers and similar */ - if (page < high_memory) { - page += addr & ~PAGE_MASK; - *(unsigned long *) page = data; - } + if (MAP_NR(page) < max_mapnr) + *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ /* this should also re-instate whatever read-only mode there was before */ - *pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot)); - invalidate(); + set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); + flush_tlb(); } static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr) @@ -188,7 +186,7 @@ static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigne struct vm_area_struct * vma; addr &= PAGE_MASK; - vma = find_vma(tsk,addr); + vma = find_vma(tsk->mm,addr); if (!vma) return NULL; if (vma->vm_start <= addr) @@ -222,8 +220,8 @@ static int read_long(struct task_struct * tsk, unsigned long addr, if (!vma_high || vma_high->vm_start != vma->vm_end) return -EIO; } - low = get_long(vma, addr & ~(sizeof(long)-1)); - high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1)); + low = get_long(tsk, vma, addr & ~(sizeof(long)-1)); + high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1)); switch (addr & (sizeof(long)-1)) { case 1: low >>= 8; @@ -240,7 +238,7 @@ static int read_long(struct task_struct * tsk, unsigned long addr, } *result = low; } else - *result = get_long(vma, addr); + *result = get_long(tsk, vma, addr); return 0; } @@ -264,8 +262,8 @@ static int write_long(struct task_struct * tsk, unsigned long addr, if (!vma_high || vma_high->vm_start != vma->vm_end) return -EIO; } - low = get_long(vma, addr & ~(sizeof(long)-1)); - high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1)); + low = get_long(tsk, vma, addr & ~(sizeof(long)-1)); + high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1)); switch (addr & (sizeof(long)-1)) { case 0: /* shouldn't happen, but safety first */ low = data; @@ -289,13 +287,78 @@ static int write_long(struct task_struct * tsk, unsigned long addr, high |= data >> 8; break; } - put_long(vma, addr & ~(sizeof(long)-1),low); - put_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high); + put_long(tsk, vma, addr & ~(sizeof(long)-1),low); + put_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high); } else - put_long(vma, addr, data); + put_long(tsk, vma, addr, data); + return 0; +} + +static int putreg(struct task_struct *child, + unsigned long regno, unsigned long value) +{ + switch (regno >> 2) { + case ORIG_EAX: + return -EIO; + case FS: + if (value && (value & 3) != 3) + return -EIO; + child->tss.fs = value; + return 0; + case GS: + if (value && (value & 3) != 3) + return -EIO; + child->tss.gs = value; + return 0; + case DS: + case ES: + if (value && (value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case SS: + case CS: + if ((value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case EFL: + value &= FLAG_MASK; + value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK; + } + if (regno > GS*4) + regno -= 2*4; + put_stack_long(child, regno - sizeof(struct pt_regs), value); return 0; } +static unsigned long getreg(struct task_struct *child, + unsigned long regno) +{ + unsigned long retval = ~0UL; + + switch (regno >> 2) { + case FS: + retval = child->tss.fs; + break; + case GS: + retval = child->tss.gs; + break; + case DS: + case ES: + case SS: + case CS: + retval = 0xffff; + /* fall through */ + default: + if (regno > GS*4) + regno -= 2*4; + regno = regno - sizeof(struct pt_regs); + retval &= get_stack_long(child, regno); + } + return retval; +} + asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; @@ -321,8 +384,10 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) return -EPERM; if ((!child->dumpable || (current->uid != child->euid) || + (current->uid != child->suid) || (current->uid != child->uid) || (current->gid != child->egid) || + (current->gid != child->sgid) || (current->gid != child->gid)) && !suser()) return -EPERM; /* the same process cannot be attached many times */ @@ -358,7 +423,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) return res; res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); if (!res) - put_fs_long(tmp,(unsigned long *) data); + put_user(tmp,(unsigned long *) data); return res; } @@ -375,22 +440,15 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (res) return res; tmp = 0; /* Default return condition */ - if(addr < 17*sizeof(long)) { - addr = addr >> 2; /* temporary hack. */ - - tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER); - if (addr == DS || addr == ES || - addr == FS || addr == GS || - addr == CS || addr == SS) - tmp &= 0xffff; - }; + if(addr < 17*sizeof(long)) + tmp = getreg(child, addr); if(addr >= (long) &dummy->u_debugreg[0] && addr <= (long) &dummy->u_debugreg[7]){ addr -= (long) &dummy->u_debugreg[0]; addr = addr >> 2; tmp = child->debugreg[addr]; }; - put_fs_long(tmp,(unsigned long *) data); + put_user(tmp,(unsigned long *) data); return 0; } @@ -404,35 +462,14 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) addr > sizeof(struct user) - 3) return -EIO; - addr = addr >> 2; /* temporary hack. */ - - if (addr == ORIG_EAX) - return -EIO; - if (addr == DS || addr == ES || - addr == FS || addr == GS || - addr == CS || addr == SS) { - data &= 0xffff; - if (data && (data & 3) != 3) - return -EIO; - } - if (addr == EFL) { /* flags. */ - data &= FLAG_MASK; - data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER) & ~FLAG_MASK; - } - /* Do not allow the user to set the debug register for kernel - address space */ - if(addr < 17){ - if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data)) - return -EIO; - return 0; - }; + if (addr < 17*sizeof(long)) + return putreg(child, addr, data); /* We need to be very careful here. We implicitly want to modify a portion of the task_struct, and we have to be selective about what portions we allow someone to modify. */ - addr = addr << 2; /* Convert back again */ if(addr >= (long) &dummy->u_debugreg[0] && addr <= (long) &dummy->u_debugreg[7]){ @@ -466,10 +503,10 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) else child->flags &= ~PF_TRACESYS; child->exit_code = data; - child->state = TASK_RUNNING; + wake_up_process(child); /* make sure the single step bit is not set. */ - tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG; - put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); + tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; + put_stack_long(child, EFL_OFFSET,tmp); return 0; } @@ -481,11 +518,13 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_KILL: { long tmp; - child->state = TASK_RUNNING; + if (child->state == TASK_ZOMBIE) /* already dead */ + return 0; + wake_up_process(child); child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ - tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG; - put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); + tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; + put_stack_long(child, EFL_OFFSET, tmp); return 0; } @@ -495,9 +534,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if ((unsigned long) data > NSIG) return -EIO; child->flags &= ~PF_TRACESYS; - tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG; - put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); - child->state = TASK_RUNNING; + tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG; + put_stack_long(child, EFL_OFFSET, tmp); + wake_up_process(child); child->exit_code = data; /* give it a chance to run. */ return 0; @@ -509,14 +548,14 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if ((unsigned long) data > NSIG) return -EIO; child->flags &= ~(PF_PTRACED|PF_TRACESYS); - child->state = TASK_RUNNING; + wake_up_process(child); child->exit_code = data; REMOVE_LINKS(child); child->p_pptr = child->p_opptr; SET_LINKS(child); /* make sure the single step bit is not set. */ - tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG; - put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); + tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; + put_stack_long(child, EFL_OFFSET, tmp); return 0; } diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 5cc4c5d7d..3dae50628 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -5,7 +5,7 @@ */ /* - * This file handles the architecture-dependent parts of process handling.. + * This file handles the architecture-dependent parts of initialization */ #include <linux/errno.h> @@ -16,30 +16,37 @@ #include <linux/unistd.h> #include <linux/ptrace.h> #include <linux/malloc.h> -#include <linux/ldt.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> #include <linux/ioport.h> #include <linux/delay.h> - -#include <asm/segment.h> +#include <linux/config.h> +#ifdef CONFIG_APM +#include <linux/apm_bios.h> +#endif +#ifdef CONFIG_BLK_DEV_RAM +#include <linux/blk.h> +#endif +#include <asm/uaccess.h> #include <asm/system.h> +#include <asm/smp.h> /* * Tell us the machine setup.. */ -char hard_math = 0; /* set by boot/head.S */ -char x86 = 0; /* set by boot/head.S to 3 or 4 */ -char x86_model = 0; /* set by boot/head.S */ -char x86_mask = 0; /* set by boot/head.S */ -int x86_capability = 0; /* set by boot/head.S */ +char hard_math = 0; /* set by kernel/head.S */ +char x86 = 0; /* set by kernel/head.S to 3..6 */ +char x86_model = 0; /* set by kernel/head.S */ +char x86_mask = 0; /* set by kernel/head.S */ +int x86_capability = 0; /* set by kernel/head.S */ int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */ +int have_cpuid = 0; /* set if CPUID instruction works */ -char x86_vendor_id[13] = "Unknown"; +char x86_vendor_id[13] = "unknown"; char ignore_irq13 = 0; /* set if exception 16 works */ -char wp_works_ok = 0; /* set if paging hardware honours WP */ +char wp_works_ok = -1; /* set if paging hardware honours WP */ char hlt_works_ok = 1; /* set if the "hlt" instruction works */ /* @@ -52,11 +59,20 @@ int EISA_bus = 0; */ struct drive_info_struct { char dummy[32]; } drive_info; struct screen_info screen_info; +#ifdef CONFIG_APM +struct apm_bios_info apm_bios_info; +#endif unsigned char aux_device_present; -extern int ramdisk_size; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + extern int root_mountflags; -extern int etext, edata, end; +extern int _etext, _edata, _end; extern char empty_zero_page[PAGE_SIZE]; @@ -65,16 +81,28 @@ extern char empty_zero_page[PAGE_SIZE]; */ #define PARAM empty_zero_page #define EXT_MEM_K (*(unsigned short *) (PARAM+2)) +#ifdef CONFIG_APM +#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64)) +#endif #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) #define SCREEN_INFO (*(struct screen_info *) (PARAM+0)) #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) -#define RAMDISK_SIZE (*(unsigned short *) (PARAM+0x1F8)) +#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) #define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) #define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) +#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210)) +#define KERNEL_START (*(unsigned long *) (PARAM+0x214)) +#define INITRD_START (*(unsigned long *) (PARAM+0x218)) +#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) #define COMMAND_LINE ((char *) (PARAM+2048)) #define COMMAND_LINE_SIZE 256 +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 + static char command_line[COMMAND_LINE_SIZE] = { 0, }; + char saved_command_line[COMMAND_LINE_SIZE]; void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p) @@ -82,35 +110,64 @@ void setup_arch(char **cmdline_p, unsigned long memory_start, memory_end; char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; + static unsigned char smptrap=0; + + if(smptrap==1) + { + return; + } + smptrap=1; - ROOT_DEV = ORIG_ROOT_DEV; + ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; +#ifdef CONFIG_APM + apm_bios_info = APM_BIOS_INFO; +#endif aux_device_present = AUX_DEVICE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); memory_end &= PAGE_MASK; - ramdisk_size = RAMDISK_SIZE; +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif #ifdef CONFIG_MAX_16M if (memory_end > 16*1024*1024) memory_end = 16*1024*1024; #endif - if (MOUNT_ROOT_RDONLY) - root_mountflags |= MS_RDONLY; - memory_start = (unsigned long) &end; - init_task.mm->start_code = TASK_SIZE; - init_task.mm->end_code = TASK_SIZE + (unsigned long) &etext; - init_task.mm->end_data = TASK_SIZE + (unsigned long) &edata; - init_task.mm->brk = TASK_SIZE + (unsigned long) &end; + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + memory_start = (unsigned long) &_end; + init_task.mm->start_code = PAGE_OFFSET; + init_task.mm->end_code = (unsigned long) &_etext; + init_task.mm->end_data = (unsigned long) &_edata; + init_task.mm->brk = (unsigned long) &_end; + + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; for (;;) { - if (c == ' ' && *(unsigned long *)from == *(unsigned long *)"mem=") { - memory_end = simple_strtoul(from+4, &from, 0); - if ( *from == 'K' || *from == 'k' ) { - memory_end = memory_end << 10; - from++; - } else if ( *from == 'M' || *from == 'm' ) { - memory_end = memory_end << 20; - from++; + /* + * "mem=nopentium" disables the 4MB page tables. + * "mem=XXX[kKmM]" overrides the BIOS-reported + * memory size + */ + if (c == ' ' && *(const unsigned long *)from == *(const unsigned long *)"mem=") { + if (to != command_line) to--; + if (!memcmp(from+4, "nopentium", 9)) { + from += 9+4; + x86_capability &= ~8; + } else { + memory_end = simple_strtoul(from+4, &from, 0); + if ( *from == 'K' || *from == 'k' ) { + memory_end = memory_end << 10; + from++; + } else if ( *from == 'M' || *from == 'm' ) { + memory_end = memory_end << 20; + from++; + } } } c = *(from++); @@ -122,60 +179,159 @@ void setup_arch(char **cmdline_p, } *to = '\0'; *cmdline_p = command_line; + memory_end += PAGE_OFFSET; *memory_start_p = memory_start; *memory_end_p = memory_end; + +#ifdef CONFIG_BLK_DEV_INITRD + if (LOADER_TYPE) { + initrd_start = INITRD_START ? INITRD_START + PAGE_OFFSET : 0; + initrd_end = initrd_start+INITRD_SIZE; + if (initrd_end > memory_end) { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end,memory_end); + initrd_start = 0; + } + } +#endif + /* request io space for devices used on all i[345]86 PC'S */ request_region(0x00,0x20,"dma1"); request_region(0x40,0x20,"timer"); - request_region(0x70,0x10,"rtc"); request_region(0x80,0x20,"dma page reg"); request_region(0xc0,0x20,"dma2"); - request_region(0xf0,0x2,"npu"); - request_region(0xf8,0x8,"npu"); + request_region(0xf0,0x10,"npu"); +} + +static const char * i486model(unsigned int nr) +{ + static const char *model[] = { + "0","DX","SX","DX/2","4","SX/2","6","DX/2-WB","DX/4","DX/4-WB", + "10","11","12","13","Am5x86-WT","Am5x86-WB" + }; + if (nr < sizeof(model)/sizeof(char *)) + return model[nr]; + return NULL; +} + +static const char * i586model(unsigned int nr) +{ + static const char *model[] = { + "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83" + }; + if (nr < sizeof(model)/sizeof(char *)) + return model[nr]; + return NULL; +} + +static const char * i686model(unsigned int nr) +{ + static const char *model[] = { + "PPro A-step", "Pentium Pro" + }; + if (nr < sizeof(model)/sizeof(char *)) + return model[nr]; + return NULL; +} + +static const char * getmodel(int x86, int model) +{ + const char *p = NULL; + static char nbuf[12]; + switch (x86) { + case 4: + p = i486model(model); + break; + case 5: + p = i586model(model); + break; + case 6: + p = i686model(model); + break; + } + if (p) + return p; + + sprintf(nbuf, "%d", model); + return nbuf; } int get_cpuinfo(char * buffer) { - char *model[2][9]={{"DX","SX","DX/2","4","SX/2","6", - "DX/2-WB","DX/4"}, - {"Pentium 60/66","Pentium 90/100","3", - "4","5","6","7","8"}}; - char mask[2]; - mask[0] = x86_mask+'@'; - mask[1] = '\0'; - return sprintf(buffer,"cpu\t\t: %c86\n" - "model\t\t: %s\n" - "mask\t\t: %s\n" - "vid\t\t: %s\n" - "fdiv_bug\t: %s\n" - "math\t\t: %s\n" - "hlt\t\t: %s\n" - "wp\t\t: %s\n" - "Integrated NPU\t: %s\n" - "Enhanced VM86\t: %s\n" - "IO Breakpoints\t: %s\n" - "4MB Pages\t: %s\n" - "TS Counters\t: %s\n" - "Pentium MSR\t: %s\n" - "Mach. Ch. Exep.\t: %s\n" - "CMPXCHGB8B\t: %s\n" - "BogoMips\t: %lu.%02lu\n", - x86+'0', - x86_model ? model[x86-4][x86_model-1] : "Unknown", - x86_mask ? mask : "Unknown", - x86_vendor_id, - fdiv_bug ? "yes" : "no", - hard_math ? "yes" : "no", - hlt_works_ok ? "yes" : "no", - wp_works_ok ? "yes" : "no", - x86_capability & 1 ? "yes" : "no", - x86_capability & 2 ? "yes" : "no", - x86_capability & 4 ? "yes" : "no", - x86_capability & 8 ? "yes" : "no", - x86_capability & 16 ? "yes" : "no", - x86_capability & 32 ? "yes" : "no", - x86_capability & 128 ? "yes" : "no", - x86_capability & 256 ? "yes" : "no", - loops_per_sec/500000, (loops_per_sec/5000) % 100 - ); + int i, len = 0; + static const char *x86_cap_flags[] = { + "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", + "cx8", "apic", "10", "11", "mtrr", "pge", "mca", "cmov", + "16", "17", "18", "19", "20", "21", "22", "mmx", + "24", "25", "26", "27", "28", "29", "30", "31" + }; + +#ifdef __SMP__ + int n; + +#define CD(X) (cpu_data[n].X) +/* SMP has the wrong name for loops_per_sec */ +#define loops_per_sec udelay_val +#define CPUN n + + for ( n = 0 ; n < 32 ; n++ ) { + if ( cpu_present_map & (1<<n) ) { + if (len) buffer[len++] = '\n'; + +#else +#define CD(X) (X) +#define CPUN 0 +#endif + + len += sprintf(buffer+len,"processor\t: %d\n" + "cpu\t\t: %c86\n" + "model\t\t: %s\n" + "vendor_id\t: %s\n", + CPUN, + CD(x86)+'0', + CD(have_cpuid) ? + getmodel(CD(x86), CD(x86_model)) : + "unknown", + CD(x86_vendor_id)); + + if (CD(x86_mask)) + len += sprintf(buffer+len, + "stepping\t: %d\n", + CD(x86_mask)); + else + len += sprintf(buffer+len, + "stepping\t: unknown\n"); + + len += sprintf(buffer+len, + "fdiv_bug\t: %s\n" + "hlt_bug\t\t: %s\n" + "fpu\t\t: %s\n" + "fpu_exception\t: %s\n" + "cpuid\t\t: %s\n" + "wp\t\t: %s\n" + "flags\t\t:", + CD(fdiv_bug) ? "yes" : "no", + CD(hlt_works_ok) ? "no" : "yes", + CD(hard_math) ? "yes" : "no", + (CD(hard_math) && ignore_irq13) + ? "yes" : "no", + CD(have_cpuid) ? "yes" : "no", + CD(wp_works_ok) ? "yes" : "no"); + + for ( i = 0 ; i < 32 ; i++ ) { + if ( CD(x86_capability) & (1 << i) ) { + len += sprintf(buffer+len, " %s", + x86_cap_flags[i]); + } + } + len += sprintf(buffer+len, + "\nbogomips\t: %lu.%02lu\n", + CD(loops_per_sec+2500)/500000, + (CD(loops_per_sec+2500)/5000) % 100); +#ifdef __SMP__ + } + } +#endif + return len; } diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c index 3db1a6985..c53a40e16 100644 --- a/arch/i386/kernel/signal.c +++ b/arch/i386/kernel/signal.c @@ -4,6 +4,8 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#include <linux/config.h> + #include <linux/sched.h> #include <linux/mm.h> #include <linux/kernel.h> @@ -13,13 +15,14 @@ #include <linux/ptrace.h> #include <linux/unistd.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #define _S(nr) (1<<((nr)-1)) #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); +asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs); /* * atomically swap in the new signal mask, and wait for a signal. @@ -40,28 +43,67 @@ asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long } } +static inline void restore_i387_hard(struct _fpstate *buf) +{ +#ifdef __SMP__ + if (current->flags & PF_USEDFPU) { + stts(); + } +#else + if (current == last_task_used_math) { + last_task_used_math = NULL; + stts(); + } +#endif + current->used_math = 1; + current->flags &= ~PF_USEDFPU; + copy_from_user(¤t->tss.i387.hard, buf, sizeof(*buf)); +} + +static void restore_i387(struct _fpstate *buf) +{ +#ifndef CONFIG_MATH_EMULATION + restore_i387_hard(buf); +#else + if (hard_math) { + restore_i387_hard(buf); + return; + } + restore_i387_soft(buf); +#endif +} + + /* * This sets regs->esp even though we don't actually use sigstacks yet.. */ asmlinkage int sys_sigreturn(unsigned long __unused) { -#define COPY(x) regs->x = context.x -#define COPY_SEG(x) \ -if ((context.x & 0xfffc) && (context.x & 3) != 3) goto badframe; COPY(x); -#define COPY_SEG_STRICT(x) \ -if (!(context.x & 0xfffc) || (context.x & 3) != 3) goto badframe; COPY(x); - struct sigcontext_struct context; +#define COPY(x) regs->x = context->x +#define COPY_SEG(seg) \ +{ unsigned int tmp = context->seg; \ +if ((tmp & 0xfffc) && (tmp & 3) != 3) goto badframe; \ +regs->x##seg = tmp; } +#define COPY_SEG_STRICT(seg) \ +{ unsigned int tmp = context->seg; \ +if ((tmp & 0xfffc) && (tmp & 3) != 3) goto badframe; \ +regs->x##seg = tmp; } +#define GET_SEG(seg) \ +{ unsigned int tmp = context->seg; \ +if ((tmp & 0xfffc) && (tmp & 3) != 3) goto badframe; \ +__asm__("mov %w0,%%" #seg: :"r" (tmp)); } + struct sigcontext * context; struct pt_regs * regs; regs = (struct pt_regs *) &__unused; - if (verify_area(VERIFY_READ, (void *) regs->esp, sizeof(context))) + context = (struct sigcontext *) regs->esp; + if (verify_area(VERIFY_READ, context, sizeof(*context))) goto badframe; - memcpy_fromfs(&context,(void *) regs->esp, sizeof(context)); - current->blocked = context.oldmask & _BLOCKABLE; + current->blocked = context->oldmask & _BLOCKABLE; COPY_SEG(ds); COPY_SEG(es); - COPY_SEG(fs); - COPY_SEG(gs); + GET_SEG(fs); + GET_SEG(gs); COPY_SEG_STRICT(ss); COPY_SEG_STRICT(cs); COPY(eip); @@ -70,66 +112,164 @@ if (!(context.x & 0xfffc) || (context.x & 3) != 3) goto badframe; COPY(x); COPY(esp); COPY(ebp); COPY(edi); COPY(esi); regs->eflags &= ~0x40DD5; - regs->eflags |= context.eflags & 0x40DD5; + regs->eflags |= context->eflags & 0x40DD5; regs->orig_eax = -1; /* disable syscall checks */ - return context.eax; + if (context->fpstate) { + struct _fpstate * buf = context->fpstate; + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + restore_i387(buf); + } + return context->eax; badframe: do_exit(SIGSEGV); } +static inline struct _fpstate * save_i387_hard(struct _fpstate * buf) +{ +#ifdef __SMP__ + if (current->flags & PF_USEDFPU) { + __asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard)); + stts(); + current->flags &= ~PF_USEDFPU; + } +#else + if (current == last_task_used_math) { + __asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard)); + last_task_used_math = NULL; + __asm__ __volatile__("fwait"); /* not needed on 486+ */ + stts(); + } +#endif + current->tss.i387.hard.status = current->tss.i387.hard.swd; + copy_to_user(buf, ¤t->tss.i387.hard, sizeof(*buf)); + current->used_math = 0; + return buf; +} + +static struct _fpstate * save_i387(struct _fpstate * buf) +{ + if (!current->used_math) + return NULL; + +#ifndef CONFIG_MATH_EMULATION + return save_i387_hard(buf); +#else + if (hard_math) + return save_i387_hard(buf); + return save_i387_soft(buf); +#endif +} + /* * Set up a signal frame... Make the stack look the way iBCS2 expects * it to look. */ -void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip, - struct pt_regs * regs, int signr, unsigned long oldmask) +static void setup_frame(struct sigaction * sa, + struct pt_regs * regs, int signr, + unsigned long oldmask) { unsigned long * frame; -#define __CODE ((unsigned long)(frame+24)) -#define CODE(x) ((unsigned long *) ((x)+__CODE)) - frame = *fp; - if (regs->ss != USER_DS) + frame = (unsigned long *) regs->esp; + if ((regs->xss & 0xffff) != USER_DS && sa->sa_restorer) frame = (unsigned long *) sa->sa_restorer; - frame -= 32; - if (verify_area(VERIFY_WRITE,frame,32*4)) + frame -= 64; + if (verify_area(VERIFY_WRITE,frame,64*4)) do_exit(SIGSEGV); + /* set up the "normal" stack seen by the signal handler (iBCS2) */ - put_fs_long(__CODE,frame); +#define __CODE ((unsigned long)(frame+24)) +#define CODE(x) ((unsigned long *) ((x)+__CODE)) + if (put_user(__CODE,frame)) + do_exit(SIGSEGV); if (current->exec_domain && current->exec_domain->signal_invmap) - put_fs_long(current->exec_domain->signal_invmap[signr], frame+1); + put_user(current->exec_domain->signal_invmap[signr], frame+1); else - put_fs_long(signr, frame+1); - put_fs_long(regs->gs, frame+2); - put_fs_long(regs->fs, frame+3); - put_fs_long(regs->es, frame+4); - put_fs_long(regs->ds, frame+5); - put_fs_long(regs->edi, frame+6); - put_fs_long(regs->esi, frame+7); - put_fs_long(regs->ebp, frame+8); - put_fs_long((long)*fp, frame+9); - put_fs_long(regs->ebx, frame+10); - put_fs_long(regs->edx, frame+11); - put_fs_long(regs->ecx, frame+12); - put_fs_long(regs->eax, frame+13); - put_fs_long(current->tss.trap_no, frame+14); - put_fs_long(current->tss.error_code, frame+15); - put_fs_long(eip, frame+16); - put_fs_long(regs->cs, frame+17); - put_fs_long(regs->eflags, frame+18); - put_fs_long(regs->esp, frame+19); - put_fs_long(regs->ss, frame+20); - put_fs_long(0,frame+21); /* 387 state pointer - not implemented*/ + put_user(signr, frame+1); + { + unsigned int tmp = 0; +#define PUT_SEG(seg, mem) \ +__asm__("mov %%" #seg",%w0":"=r" (tmp):"0" (tmp)); put_user(tmp,mem); + PUT_SEG(gs, frame+2); + PUT_SEG(fs, frame+3); + } + put_user(regs->xes, frame+4); + put_user(regs->xds, frame+5); + put_user(regs->edi, frame+6); + put_user(regs->esi, frame+7); + put_user(regs->ebp, frame+8); + put_user(regs->esp, frame+9); + put_user(regs->ebx, frame+10); + put_user(regs->edx, frame+11); + put_user(regs->ecx, frame+12); + put_user(regs->eax, frame+13); + put_user(current->tss.trap_no, frame+14); + put_user(current->tss.error_code, frame+15); + put_user(regs->eip, frame+16); + put_user(regs->xcs, frame+17); + put_user(regs->eflags, frame+18); + put_user(regs->esp, frame+19); + put_user(regs->xss, frame+20); + put_user((unsigned long) save_i387((struct _fpstate *)(frame+32)),frame+21); /* non-iBCS2 extensions.. */ - put_fs_long(oldmask, frame+22); - put_fs_long(current->tss.cr2, frame+23); + put_user(oldmask, frame+22); + put_user(current->tss.cr2, frame+23); /* set up the return code... */ - put_fs_long(0x0000b858, CODE(0)); /* popl %eax ; movl $,%eax */ - put_fs_long(0x80cd0000, CODE(4)); /* int $0x80 */ - put_fs_long(__NR_sigreturn, CODE(2)); - *fp = frame; + put_user(0x0000b858, CODE(0)); /* popl %eax ; movl $,%eax */ + put_user(0x80cd0000, CODE(4)); /* int $0x80 */ + put_user(__NR_sigreturn, CODE(2)); #undef __CODE #undef CODE + + /* Set up registers for signal handler */ + regs->esp = (unsigned long) frame; + regs->eip = (unsigned long) sa->sa_handler; + { + unsigned long seg = USER_DS; + __asm__("mov %w0,%%fs ; mov %w0,%%gs":"=r" (seg) :"0" (seg)); + set_fs(seg); + regs->xds = seg; + regs->xes = seg; + regs->xss = seg; + regs->xcs = USER_CS; + } + regs->eflags &= ~TF_MASK; +} + +/* + * OK, we're invoking a handler + */ +static void handle_signal(unsigned long signr, struct sigaction *sa, + unsigned long oldmask, struct pt_regs * regs) +{ + /* are we from a system call? */ + if (regs->orig_eax >= 0) { + /* If so, check system call restarting.. */ + switch (regs->eax) { + case -ERESTARTNOHAND: + regs->eax = -EINTR; + break; + + case -ERESTARTSYS: + if (!(sa->sa_flags & SA_RESTART)) { + regs->eax = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->eax = regs->orig_eax; + regs->eip -= 2; + } + } + + /* set up the stack frame */ + setup_frame(sa, regs, signr, oldmask); + + if (sa->sa_flags & SA_ONESHOT) + sa->sa_handler = NULL; + if (!(sa->sa_flags & SA_NOMASK)) + current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; } /* @@ -144,18 +284,21 @@ void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip, asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) { unsigned long mask = ~current->blocked; - unsigned long handler_signal = 0; - unsigned long *frame = NULL; - unsigned long eip = 0; unsigned long signr; struct sigaction * sa; while ((signr = current->signal & mask)) { + /* + * This stops gcc flipping out. Otherwise the assembler + * including volatiles for the inline function to get + * current combined with this gets it confused. + */ + struct task_struct *t=current; __asm__("bsf %3,%1\n\t" "btrl %1,%0" - :"=m" (current->signal),"=r" (signr) - :"0" (current->signal), "1" (signr)); - sa = current->sigaction + signr; + :"=m" (t->signal),"=r" (signr) + :"0" (t->signal), "1" (signr)); + sa = current->sig->action + signr; signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; @@ -171,7 +314,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) current->signal |= _S(signr); continue; } - sa = current->sigaction + signr - 1; + sa = current->sig->action + signr - 1; } if (sa->sa_handler == SIG_IGN) { if (signr != SIGCHLD) @@ -188,12 +331,15 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) case SIGCONT: case SIGCHLD: case SIGWINCH: continue; - case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + case SIGSTOP: if (current->flags & PF_PTRACED) continue; current->state = TASK_STOPPED; current->exit_code = signr; - if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) notify_parent(current); schedule(); @@ -208,53 +354,23 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) /* fall through */ default: current->signal |= _S(signr & 0x7f); + current->flags |= PF_SIGNALED; do_exit(signr); } } - /* - * OK, we're invoking a handler - */ - if (regs->orig_eax >= 0) { - if (regs->eax == -ERESTARTNOHAND || - (regs->eax == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART))) - regs->eax = -EINTR; - } - handler_signal |= 1 << (signr-1); - mask &= ~sa->sa_mask; + handle_signal(signr, sa, oldmask, regs); + return 1; } - if (regs->orig_eax >= 0 && - (regs->eax == -ERESTARTNOHAND || - regs->eax == -ERESTARTSYS || - regs->eax == -ERESTARTNOINTR)) { - regs->eax = regs->orig_eax; - regs->eip -= 2; - } - if (!handler_signal) /* no handler will be called - return 0 */ - return 0; - eip = regs->eip; - frame = (unsigned long *) regs->esp; - signr = 1; - sa = current->sigaction; - for (mask = 1 ; mask ; sa++,signr++,mask += mask) { - if (mask > handler_signal) - break; - if (!(mask & handler_signal)) - continue; - setup_frame(sa,&frame,eip,regs,signr,oldmask); - eip = (unsigned long) sa->sa_handler; - if (sa->sa_flags & SA_ONESHOT) - sa->sa_handler = NULL; -/* force a supervisor-mode page-in of the signal handler to reduce races */ - __asm__("testb $0,%%fs:%0": :"m" (*(char *) eip)); - regs->cs = USER_CS; regs->ss = USER_DS; - regs->ds = USER_DS; regs->es = USER_DS; - regs->gs = USER_DS; regs->fs = USER_DS; - current->blocked |= sa->sa_mask; - oldmask |= sa->sa_mask; + + /* Did we come from a system call? */ + if (regs->orig_eax >= 0) { + /* Restart the system call - no handlers present */ + if (regs->eax == -ERESTARTNOHAND || + regs->eax == -ERESTARTSYS || + regs->eax == -ERESTARTNOINTR) { + regs->eax = regs->orig_eax; + regs->eip -= 2; + } } - regs->esp = (unsigned long) frame; - regs->eip = eip; /* "return" to the first handler */ - regs->eflags &= ~TF_MASK; - current->tss.trap_no = current->tss.error_code = 0; - return 1; + return 0; } diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c new file mode 100644 index 000000000..05c80b711 --- /dev/null +++ b/arch/i386/kernel/smp.c @@ -0,0 +1,1204 @@ +/* + * Intel MP v1.1/v1.4 specification support routines for multi-pentium + * hosts. + * + * (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net> + * Supported by Caldera http://www.caldera.com. + * Much of the core SMP work is based on previous work by Thomas Radke, to + * whom a great many thanks are extended. + * + * Thanks to Intel for making available several different Pentium and + * Pentium Pro MP machines. + * + * This code is released under the GNU public license version 2 or + * later. + * + * Fixes + * Felix Koop : NR_CPUS used properly + * Jose Renau : Handle single CPU case. + * Alan Cox : By repeated request 8) - Total BogoMIP report. + * Greg Wright : Fix for kernel stacks panic. + * Erich Boleyn : MP v1.4 and additional changes. + */ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/kernel_stat.h> +#include <linux/delay.h> +#include <linux/mc146818rtc.h> +#include <asm/i82489.h> +#include <linux/smp.h> +#include <asm/pgtable.h> +#include <asm/bitops.h> +#include <asm/pgtable.h> +#include <asm/smp.h> + +/* + * Why isn't this somewhere standard ?? + */ + +extern __inline int max(int a,int b) +{ + if(a>b) + return a; + return b; +} + + +int smp_found_config=0; /* Have we found an SMP box */ + +unsigned long cpu_present_map = 0; /* Bitmask of existing CPU's */ +int smp_num_cpus = 1; /* Total count of live CPU's */ +int smp_threads_ready=0; /* Set when the idlers are all forked */ +volatile int cpu_number_map[NR_CPUS]; /* which CPU maps to which logical number */ +volatile int cpu_logical_map[NR_CPUS]; /* which logical number maps to which CPU */ +volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; /* We always use 0 the rest is ready for parallel delivery */ +volatile unsigned long smp_invalidate_needed; /* Used for the invalidate map that's also checked in the spinlock */ +struct cpuinfo_x86 cpu_data[NR_CPUS]; /* Per cpu bogomips and other parameters */ +static unsigned int num_processors = 1; /* Internal processor count */ +static unsigned long io_apic_addr = 0xFEC00000; /* Address of the I/O apic (not yet used) */ +unsigned char boot_cpu_id = 0; /* Processor that is doing the boot up */ +static unsigned char *kstack_base,*kstack_end; /* Kernel stack list pointers */ +static int smp_activated = 0; /* Tripped once we need to start cross invalidating */ +int apic_version[NR_CPUS]; /* APIC version number */ +static volatile int smp_commenced=0; /* Tripped when we start scheduling */ +unsigned long apic_addr=0xFEE00000; /* Address of APIC (defaults to 0xFEE00000) */ +unsigned long nlong = 0; /* dummy used for apic_reg address + 0x20 */ +unsigned char *apic_reg=((unsigned char *)(&nlong))-0x20;/* Later set to the ioremap() of the APIC */ +unsigned long apic_retval; /* Just debugging the assembler.. */ +unsigned char *kernel_stacks[NR_CPUS]; /* Kernel stack pointers for CPU's (debugging) */ + +static volatile unsigned char smp_cpu_in_msg[NR_CPUS]; /* True if this processor is sending an IPI */ +static volatile unsigned long smp_msg_data; /* IPI data pointer */ +static volatile int smp_src_cpu; /* IPI sender processor */ +static volatile int smp_msg_id; /* Message being sent */ + +volatile unsigned long kernel_flag=0; /* Kernel spinlock */ +volatile unsigned char active_kernel_processor = NO_PROC_ID; /* Processor holding kernel spinlock */ +volatile unsigned long kernel_counter=0; /* Number of times the processor holds the lock */ +volatile unsigned long syscall_count=0; /* Number of times the processor holds the syscall lock */ + +volatile unsigned long ipi_count; /* Number of IPI's delivered */ +#ifdef __SMP_PROF__ +volatile unsigned long smp_spins[NR_CPUS]={0}; /* Count interrupt spins */ +volatile unsigned long smp_spins_syscall[NR_CPUS]={0}; /* Count syscall spins */ +volatile unsigned long smp_spins_syscall_cur[NR_CPUS]={0};/* Count spins for the actual syscall */ +volatile unsigned long smp_spins_sys_idle[NR_CPUS]={0}; /* Count spins for sys_idle */ +volatile unsigned long smp_idle_count[1+NR_CPUS]={0,}; /* Count idle ticks */ +#endif +#if defined (__SMP_PROF__) +volatile unsigned long smp_idle_map=0; /* Map for idle processors */ +#endif + +volatile unsigned long smp_proc_in_lock[NR_CPUS] = {0,};/* for computing process time */ +volatile unsigned long smp_process_available=0; + +/*#define SMP_DEBUG*/ + +#ifdef SMP_DEBUG +#define SMP_PRINTK(x) printk x +#else +#define SMP_PRINTK(x) +#endif + + +/* + * Checksum an MP configuration block. + */ + +static int mpf_checksum(unsigned char *mp, int len) +{ + int sum=0; + while(len--) + sum+=*mp++; + return sum&0xFF; +} + +/* + * Processor encoding in an MP configuration block + */ + +static char *mpc_family(int family,int model) +{ + static char n[32]; + static char *model_defs[]= + { + "80486DX","80486DX", + "80486SX","80486DX/2 or 80487", + "80486SL","Intel5X2(tm)", + "Unknown","Unknown", + "80486DX/4" + }; + if(family==0x6) + return("Pentium(tm) Pro"); + if(family==0x5) + return("Pentium(tm)"); + if(family==0x0F && model==0x0F) + return("Special controller"); + if(family==0x04 && model<9) + return model_defs[model]; + sprintf(n,"Unknown CPU [%d:%d]",family, model); + return n; +} + +/* + * Read the MPC + */ + +static int smp_read_mpc(struct mp_config_table *mpc) +{ + char str[16]; + int count=sizeof(*mpc); + int apics=0; + unsigned char *mpt=((unsigned char *)mpc)+count; + + if(memcmp(mpc->mpc_signature,MPC_SIGNATURE,4)) + { + printk("Bad signature [%c%c%c%c].\n", + mpc->mpc_signature[0], + mpc->mpc_signature[1], + mpc->mpc_signature[2], + mpc->mpc_signature[3]); + return 1; + } + if(mpf_checksum((unsigned char *)mpc,mpc->mpc_length)) + { + printk("Checksum error.\n"); + return 1; + } + if(mpc->mpc_spec!=0x01 && mpc->mpc_spec!=0x04) + { + printk("Bad Config Table version (%d)!!\n",mpc->mpc_spec); + return 1; + } + memcpy(str,mpc->mpc_oem,8); + str[8]=0; + printk("OEM ID: %s ",str); + memcpy(str,mpc->mpc_productid,12); + str[12]=0; + printk("Product ID: %s ",str); + printk("APIC at: 0x%lX\n",mpc->mpc_lapic); + + /* set the local APIC address */ + apic_addr = mpc->mpc_lapic; + + /* + * Now process the configuration blocks. + */ + + while(count<mpc->mpc_length) + { + switch(*mpt) + { + case MP_PROCESSOR: + { + struct mpc_config_processor *m= + (struct mpc_config_processor *)mpt; + if(m->mpc_cpuflag&CPU_ENABLED) + { + printk("Processor #%d %s APIC version %d\n", + m->mpc_apicid, + mpc_family((m->mpc_cpufeature& + CPU_FAMILY_MASK)>>8, + (m->mpc_cpufeature& + CPU_MODEL_MASK)>>4), + m->mpc_apicver); +#ifdef SMP_DEBUG + if(m->mpc_featureflag&(1<<0)) + printk(" Floating point unit present.\n"); + if(m->mpc_featureflag&(1<<7)) + printk(" Machine Exception supported.\n"); + if(m->mpc_featureflag&(1<<8)) + printk(" 64 bit compare & exchange supported.\n"); + if(m->mpc_featureflag&(1<<9)) + printk(" Internal APIC present.\n"); +#endif + if(m->mpc_cpuflag&CPU_BOOTPROCESSOR) + { + SMP_PRINTK((" Bootup CPU\n")); + boot_cpu_id=m->mpc_apicid; + } + else /* Boot CPU already counted */ + num_processors++; + + if(m->mpc_apicid>NR_CPUS) + printk("Processor #%d unused. (Max %d processors).\n",m->mpc_apicid, NR_CPUS); + else + { + cpu_present_map|=(1<<m->mpc_apicid); + apic_version[m->mpc_apicid]=m->mpc_apicver; + } + } + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_BUS: + { + struct mpc_config_bus *m= + (struct mpc_config_bus *)mpt; + memcpy(str,m->mpc_bustype,6); + str[6]=0; + SMP_PRINTK(("Bus #%d is %s\n", + m->mpc_busid, + str)); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_IOAPIC: + { + struct mpc_config_ioapic *m= + (struct mpc_config_ioapic *)mpt; + if(m->mpc_flags&MPC_APIC_USABLE) + { + apics++; + printk("I/O APIC #%d Version %d at 0x%lX.\n", + m->mpc_apicid,m->mpc_apicver, + m->mpc_apicaddr); + io_apic_addr = m->mpc_apicaddr; + } + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_INTSRC: + { + struct mpc_config_intsrc *m= + (struct mpc_config_intsrc *)mpt; + + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_LINTSRC: + { + struct mpc_config_intlocal *m= + (struct mpc_config_intlocal *)mpt; + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + } + } + if(apics>1) + printk("Warning: Multiple APIC's not supported.\n"); + return num_processors; +} + +/* + * Scan the memory blocks for an SMP configuration block. + */ + +int smp_scan_config(unsigned long base, unsigned long length) +{ + unsigned long *bp=(unsigned long *)base; + struct intel_mp_floating *mpf; + + SMP_PRINTK(("Scan SMP from %p for %ld bytes.\n", + bp,length)); + if(sizeof(*mpf)!=16) + printk("Error: MPF size\n"); + + while(length>0) + { + if(*bp==SMP_MAGIC_IDENT) + { + mpf=(struct intel_mp_floating *)bp; + if(mpf->mpf_length==1 && + !mpf_checksum((unsigned char *)bp,16) && + (mpf->mpf_specification == 1 + || mpf->mpf_specification == 4) ) + { + printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification); + if(mpf->mpf_feature2&(1<<7)) + printk(" IMCR and PIC compatibility mode.\n"); + else + printk(" Virtual Wire compatibility mode.\n"); + smp_found_config=1; + /* + * Now see if we need to read further. + */ + if(mpf->mpf_feature1!=0) + { + unsigned long cfg; + + /* + * We need to know what the local + * APIC id of the boot CPU is! + */ + +/* + * + * HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK + * + * It's not just a crazy hack... ;-) + */ + /* + * Standard page mapping + * functions don't work yet. + * We know that page 0 is not + * used. Steal it for now! + */ + + cfg=pg0[0]; + pg0[0] = (apic_addr | 7); + local_flush_tlb(); + + boot_cpu_id = GET_APIC_ID(*((volatile unsigned long *) APIC_ID)); + + /* + * Give it back + */ + + pg0[0]= cfg; + local_flush_tlb(); + +/* + * + * END OF HACK END OF HACK END OF HACK END OF HACK END OF HACK + * + */ + /* + * 2 CPUs, numbered 0 & 1. + */ + cpu_present_map=3; + num_processors=2; + printk("I/O APIC at 0xFEC00000.\n"); + printk("Bus#0 is "); + } + switch(mpf->mpf_feature1) + { + case 1: + case 5: + printk("ISA\n"); + break; + case 2: + printk("EISA with no IRQ8 chaining\n"); + break; + case 6: + case 3: + printk("EISA\n"); + break; + case 4: + case 7: + printk("MCA\n"); + break; + case 0: + break; + default: + printk("???\nUnknown standard configuration %d\n", + mpf->mpf_feature1); + return 1; + } + if(mpf->mpf_feature1>4) + { + printk("Bus #1 is PCI\n"); + + /* + * Set local APIC version to + * the integrated form. + * It's initialized to zero + * otherwise, representing + * a discrete 82489DX. + */ + apic_version[0] = 0x10; + apic_version[1] = 0x10; + } + /* + * Read the physical hardware table. + * Anything here will override the + * defaults. + */ + if(mpf->mpf_physptr) + smp_read_mpc((void *)mpf->mpf_physptr); + + /* + * Now that the boot CPU id is known, + * set some other information about it. + */ + nlong = boot_cpu_id<<24; /* Dummy 'self' for bootup */ + cpu_logical_map[0] = boot_cpu_id; + + printk("Processors: %d\n", num_processors); + /* + * Only use the first configuration found. + */ + return 1; + } + } + bp+=4; + length-=16; + } + + return 0; +} + +/* + * Trampoline 80x86 program as an array. + */ + +static unsigned char trampoline_data[]={ +#include "trampoline.hex" +}; + +/* + * Currently trivial. Write the real->protected mode + * bootstrap into the page concerned. The caller + * has made sure it's suitably aligned. + */ + +static void install_trampoline(unsigned char *mp) +{ + memcpy(mp,trampoline_data,sizeof(trampoline_data)); +} + +/* + * We are called very early to get the low memory for the trampoline/kernel stacks + * This has to be done by mm/init.c to parcel us out nice low memory. We allocate + * the kernel stacks at 4K, 8K, 12K... currently (0-03FF is preserved for SMM and + * other things). + */ + +unsigned long smp_alloc_memory(unsigned long mem_base) +{ + int size=(num_processors-1)*PAGE_SIZE; /* Number of stacks needed */ + /* + * Our stacks have to be below the 1Mb line, and mem_base on entry + * is 4K aligned. + */ + + if(mem_base+size>=0x9F000) + panic("smp_alloc_memory: Insufficient low memory for kernel stacks.\n"); + kstack_base=(void *)mem_base; + mem_base+=size; + kstack_end=(void *)mem_base; + return mem_base; +} + +/* + * Hand out stacks one at a time. + */ + +static void *get_kernel_stack(void) +{ + void *stack=kstack_base; + if(kstack_base>=kstack_end) + return NULL; + kstack_base+=PAGE_SIZE; + return stack; +} + + +/* + * The bootstrap kernel entry code has set these up. Save them for + * a given CPU + */ + +void smp_store_cpu_info(int id) +{ + struct cpuinfo_x86 *c=&cpu_data[id]; + c->hard_math=hard_math; /* Always assumed same currently */ + c->x86=x86; + c->x86_model=x86_model; + c->x86_mask=x86_mask; + c->x86_capability=x86_capability; + c->fdiv_bug=fdiv_bug; + c->wp_works_ok=wp_works_ok; /* Always assumed the same currently */ + c->hlt_works_ok=hlt_works_ok; + c->have_cpuid=have_cpuid; + c->udelay_val=loops_per_sec; + strcpy(c->x86_vendor_id, x86_vendor_id); +} + +/* + * Architecture specific routine called by the kernel just before init is + * fired off. This allows the BP to have everything in order [we hope]. + * At the end of this all the AP's will hit the system scheduling and off + * we go. Each AP will load the system gdt's and jump through the kernel + * init into idle(). At this point the scheduler will one day take over + * and give them jobs to do. smp_callin is a standard routine + * we use to track CPU's as they power up. + */ + +void smp_commence(void) +{ + /* + * Lets the callin's below out of their loop. + */ + smp_commenced=1; +} + +void smp_callin(void) +{ + extern void calibrate_delay(void); + int cpuid=GET_APIC_ID(apic_read(APIC_ID)); + unsigned long l; + + /* + * Activate our APIC + */ + + SMP_PRINTK(("CALLIN %d\n",smp_processor_id())); + l=apic_read(APIC_SPIV); + l|=(1<<8); /* Enable */ + apic_write(APIC_SPIV,l); + sti(); + /* + * Get our bogomips. + */ + calibrate_delay(); + /* + * Save our processor parameters + */ + smp_store_cpu_info(cpuid); + /* + * Allow the master to continue. + */ + set_bit(cpuid, (unsigned long *)&cpu_callin_map[0]); + /* + * Until we are ready for SMP scheduling + */ + load_ldt(0); +/* printk("Testing faulting...\n"); + *(long *)0=1; OOPS... */ + local_flush_tlb(); + while(!smp_commenced); + if (cpu_number_map[cpuid] == -1) + while(1); + local_flush_tlb(); + SMP_PRINTK(("Commenced..\n")); + + load_TR(cpu_number_map[cpuid]); +/* while(1);*/ +} + +/* + * Cycle through the processors sending pentium IPI's to boot each. + */ + +void smp_boot_cpus(void) +{ + int i; + int cpucount=0; + unsigned long cfg; + void *stack; + extern unsigned long init_user_stack[]; + + /* + * Initialize the logical to physical cpu number mapping + */ + + for (i = 0; i < NR_CPUS; i++) + cpu_number_map[i] = -1; + + /* + * Setup boot CPU information + */ + + kernel_stacks[boot_cpu_id]=(void *)init_user_stack; /* Set up for boot processor first */ + + smp_store_cpu_info(boot_cpu_id); /* Final full version of the data */ + + cpu_present_map |= (1 << smp_processor_id()); + cpu_number_map[boot_cpu_id] = 0; + active_kernel_processor=boot_cpu_id; + + /* + * If we don't conform to the Intel MPS standard, get out + * of here now! + */ + + if (!smp_found_config) + return; + + /* + * Map the local APIC into kernel space + */ + + apic_reg = ioremap(apic_addr,4096); + + if(apic_reg == NULL) + panic("Unable to map local apic.\n"); + +#ifdef SMP_DEBUG + { + int reg; + + /* + * This is to verify that we're looking at + * a real local APIC. Check these against + * your board if the CPUs aren't getting + * started for no apparent reason. + */ + + reg = apic_read(APIC_VERSION); + SMP_PRINTK(("Getting VERSION: %x\n", reg)); + + apic_write(APIC_VERSION, 0); + reg = apic_read(APIC_VERSION); + SMP_PRINTK(("Getting VERSION: %x\n", reg)); + + /* + * The two version reads above should print the same + * NON-ZERO!!! numbers. If the second one is zero, + * there is a problem with the APIC write/read + * definitions. + * + * The next two are just to see if we have sane values. + * They're only really relevant if we're in Virtual Wire + * compatibility mode, but most boxes are anymore. + */ + + + reg = apic_read(APIC_LVT0); + SMP_PRINTK(("Getting LVT0: %x\n", reg)); + + reg = apic_read(APIC_LVT1); + SMP_PRINTK(("Getting LVT1: %x\n", reg)); + } +#endif + + /* + * Enable the local APIC + */ + + cfg=apic_read(APIC_SPIV); + cfg|=(1<<8); /* Enable APIC */ + apic_write(APIC_SPIV,cfg); + + udelay(10); + + /* + * Now scan the cpu present map and fire up the other CPUs. + */ + + SMP_PRINTK(("CPU map: %lx\n", cpu_present_map)); + + for(i=0;i<NR_CPUS;i++) + { + /* + * Don't even attempt to start the boot CPU! + */ + if (i == boot_cpu_id) + continue; + + if (cpu_present_map & (1 << i)) + { + unsigned long send_status, accept_status; + int timeout, num_starts, j; + + /* + * We need a kernel stack for each processor. + */ + + stack=get_kernel_stack(); /* We allocated these earlier */ + if(stack==NULL) + panic("No memory for processor stacks.\n"); + kernel_stacks[i]=stack; + install_trampoline(stack); + + printk("Booting processor %d stack %p: ",i,stack); /* So we set what's up */ + + /* + * This grunge runs the startup process for + * the targeted processor. + */ + + SMP_PRINTK(("Setting warm reset code and vector.\n")); + + /* + * Install a writable page 0 entry. + */ + + cfg=pg0[0]; + + CMOS_WRITE(0xa, 0xf); + pg0[0]=7; + local_flush_tlb(); + *((volatile unsigned short *) 0x469) = ((unsigned long)stack)>>4; + *((volatile unsigned short *) 0x467) = 0; + + /* + * Protect it again + */ + + pg0[0]= cfg; + local_flush_tlb(); + + /* + * Be paranoid about clearing APIC errors. + */ + + if ( apic_version[i] & 0xF0 ) + { + apic_write(APIC_ESR, 0); + accept_status = (apic_read(APIC_ESR) & 0xEF); + } + + /* + * Status is now clean + */ + + send_status = 0; + accept_status = 0; + + /* + * Starting actual IPI sequence... + */ + + SMP_PRINTK(("Asserting INIT.\n")); + + /* + * Turn INIT on + */ + + cfg=apic_read(APIC_ICR2); + cfg&=0x00FFFFFF; + apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */ + cfg=apic_read(APIC_ICR); + cfg&=~0xCDFFF; /* Clear bits */ + cfg |= (APIC_DEST_FIELD | APIC_DEST_LEVELTRIG + | APIC_DEST_ASSERT | APIC_DEST_DM_INIT); + apic_write(APIC_ICR, cfg); /* Send IPI */ + + udelay(200); + SMP_PRINTK(("Deasserting INIT.\n")); + + cfg=apic_read(APIC_ICR2); + cfg&=0x00FFFFFF; + apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */ + cfg=apic_read(APIC_ICR); + cfg&=~0xCDFFF; /* Clear bits */ + cfg |= (APIC_DEST_FIELD | APIC_DEST_LEVELTRIG + | APIC_DEST_DM_INIT); + apic_write(APIC_ICR, cfg); /* Send IPI */ + + /* + * Should we send STARTUP IPIs ? + * + * Determine this based on the APIC version. + * If we don't have an integrated APIC, don't + * send the STARTUP IPIs. + */ + + if ( apic_version[i] & 0xF0 ) + num_starts = 2; + else + num_starts = 0; + + /* + * Run STARTUP IPI loop. + */ + + for (j = 1; !(send_status || accept_status) + && (j <= num_starts) ; j++) + { + SMP_PRINTK(("Sending STARTUP #%d.\n",j)); + + apic_write(APIC_ESR, 0); + + /* + * STARTUP IPI + */ + + cfg=apic_read(APIC_ICR2); + cfg&=0x00FFFFFF; + apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */ + cfg=apic_read(APIC_ICR); + cfg&=~0xCDFFF; /* Clear bits */ + cfg |= (APIC_DEST_FIELD + | APIC_DEST_DM_STARTUP + | (((int) stack) >> 12) ); /* Boot on the stack */ + apic_write(APIC_ICR, cfg); /* Kick the second */ + + timeout = 0; + do { + udelay(10); + } while ( (send_status = (apic_read(APIC_ICR) & 0x1000)) + && (timeout++ < 1000)); + udelay(200); + + accept_status = (apic_read(APIC_ESR) & 0xEF); + } + + if (send_status) /* APIC never delivered?? */ + printk("APIC never delivered???\n"); + if (accept_status) /* Send accept error */ + printk("APIC delivery error (%lx).\n", accept_status); + + if( !(send_status || accept_status) ) + { + for(timeout=0;timeout<50000;timeout++) + { + if(cpu_callin_map[0]&(1<<i)) + break; /* It has booted */ + udelay(100); /* Wait 5s total for a response */ + } + if(cpu_callin_map[0]&(1<<i)) + { + cpucount++; + /* number CPUs logically, starting from 1 (BSP is 0) */ + cpu_number_map[i] = cpucount; + cpu_logical_map[cpucount] = i; + } + else + { + if(*((volatile unsigned char *)8192)==0xA5) + printk("Stuck ??\n"); + else + printk("Not responding.\n"); + } + } + + /* mark "stuck" area as not stuck */ + *((volatile unsigned long *)8192) = 0; + } + + /* + * Make sure we unmap all failed CPUs + */ + + if (cpu_number_map[i] == -1) + cpu_present_map &= ~(1 << i); + } + + /* + * Cleanup possible dangling ends... + */ + + /* + * Install writable page 0 entry. + */ + + cfg = pg0[0]; + pg0[0] = 3; /* writeable, present, addr 0 */ + local_flush_tlb(); + + /* + * Paranoid: Set warm reset code and vector here back + * to default values. + */ + + CMOS_WRITE(0, 0xf); + + *((volatile long *) 0x467) = 0; + + /* + * Restore old page 0 entry. + */ + + pg0[0] = cfg; + local_flush_tlb(); + + /* + * Allow the user to impress friends. + */ + + if(cpucount==0) + { + printk("Error: only one processor found.\n"); + cpu_present_map=(1<<smp_processor_id()); + } + else + { + unsigned long bogosum=0; + for(i=0;i<32;i++) + { + if(cpu_present_map&(1<<i)) + bogosum+=cpu_data[i].udelay_val; + } + printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n", + cpucount+1, + (bogosum+2500)/500000, + ((bogosum+2500)/5000)%100); + smp_activated=1; + smp_num_cpus=cpucount+1; + } +} + + +/* + * A non wait message cannot pass data or cpu source info. This current setup + * is only safe because the kernel lock owner is the only person who can send a message. + * + * Wrapping this whole block in a spinlock is not the safe answer either. A processor may + * get stuck with irq's off waiting to send a message and thus not replying to the person + * spinning for a reply.... + * + * In the end flush tlb ought to be the NMI and a very very short function (to avoid the old + * IDE disk problems), and other messages sent with IRQ's enabled in a civilised fashion. That + * will also boost performance. + */ + +void smp_message_pass(int target, int msg, unsigned long data, int wait) +{ + unsigned long cfg; + unsigned long target_map; + int p=smp_processor_id(); + int irq=0x2d; /* IRQ 13 */ + int ct=0; + static volatile int message_cpu = NO_PROC_ID; + + /* + * During boot up send no messages + */ + + if(!smp_activated || !smp_commenced) + return; + + + /* + * Skip the reschedule if we are waiting to clear a + * message at this time. The reschedule cannot wait + * but is not critical. + */ + + if(msg==MSG_RESCHEDULE) /* Reschedules we do via trap 0x30 */ + { + irq=0x30; + if(smp_cpu_in_msg[p]) + return; + } + + /* + * Sanity check we don't re-enter this across CPU's. Only the kernel + * lock holder may send messages. For a STOP_CPU we are bringing the + * entire box to the fastest halt we can.. A reschedule carries + * no data and can occur during a flush.. guess what panic + * I got to notice this bug... + */ + + if(message_cpu!=NO_PROC_ID && msg!=MSG_STOP_CPU && msg!=MSG_RESCHEDULE) + { + panic("CPU #%d: Message pass %d but pass in progress by %d of %d\n", + smp_processor_id(),msg,message_cpu, smp_msg_id); + } + message_cpu=smp_processor_id(); + + + /* + * We are busy + */ + + smp_cpu_in_msg[p]++; + + /* + * Reschedule is currently special + */ + + if(msg!=MSG_RESCHEDULE) + { + smp_src_cpu=p; + smp_msg_id=msg; + smp_msg_data=data; + } + +/* printk("SMP message pass #%d to %d of %d\n", + p, msg, target);*/ + + /* + * Wait for the APIC to become ready - this should never occur. Its + * a debugging check really. + */ + + while(ct<1000) + { + cfg=apic_read(APIC_ICR); + if(!(cfg&(1<<12))) + break; + ct++; + udelay(10); + } + + /* + * Just pray... there is nothing more we can do + */ + + if(ct==1000) + printk("CPU #%d: previous IPI still not cleared after 10mS", smp_processor_id()); + + /* + * Program the APIC to deliver the IPI + */ + + cfg=apic_read(APIC_ICR2); + cfg&=0x00FFFFFF; + apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(target)); /* Target chip */ + cfg=apic_read(APIC_ICR); + cfg&=~0xFDFFF; /* Clear bits */ + cfg|=APIC_DEST_FIELD|APIC_DEST_DM_FIXED|irq; /* Send an IRQ 13 */ + + /* + * Set the target requirement + */ + + if(target==MSG_ALL_BUT_SELF) + { + cfg|=APIC_DEST_ALLBUT; + target_map=cpu_present_map; + cpu_callin_map[0]=(1<<smp_src_cpu); + } + else if(target==MSG_ALL) + { + cfg|=APIC_DEST_ALLINC; + target_map=cpu_present_map; + cpu_callin_map[0]=0; + } + else + { + target_map=(1<<target); + cpu_callin_map[0]=0; + } + + /* + * Send the IPI. The write to APIC_ICR fires this off. + */ + + apic_write(APIC_ICR, cfg); + + /* + * Spin waiting for completion + */ + + switch(wait) + { + case 1: + while(cpu_callin_map[0]!=target_map); /* Spin on the pass */ + break; + case 2: + while(smp_invalidate_needed); /* Wait for invalidate map to clear */ + break; + } + + /* + * Record our completion + */ + + smp_cpu_in_msg[p]--; + message_cpu=NO_PROC_ID; +} + +/* + * This is fraught with deadlocks. Linus does a flush tlb at a whim + * even with IRQ's off. We have to avoid a pair of crossing flushes + * or we are doomed. See the notes about smp_message_pass. + */ + +void smp_flush_tlb(void) +{ + unsigned long flags; + if(smp_activated && smp_processor_id()!=active_kernel_processor) + panic("CPU #%d:Attempted flush tlb IPI when not AKP(=%d)\n",smp_processor_id(),active_kernel_processor); +/* printk("SMI-");*/ + + /* + * The assignment is safe because it's volatile so the compiler cannot reorder it, + * because the i586 has strict memory ordering and because only the kernel lock holder + * may issue a tlb flush. If you break any one of those three change this to an atomic + * bus locked or. + */ + + smp_invalidate_needed=cpu_present_map&~(1<<smp_processor_id()); + + /* + * Processors spinning on the lock will see this IRQ late. The smp_invalidate_needed map will + * ensure they don't do a spurious flush tlb or miss one. + */ + + save_flags(flags); + cli(); + smp_message_pass(MSG_ALL_BUT_SELF, MSG_INVALIDATE_TLB, 0L, 2); + + /* + * Flush the local TLB + */ + + local_flush_tlb(); + + restore_flags(flags); + + /* + * Completed. + */ + +/* printk("SMID\n");*/ +} + +/* + * Reschedule call back + */ + +void smp_reschedule_irq(int cpl, struct pt_regs *regs) +{ +#ifdef DEBUGGING_SMP_RESCHED + static int ct=0; + if(ct==0) + { + printk("Beginning scheduling on CPU#%d\n",smp_processor_id()); + ct=1; + } +#endif + if(smp_processor_id()!=active_kernel_processor) + panic("SMP Reschedule on CPU #%d, but #%d is active.\n", + smp_processor_id(), active_kernel_processor); + + need_resched=1; + + /* + * Clear the IPI + */ + apic_read(APIC_SPIV); /* Dummy read */ + apic_write(APIC_EOI, 0); /* Docs say use 0 for future compatibility */ +} + +/* + * Message call back. + */ + +void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs) +{ + int i=smp_processor_id(); +/* static int n=0; + if(n++<NR_CPUS) + printk("IPI %d->%d(%d,%ld)\n",smp_src_cpu,i,smp_msg_id,smp_msg_data);*/ + switch(smp_msg_id) + { + case 0: /* IRQ 13 testing - boring */ + return; + + /* + * A TLB flush is needed. + */ + + case MSG_INVALIDATE_TLB: + if(clear_bit(i,(unsigned long *)&smp_invalidate_needed)) + local_flush_tlb(); + set_bit(i, (unsigned long *)&cpu_callin_map[0]); + /* cpu_callin_map[0]|=1<<smp_processor_id();*/ + break; + + /* + * Halt other CPU's for a panic or reboot + */ + case MSG_STOP_CPU: + while(1) + { + if(cpu_data[smp_processor_id()].hlt_works_ok) + __asm__("hlt"); + } + default: + printk("CPU #%d sent invalid cross CPU message to CPU #%d: %X(%lX).\n", + smp_src_cpu,smp_processor_id(),smp_msg_id,smp_msg_data); + break; + } + /* + * Clear the IPI, so we can receive future IPI's + */ + + apic_read(APIC_SPIV); /* Dummy read */ + apic_write(APIC_EOI, 0); /* Docs say use 0 for future compatibility */ +} diff --git a/arch/i386/kernel/sys_i386.c b/arch/i386/kernel/sys_i386.c new file mode 100644 index 000000000..619b372d8 --- /dev/null +++ b/arch/i386/kernel/sys_i386.c @@ -0,0 +1,181 @@ +/* + * linux/arch/i386/kernel/sys_i386.c + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/i386 + * platform. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/stat.h> +#include <linux/mman.h> +#include <linux/file.h> + +#include <asm/uaccess.h> +#include <asm/ipc.h> + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +asmlinkage int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + error = verify_area(VERIFY_WRITE,fildes,8); + if (error) + return error; + error = do_pipe(fd); + if (error) + return error; + put_user(fd[0],0+fildes); + put_user(fd[1],1+fildes); + return 0; +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +asmlinkage int old_mmap(struct mmap_arg_struct *arg) +{ + int error; + struct file * file = NULL; + struct mmap_arg_struct a; + + error = verify_area(VERIFY_READ, arg, sizeof(*arg)); + if (error) + return error; + copy_from_user(&a, arg, sizeof(a)); + if (!(a.flags & MAP_ANONYMOUS)) { + if (a.fd >= NR_OPEN || !(file = current->files->fd[a.fd])) + return -EBADF; + } + a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + error = do_mmap(file, a.addr, a.len, a.prot, a.flags, a.offset); + return error; +} + +extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); + +struct sel_arg_struct { + unsigned long n; + fd_set *inp, *outp, *exp; + struct timeval *tvp; +}; + +asmlinkage int old_select(struct sel_arg_struct *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) +{ + int version; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + if (call <= SEMCTL) + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + int err; + if (!ptr) + return -EINVAL; + if ((err = verify_area (VERIFY_READ, ptr, sizeof(long)))) + return err; + get_user(fourth.__pad, (void **) ptr); + return sys_semctl (first, second, third, fourth); + } + default: + return -EINVAL; + } + if (call <= MSGCTL) + switch (call) { + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + int err; + if (!ptr) + return -EINVAL; + if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp)))) + return err; + copy_from_user(&tmp,(struct ipc_kludge *) ptr, sizeof (tmp)); + return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); + } + case 1: default: + return sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + default: + return -EINVAL; + } + if (call <= SHMCTL) + switch (call) { + case SHMAT: + switch (version) { + case 0: default: { + ulong raddr; + int err; + if ((err = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong)))) + return err; + err = sys_shmat (first, (char *) ptr, second, &raddr); + if (err) + return err; + put_user (raddr, (ulong *) third); + return 0; + } + case 1: /* iBCS2 emulator entry point */ + if (get_fs() != get_ds()) + return -EINVAL; + return sys_shmat (first, (char *) ptr, second, (ulong *) third); + } + case SHMDT: + return sys_shmdt ((char *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, (struct shmid_ds *) ptr); + default: + return -EINVAL; + } + return -EINVAL; +} diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c new file mode 100644 index 000000000..0eddd66e5 --- /dev/null +++ b/arch/i386/kernel/time.c @@ -0,0 +1,500 @@ +/* + * linux/arch/i386/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * This file contains the PC-specific time handling details: + * reading the RTC at bootup, etc.. + * 1994-07-02 Alan Modra + * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime + * 1995-03-26 Markus Kuhn + * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 + * precision CMOS clock update + * 1996-05-03 Ingo Molnar + * fixed time warps in do_[slow|fast]_gettimeoffset() + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/delay.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/delay.h> + +#include <linux/mc146818rtc.h> +#include <linux/timex.h> +#include <linux/config.h> + +extern int setup_x86_irq(int, struct irqaction *); + +#ifndef CONFIG_APM /* cycle counter may be unreliable */ +/* Cycle counter value at the previous timer interrupt.. */ +static struct { + unsigned long low; + unsigned long high; +} init_timer_cc, last_timer_cc; + +/* + * This is more assembly than C, but it's also rather + * timing-critical and we have to use assembler to get + * reasonable 64-bit arithmetic + */ +static unsigned long do_fast_gettimeoffset(void) +{ + register unsigned long eax asm("ax"); + register unsigned long edx asm("dx"); + unsigned long tmp, quotient, low_timer, missing_time; + + /* Last jiffy when do_fast_gettimeoffset() was called.. */ + static unsigned long last_jiffies=0; + + /* Cached "clocks per usec" value.. */ + static unsigned long cached_quotient=0; + + /* The "clocks per usec" value is calculated once each jiffy */ + tmp = jiffies; + quotient = cached_quotient; + low_timer = last_timer_cc.low; + missing_time = 0; + if (last_jiffies != tmp) { + last_jiffies = tmp; + /* + * test for hanging bottom handler (this means xtime is not + * updated yet) + */ + if (test_bit(TIMER_BH, &bh_active) ) + { + missing_time = 1000020/HZ; + } + + /* Get last timer tick in absolute kernel time */ + eax = low_timer; + edx = last_timer_cc.high; + __asm__("subl "SYMBOL_NAME_STR(init_timer_cc)",%0\n\t" + "sbbl "SYMBOL_NAME_STR(init_timer_cc)"+4,%1" + :"=a" (eax), "=d" (edx) + :"0" (eax), "1" (edx)); + + /* + * Divide the 64-bit time with the 32-bit jiffy counter, + * getting the quotient in clocks. + * + * Giving quotient = "average internal clocks per usec" + */ + __asm__("divl %2" + :"=a" (eax), "=d" (edx) + :"r" (tmp), + "0" (eax), "1" (edx)); + + edx = 1000020/HZ; + tmp = eax; + eax = 0; + + __asm__("divl %2" + :"=a" (eax), "=d" (edx) + :"r" (tmp), + "0" (eax), "1" (edx)); + cached_quotient = eax; + quotient = eax; + } + + /* Read the time counter */ + __asm__(".byte 0x0f,0x31" + :"=a" (eax), "=d" (edx)); + + /* .. relative to previous jiffy (32 bits is enough) */ + edx = 0; + eax -= low_timer; + + /* + * Time offset = (1000020/HZ * time_low) / quotient. + */ + + __asm__("mull %2" + :"=a" (eax), "=d" (edx) + :"r" (quotient), + "0" (eax), "1" (edx)); + + /* + * Due to rounding errors (and jiffies inconsistencies), + * we need to check the result so that we'll get a timer + * that is monotonic. + */ + if (edx >= 1000020/HZ) + edx = 1000020/HZ-1; + + eax = edx + missing_time; + return eax; +} +#endif + +/* This function must be called with interrupts disabled + * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs + * + * However, the pc-audio speaker driver changes the divisor so that + * it gets interrupted rather more often - it loads 64 into the + * counter rather than 11932! This has an adverse impact on + * do_gettimeoffset() -- it stops working! What is also not + * good is that the interval that our timer function gets called + * is no longer 10.0002 ms, but 9.9767 ms. To get around this + * would require using a different timing source. Maybe someone + * could use the RTC - I know that this can interrupt at frequencies + * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix + * it so that at startup, the timer code in sched.c would select + * using either the RTC or the 8253 timer. The decision would be + * based on whether there was any other device around that needed + * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz, + * and then do some jiggery to have a version of do_timer that + * advanced the clock by 1/1024 s. Every time that reached over 1/100 + * of a second, then do all the old code. If the time was kept correct + * then do_gettimeoffset could just return 0 - there is no low order + * divider that can be accessed. + * + * Ideally, you would be able to use the RTC for the speaker driver, + * but it appears that the speaker driver really needs interrupt more + * often than every 120 us or so. + * + * Anyway, this needs more thought.... pjsg (1993-08-28) + * + * If you are really that interested, you should be reading + * comp.protocols.time.ntp! + */ + +#define TICK_SIZE tick + +static unsigned long do_slow_gettimeoffset(void) +{ + int count; + static int count_p = 0; + unsigned long offset = 0; + static unsigned long jiffies_p = 0; + + /* + * cache volatile jiffies temporarily; we have IRQs turned off. + */ + unsigned long jiffies_t; + + /* timer count may underflow right here */ + outb_p(0x00, 0x43); /* latch the count ASAP */ + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + + jiffies_t = jiffies; + + /* + * avoiding timer inconsistencies (they are rare, but they happen)... + * there are three kinds of problems that must be avoided here: + * 1. the timer counter underflows + * 2. hardware problem with the timer, not giving us continuous time, + * the counter does small "jumps" upwards on some Pentium systems, + * thus causes time warps + * 3. we are after the timer interrupt, but the bottom half handler + * hasn't executed yet. + */ + if( count > count_p ) { + if( jiffies_t == jiffies_p ) { + if( count > LATCH-LATCH/100 ) + offset = TICK_SIZE; + else + /* + * argh, the timer is bugging we cant do nothing + * but to give the previous clock value. + */ + count = count_p; + } else { + if( test_bit(TIMER_BH, &bh_active) ) { + /* + * we have detected a counter underflow. + */ + offset = TICK_SIZE; + count_p = count; + } else { + count_p = count; + jiffies_p = jiffies_t; + } + } + } else { + count_p = count; + jiffies_p = jiffies_t; + } + + + count = ((LATCH-1) - count) * TICK_SIZE; + count = (count + LATCH/2) / LATCH; + + return offset + count; +} + +/* + * this is only used if we have fast gettimeoffset: + */ +void do_x86_get_fast_time(struct timeval * tv) +{ + do_gettimeofday(tv); +} + +static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; + +/* + * This version of gettimeofday has near microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + + save_flags(flags); + cli(); + *tv = xtime; + tv->tv_usec += do_gettimeoffset(); + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } + restore_flags(flags); +} + +void do_settimeofday(struct timeval *tv) +{ + cli(); + /* This is revolting. We need to set the xtime.tv_usec + * correctly. However, the value in this location is + * is value at the last tick. + * Discover what correction gettimeofday + * would have done, and then undo it! + */ + tv->tv_usec -= do_gettimeoffset(); + + if (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_state = TIME_BAD; + time_maxerror = MAXPHASE; + time_esterror = MAXPHASE; + sti(); +} + + +/* + * In order to set the CMOS clock precisely, set_rtc_mmss has to be + * called 500 ms after the second nowtime has started, because when + * nowtime is written into the registers of the CMOS clock, it will + * jump to the next second precisely 500 ms later. Check the Motorola + * MC146818A or Dallas DS12887 data sheet for details. + */ +static int set_rtc_mmss(unsigned long nowtime) +{ + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + unsigned char save_control, save_freq_select; + + save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + cmos_minutes = CMOS_READ(RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + BCD_TO_BIN(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(real_seconds); + BIN_TO_BCD(real_minutes); + } + CMOS_WRITE(real_seconds,RTC_SECONDS); + CMOS_WRITE(real_minutes,RTC_MINUTES); + } else + retval = -1; + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + + return retval; +} + +/* last time the cmos clock got updated */ +static long last_rtc_update = 0; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static inline void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + do_timer(regs); + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec > 500000 - (tick >> 1) && + xtime.tv_usec < 500000 + (tick >> 1)) + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + /* As we return to user mode fire off the other CPU schedulers.. this is + basically because we don't yet share IRQ's around. This message is + rigged to be safe on the 386 - basically it's a hack, so don't look + closely for now.. */ + /*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */ + +} + +#ifndef CONFIG_APM /* cycle counter may be unreliable */ +/* + * This is the same as the above, except we _also_ save the current + * cycle counter value at the time of the timer interrupt, so that + * we later on can estimate the time of day more exactly. + */ +static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* read Pentium cycle counter */ + __asm__(".byte 0x0f,0x31" + :"=a" (last_timer_cc.low), + "=d" (last_timer_cc.high)); + timer_interrupt(irq, NULL, regs); +} +#endif + +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +static inline unsigned long mktime(unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((( + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + +unsigned long get_cmos_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + /* read RTC exactly on falling edge of update flag */ + for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) + break; + do { /* Isn't this overkill ? UIP above should guarantee consistency */ + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + +static struct irqaction irq0 = { timer_interrupt, 0, 0, "timer", NULL, NULL}; + +void time_init(void) +{ + xtime.tv_sec = get_cmos_time(); + xtime.tv_usec = 0; + + /* If we have the CPU hardware time counters, use them */ +#ifndef CONFIG_APM + /* Don't use them if a suspend/resume could + corrupt the timer value. This problem + needs more debugging. */ + if (x86_capability & 16) { + do_gettimeoffset = do_fast_gettimeoffset; + do_get_fast_time = do_x86_get_fast_time; + + if( strcmp( x86_vendor_id, "AuthenticAMD" ) == 0 ) { + if( x86 == 5 ) { + if( x86_model == 0 ) { + /* turn on cycle counters during power down */ + __asm__ __volatile__ (" movl $0x83, %%ecx \n \ + .byte 0x0f,0x32 \n \ + orl $1,%%eax \n \ + .byte 0x0f,0x30 \n " + : : : "ax", "cx", "dx" ); + udelay(500); + } + } + } + + /* read Pentium cycle counter */ + __asm__(".byte 0x0f,0x31" + :"=a" (init_timer_cc.low), + "=d" (init_timer_cc.high)); + irq0.handler = pentium_timer_interrupt; + } +#endif + setup_x86_irq(0, &irq0); +} diff --git a/arch/i386/kernel/trampoline.S b/arch/i386/kernel/trampoline.S new file mode 100644 index 000000000..29bcc9040 --- /dev/null +++ b/arch/i386/kernel/trampoline.S @@ -0,0 +1,74 @@ +! +! Trampoline.S Derived from Setup.S by Linus Torvalds +! +! Entry: CS:IP point to the start of our code, we are +! in real mode with no stack, but the rest of the +! trampoline page to make our stack and everything else +! is a mystery. +! +! In fact we don't actually need a stack so we don't +! set one up. +! +! We jump into the boot/compressed/head.S code. So you'd +! better be running a compressed kernel image or you +! won't get very far. +! +#define __ASSEMBLY__ +#include <asm/segment.h> + +.text + extrn startup32 + +entry start +start: +! nop +! jmp start ! Test + mov ax,cs ! Code and data in the same place + mov ds,ax ! + mov cx,ax ! Pass stack info to the 32bit boot + add cx,cx + add cx,cx + add cx,cx + add cx,cx ! Segment -> Offset + add cx, #4096 ! End of page is wanted + mov bx,#1 ! Flag an SMP trampoline + cli ! We should be safe anyway + + lidt idt_48 ! load idt with 0,0 + lgdt gdt_48 ! load gdt with whatever is appropriate + + xor ax,ax + inc ax ! protected mode (PE) bit + lmsw ax ! Into protected mode + jmp flush_instr +flush_instr: + jmpi 8192+startup32,KERNEL_CS ! Jump to the 32bit trampoline code +! jmpi 0x100000,KERNEL_CS ! Jump into the 32bit startup +! .byte 0x66,0x67 ! 32bit +! .byte 0xea,0x00,0x00,0x10,0x00,0x10,0x00 !jmpi .0x100000,KERNEL_CS + +gdt: + .word 0,0,0,0 ! dummy + + .word 0,0,0,0 ! unused + + .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb) + .word 0x0000 ! base address=0 + .word 0x9A00 ! code read/exec + .word 0x00C0 ! granularity=4096, 386 + + .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb) + .word 0x0000 ! base address=0 + .word 0x9200 ! data read/write + .word 0x00C0 ! granularity=4096, 386 + +idt_48: + .word 0 ! idt limit=0 + .word 0,0 ! idt base=0L + +gdt_48: + .word 0x800 ! gdt limit=2048, 256 GDT entries + .word 8192+gdt,0x0 ! gdt base = 8192+gdt (first SMP CPU) + ! we load the others with the first table + ! saves rewriting gdt_48 for each + diff --git a/arch/i386/kernel/trampoline32.S b/arch/i386/kernel/trampoline32.S new file mode 100644 index 000000000..fafa972a0 --- /dev/null +++ b/arch/i386/kernel/trampoline32.S @@ -0,0 +1,20 @@ +! +! 32bit side of the trampoline code +! +#define __ASSEMBLY__ +#include <asm/segment.h> +! +! +! Anything but a relative address here will be wrong by 8K... +! + .globl startup32 +.text +startup32: +! Run the kernel + mov eax,#KERNEL_DS + mov ds,ax + mov eax,#0xA5A5A5A5 + mov [8192],eax + jmpi 0x100000,KERNEL_CS +l1: + .byte 0xEA,0x00,0x00,0x10,0x00,0x10,0x00 diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 6dd7fc65b..8dfab81c5 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -10,6 +10,7 @@ * to mainly kill the offending process (probably by giving it a signal, * but possibly by killing it outright if necessary). */ +#include <linux/config.h> #include <linux/head.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -21,12 +22,12 @@ #include <linux/mm.h> #include <asm/system.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/io.h> asmlinkage int system_call(void); asmlinkage void lcall7(void); -struct desc_struct default_ldt; +struct desc_struct default_ldt = { 0, 0 }; static inline void console_verbose(void) { @@ -39,9 +40,7 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ tsk->tss.error_code = error_code; \ tsk->tss.trap_no = trapnr; \ - if (signr == SIGTRAP && current->flags & PF_PTRACED) \ - current->blocked &= ~(1 << (SIGTRAP-1)); \ - send_sig(signr, tsk, 1); \ + force_sig(signr, tsk); \ die_if_kernel(str,regs,error_code); \ } @@ -82,6 +81,7 @@ asmlinkage void page_fault(void); asmlinkage void coprocessor_error(void); asmlinkage void reserved(void); asmlinkage void alignment_check(void); +asmlinkage void spurious_interrupt_bug(void); int kstack_depth_to_print = 24; @@ -93,31 +93,32 @@ int kstack_depth_to_print = 24; #define VMALLOC_OFFSET (8*1024*1024) #define MODULE_RANGE (8*1024*1024) -/*static*/ void die_if_kernel(char * str, struct pt_regs * regs, long err) +/*static*/ void die_if_kernel(const char * str, struct pt_regs * regs, long err) { int i; unsigned long esp; unsigned short ss; unsigned long *stack, addr, module_start, module_end; - extern char start_kernel, etext; + extern char start_kernel, _etext; esp = (unsigned long) ®s->esp; ss = KERNEL_DS; - if ((regs->eflags & VM_MASK) || (3 & regs->cs) == 3) + if ((regs->eflags & VM_MASK) || (3 & regs->xcs) == 3) return; - if (regs->cs & 3) { + if (regs->xcs & 3) { esp = regs->esp; - ss = regs->ss; + ss = regs->xss & 0xffff; } console_verbose(); printk("%s: %04lx\n", str, err & 0xffff); - printk("EIP: %04x:%08lx\nEFLAGS: %08lx\n", 0xffff & regs->cs,regs->eip,regs->eflags); + printk("CPU: %d\n", smp_processor_id()); + printk("EIP: %04x:[<%08lx>]\nEFLAGS: %08lx\n", 0xffff & regs->xcs,regs->eip,regs->eflags); printk("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", regs->eax, regs->ebx, regs->ecx, regs->edx); printk("esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n", regs->esi, regs->edi, regs->ebp, esp); - printk("ds: %04x es: %04x fs: %04x gs: %04x ss: %04x\n", - regs->ds, regs->es, regs->fs, regs->gs, ss); + printk("ds: %04x es: %04x ss: %04x\n", + regs->xds & 0xffff, regs->xes & 0xffff, ss); store_TR(i); if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) printk("Corrupted stack page\n"); @@ -134,7 +135,8 @@ int kstack_depth_to_print = 24; printk("\nCall Trace: "); stack = (unsigned long *) esp; i = 1; - module_start = ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); + module_start = PAGE_OFFSET + (max_mapnr << PAGE_SHIFT); + module_start = ((module_start + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); module_end = module_start + MODULE_RANGE; while (((long) stack & 4095) != 0) { addr = get_seg_long(ss, stack++); @@ -147,17 +149,17 @@ int kstack_depth_to_print = 24; * out the call path that was taken. */ if (((addr >= (unsigned long) &start_kernel) && - (addr <= (unsigned long) &etext)) || + (addr <= (unsigned long) &_etext)) || ((addr >= module_start) && (addr <= module_end))) { if (i && ((i % 8) == 0)) printk("\n "); - printk("%08lx ", addr); + printk("[<%08lx>] ", addr); i++; } } printk("\nCode: "); for(i=0;i<20;i++) - printk("%02x ",0xff & get_seg_byte(regs->cs,(i+(char *)regs->eip))); + printk("%02x ",0xff & get_seg_byte(regs->xcs & 0xffff,(i+(char *)regs->eip))); printk("\n"); do_exit(SIGSEGV); } @@ -173,8 +175,8 @@ DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current) DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) -DO_ERROR(15, SIGSEGV, "reserved", reserved, current) DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) +DO_ERROR(18, SIGSEGV, "reserved", reserved, current) asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) { @@ -185,16 +187,20 @@ asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) die_if_kernel("general protection",regs,error_code); current->tss.error_code = error_code; current->tss.trap_no = 13; - send_sig(SIGSEGV, current, 1); + force_sig(SIGSEGV, current); } asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { +#ifdef CONFIG_SMP_NMI_INVAL + smp_flush_tlb_rcv(); +#else #ifndef CONFIG_IGNORE_NMI printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); printk("You probably have a hardware problem with your RAM chips or a\n"); printk("power saving mode enabled.\n"); #endif +#endif } asmlinkage void do_debug(struct pt_regs * regs, long error_code) @@ -203,12 +209,10 @@ asmlinkage void do_debug(struct pt_regs * regs, long error_code) handle_vm86_debug((struct vm86_regs *) regs, error_code); return; } - if (current->flags & PF_PTRACED) - current->blocked &= ~(1 << (SIGTRAP-1)); - send_sig(SIGTRAP, current, 1); + force_sig(SIGTRAP, current); current->tss.trap_no = 1; current->tss.error_code = error_code; - if ((regs->cs & 3) == 0) { + if ((regs->xcs & 3) == 0) { /* If this is a kernel mode trap, then reset db7 and allow us to continue */ __asm__("movl %0,%%db7" : /* no output */ @@ -219,40 +223,35 @@ asmlinkage void do_debug(struct pt_regs * regs, long error_code) } /* - * Allow the process which triggered the interrupt to recover the error - * condition. - * - the status word is saved in the cs selector. - * - the tag word is saved in the operand selector. - * - the status word is then cleared and the tags all set to Empty. - * - * This will give sufficient information for complete recovery provided that - * the affected process knows or can deduce the code and data segments - * which were in force when the exception condition arose. - * * Note that we play around with the 'TS' bit to hopefully get * the correct behaviour even in the presence of the asynchronous * IRQ13 behaviour */ void math_error(void) { - struct i387_hard_struct * env; + struct task_struct * task; clts(); - if (!last_task_used_math) { +#ifdef __SMP__ + task = current; +#else + task = last_task_used_math; + last_task_used_math = NULL; + if (!task) { __asm__("fnclex"); return; } - env = &last_task_used_math->tss.i387.hard; - send_sig(SIGFPE, last_task_used_math, 1); - last_task_used_math->tss.trap_no = 16; - last_task_used_math->tss.error_code = 0; - __asm__ __volatile__("fnsave %0":"=m" (*env)); - last_task_used_math = NULL; +#endif + /* + * Save the info for the exception handler + */ + __asm__ __volatile__("fnsave %0":"=m" (task->tss.i387.hard)); + task->flags&=~PF_USEDFPU; stts(); - env->fcs = (env->swd & 0x0000ffff) | (env->fcs & 0xffff0000); - env->fos = env->twd; - env->swd &= 0xffff3800; - env->twd = 0xffffffff; + + force_sig(SIGFPE, task); + task->tss.trap_no = 16; + task->tss.error_code = 0; } asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) @@ -261,6 +260,12 @@ asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) math_error(); } +asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs, + long error_code) +{ + printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); +} + /* * 'math_state_restore()' saves the current math information in the * old math state array, and gets the new ones from the current task @@ -270,23 +275,37 @@ asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) */ asmlinkage void math_state_restore(void) { - __asm__ __volatile__("clts"); + __asm__ __volatile__("clts"); /* Allow maths ops (or we recurse) */ + +/* + * SMP is actually simpler than uniprocessor for once. Because + * we can't pull the delayed FPU switching trick Linus does + * we simply have to do the restore each context switch and + * set the flag. switch_to() will always save the state in + * case we swap processors. We also don't use the coprocessor + * timer - IRQ 13 mode isn't used with SMP machines (thank god). + */ +#ifndef __SMP__ if (last_task_used_math == current) return; - timer_table[COPRO_TIMER].expires = jiffies+50; - timer_active |= 1<<COPRO_TIMER; if (last_task_used_math) __asm__("fnsave %0":"=m" (last_task_used_math->tss.i387)); else __asm__("fnclex"); last_task_used_math = current; - if (current->used_math) { +#endif + + if(current->used_math) __asm__("frstor %0": :"m" (current->tss.i387)); - } else { + else + { + /* + * Our first FPU usage, clean the chip. + */ __asm__("fninit"); - current->used_math=1; + current->used_math = 1; } - timer_active &= ~(1<<COPRO_TIMER); + current->flags|=PF_USEDFPU; /* So we fnsave on switch_to() */ } #ifndef CONFIG_MATH_EMULATION @@ -295,7 +314,7 @@ asmlinkage void math_emulate(long arg) { printk("math-emulation not enabled and no coprocessor found.\n"); printk("killing %s.\n",current->comm); - send_sig(SIGFPE,current,1); + force_sig(SIGFPE,current); schedule(); } @@ -305,8 +324,16 @@ void trap_init(void) { int i; struct desc_struct * p; - - if (strncmp((char*)0x0FFFD9, "EISA", 4) == 0) + static int smptrap=0; + + if(smptrap) + { + __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); + load_ldt(0); + return; + } + smptrap++; + if (readl(0x0FFFD9) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24)) EISA_bus = 1; set_call_gate(&default_ldt,lcall7); set_trap_gate(0,÷_error); @@ -324,7 +351,7 @@ void trap_init(void) 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(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); for (i=18;i<48;i++) diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c index d55f8248f..6cceba36c 100644 --- a/arch/i386/kernel/vm86.c +++ b/arch/i386/kernel/vm86.c @@ -11,7 +11,7 @@ #include <linux/ptrace.h> #include <linux/mm.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/io.h> @@ -59,8 +59,12 @@ asmlinkage struct pt_regs * save_v86_state(struct vm86_regs * regs) do_exit(SIGSEGV); } set_flags(regs->eflags, VEFLAGS, VIF_MASK | current->tss.v86mask); - memcpy_tofs(¤t->tss.vm86_info->regs,regs,sizeof(*regs)); - put_fs_long(current->tss.screen_bitmap,¤t->tss.vm86_info->screen_bitmap); + tmp = copy_to_user(¤t->tss.vm86_info->regs,regs,sizeof(*regs)); + tmp += put_user(current->tss.screen_bitmap,¤t->tss.vm86_info->screen_bitmap); + if (tmp) { + printk("vm86: could not access userspace vm86_info\n"); + do_exit(SIGSEGV); + } tmp = current->tss.esp0; current->tss.esp0 = current->saved_kernel_stack; current->saved_kernel_stack = 0; @@ -74,7 +78,7 @@ static void mark_screen_rdonly(struct task_struct * tsk) pte_t *pte; int i; - pgd = pgd_offset(tsk, 0xA0000); + pgd = pgd_offset(tsk->mm, 0xA0000); if (pgd_none(*pgd)) return; if (pgd_bad(*pgd)) { @@ -93,32 +97,32 @@ static void mark_screen_rdonly(struct task_struct * tsk) pte = pte_offset(pmd, 0xA0000); for (i = 0; i < 32; i++) { if (pte_present(*pte)) - *pte = pte_wrprotect(*pte); + set_pte(pte, pte_wrprotect(*pte)); pte++; } - invalidate(); + flush_tlb(); } asmlinkage int sys_vm86(struct vm86_struct * v86) { struct vm86_struct info; + struct task_struct *tsk = current; struct pt_regs * pt_regs = (struct pt_regs *) &v86; - int error; - if (current->saved_kernel_stack) + if (tsk->saved_kernel_stack) return -EPERM; - /* v86 must be readable (now) and writable (for save_v86_state) */ - error = verify_area(VERIFY_WRITE,v86,sizeof(*v86)); - if (error) - return error; - memcpy_fromfs(&info,v86,sizeof(info)); + if (copy_from_user(&info,v86,sizeof(info))) + return -EFAULT; /* * make sure the vm86() system call doesn't try to do anything silly */ info.regs.__null_ds = 0; info.regs.__null_es = 0; - info.regs.__null_fs = 0; - info.regs.__null_gs = 0; + +/* we are clearing fs,gs later just before "jmp ret_from_sys_call", + * because starting with Linux 2.1.x they aren't no longer saved/restored + */ + /* * The eflags register is also special: we cannot trust that the user * has set it up safely, so this makes sure interrupt etc flags are @@ -131,16 +135,16 @@ asmlinkage int sys_vm86(struct vm86_struct * v86) switch (info.cpu_type) { case CPU_286: - current->tss.v86mask = 0; + tsk->tss.v86mask = 0; break; case CPU_386: - current->tss.v86mask = NT_MASK | IOPL_MASK; + tsk->tss.v86mask = NT_MASK | IOPL_MASK; break; case CPU_486: - current->tss.v86mask = AC_MASK | NT_MASK | IOPL_MASK; + tsk->tss.v86mask = AC_MASK | NT_MASK | IOPL_MASK; break; default: - current->tss.v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK; + tsk->tss.v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK; break; } @@ -148,17 +152,19 @@ asmlinkage int sys_vm86(struct vm86_struct * v86) * Save old state, set default return value (%eax) to 0 */ pt_regs->eax = 0; - current->saved_kernel_stack = current->tss.esp0; - current->tss.esp0 = (unsigned long) pt_regs; - current->tss.vm86_info = v86; + tsk->saved_kernel_stack = tsk->tss.esp0; + tsk->tss.esp0 = (unsigned long) pt_regs; + tsk->tss.vm86_info = v86; - current->tss.screen_bitmap = info.screen_bitmap; + tsk->tss.screen_bitmap = info.screen_bitmap; if (info.flags & VM86_SCREEN_BITMAP) - mark_screen_rdonly(current); - __asm__ __volatile__("movl %0,%%esp\n\t" + mark_screen_rdonly(tsk); + __asm__ __volatile__( + "xorl %%eax,%%eax; mov %%ax,%%fs; mov %%ax,%%gs\n\t" + "movl %0,%%esp\n\t" "jmp ret_from_sys_call" : /* no outputs */ - :"r" (&info.regs)); + :"r" (&info.regs), "b" (tsk) : "ax"); return 0; } @@ -170,7 +176,7 @@ static inline void return_to_32bit(struct vm86_regs * regs16, int retval) regs32->eax = retval; __asm__ __volatile__("movl %0,%%esp\n\t" "jmp ret_from_sys_call" - : : "r" (regs32)); + : : "r" (regs32), "b" (current)); } static inline void set_IF(struct vm86_regs * regs) @@ -217,10 +223,10 @@ static inline unsigned long get_vflags(struct vm86_regs * regs) static inline int is_revectored(int nr, struct revectored_struct * bitmap) { - __asm__ __volatile__("btl %2,%%fs:%1\n\tsbbl %0,%0" - :"=r" (nr) - :"m" (*bitmap),"r" (nr)); - return nr; + unsigned long map; + if (get_user(map, bitmap->__map + (nr >> 5))) + return 1; + return test_bit(nr & ((1 << 5)-1), &map); } /* @@ -231,16 +237,16 @@ static inline int is_revectored(int nr, struct revectored_struct * bitmap) #define pushb(base, ptr, val) \ __asm__ __volatile__( \ "decw %w0\n\t" \ - "movb %2,%%fs:0(%1,%0)" \ + "movb %2,0(%1,%0)" \ : "=r" (ptr) \ : "r" (base), "q" (val), "0" (ptr)) #define pushw(base, ptr, val) \ __asm__ __volatile__( \ "decw %w0\n\t" \ - "movb %h2,%%fs:0(%1,%0)\n\t" \ + "movb %h2,0(%1,%0)\n\t" \ "decw %w0\n\t" \ - "movb %b2,%%fs:0(%1,%0)" \ + "movb %b2,0(%1,%0)" \ : "=r" (ptr) \ : "r" (base), "q" (val), "0" (ptr)) @@ -248,21 +254,21 @@ __asm__ __volatile__( \ __asm__ __volatile__( \ "decw %w0\n\t" \ "rorl $16,%2\n\t" \ - "movb %h2,%%fs:0(%1,%0)\n\t" \ + "movb %h2,0(%1,%0)\n\t" \ "decw %w0\n\t" \ - "movb %b2,%%fs:0(%1,%0)\n\t" \ + "movb %b2,0(%1,%0)\n\t" \ "decw %w0\n\t" \ "rorl $16,%2\n\t" \ - "movb %h2,%%fs:0(%1,%0)\n\t" \ + "movb %h2,0(%1,%0)\n\t" \ "decw %w0\n\t" \ - "movb %b2,%%fs:0(%1,%0)" \ + "movb %b2,0(%1,%0)" \ : "=r" (ptr) \ : "r" (base), "q" (val), "0" (ptr)) #define popb(base, ptr) \ ({ unsigned long __res; \ __asm__ __volatile__( \ - "movb %%fs:0(%1,%0),%b2\n\t" \ + "movb 0(%1,%0),%b2\n\t" \ "incw %w0" \ : "=r" (ptr), "=r" (base), "=q" (__res) \ : "0" (ptr), "1" (base), "2" (0)); \ @@ -271,9 +277,9 @@ __res; }) #define popw(base, ptr) \ ({ unsigned long __res; \ __asm__ __volatile__( \ - "movb %%fs:0(%1,%0),%b2\n\t" \ + "movb 0(%1,%0),%b2\n\t" \ "incw %w0\n\t" \ - "movb %%fs:0(%1,%0),%h2\n\t" \ + "movb 0(%1,%0),%h2\n\t" \ "incw %w0" \ : "=r" (ptr), "=r" (base), "=q" (__res) \ : "0" (ptr), "1" (base), "2" (0)); \ @@ -282,14 +288,14 @@ __res; }) #define popl(base, ptr) \ ({ unsigned long __res; \ __asm__ __volatile__( \ - "movb %%fs:0(%1,%0),%b2\n\t" \ + "movb 0(%1,%0),%b2\n\t" \ "incw %w0\n\t" \ - "movb %%fs:0(%1,%0),%h2\n\t" \ + "movb 0(%1,%0),%h2\n\t" \ "incw %w0\n\t" \ "rorl $16,%2\n\t" \ - "movb %%fs:0(%1,%0),%b2\n\t" \ + "movb 0(%1,%0),%b2\n\t" \ "incw %w0\n\t" \ - "movb %%fs:0(%1,%0),%h2\n\t" \ + "movb 0(%1,%0),%h2\n\t" \ "incw %w0\n\t" \ "rorl $16,%2" \ : "=r" (ptr), "=r" (base), "=q" (__res) \ @@ -298,22 +304,31 @@ __res; }) static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned long sp) { - unsigned short seg = get_fs_word((void *) ((i<<2)+2)); - - if (seg == BIOSSEG || regs->cs == BIOSSEG || - is_revectored(i, ¤t->tss.vm86_info->int_revectored)) - return_to_32bit(regs, VM86_INTx + (i << 8)); + unsigned long *intr_ptr, segoffs; + + if (regs->cs == BIOSSEG) + goto cannot_handle; + if (is_revectored(i, ¤t->tss.vm86_info->int_revectored)) + goto cannot_handle; if (i==0x21 && is_revectored(AH(regs),¤t->tss.vm86_info->int21_revectored)) - return_to_32bit(regs, VM86_INTx + (i << 8)); + goto cannot_handle; + intr_ptr = (unsigned long *) (i << 2); + if (get_user(segoffs, intr_ptr)) + goto cannot_handle; + if ((segoffs >> 16) == BIOSSEG) + goto cannot_handle; pushw(ssp, sp, get_vflags(regs)); pushw(ssp, sp, regs->cs); pushw(ssp, sp, IP(regs)); - regs->cs = seg; + regs->cs = segoffs >> 16; SP(regs) -= 6; - IP(regs) = get_fs_word((void *) (i<<2)); + IP(regs) = segoffs & 0xffff; clear_TF(regs); clear_IF(regs); return; + +cannot_handle: + return_to_32bit(regs, VM86_INTx + (i << 8)); } void handle_vm86_debug(struct vm86_regs * regs, long error_code) @@ -367,6 +382,7 @@ void handle_vm86_fault(struct vm86_regs * regs, long error_code) set_vflags_long(popl(ssp, sp), regs); return; } + break; /* pushf */ case 0x9c: @@ -413,8 +429,10 @@ void handle_vm86_fault(struct vm86_regs * regs, long error_code) IP(regs)++; set_IF(regs); return; - - default: - return_to_32bit(regs, VM86_UNKNOWN); } + + /* + * We didn't recognize it, let the emulator take care of it.. + */ + return_to_32bit(regs, VM86_UNKNOWN); } diff --git a/arch/i386/lib/Makefile b/arch/i386/lib/Makefile new file mode 100644 index 000000000..cf54dd7df --- /dev/null +++ b/arch/i386/lib/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for i386-specific library files.. +# + +ifdef SMP +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o +else +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o +endif + +L_TARGET = lib.a +L_OBJS = checksum.o semaphore.o + +include $(TOPDIR)/Rules.make diff --git a/arch/i386/lib/checksum.c b/arch/i386/lib/checksum.c new file mode 100644 index 000000000..d2bf48b08 --- /dev/null +++ b/arch/i386/lib/checksum.c @@ -0,0 +1,194 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, <jorge@laser.satlink.net> + * Arnt Gulbrandsen, <agulbra@nvg.unit.no> + * Tom May, <ftom@netcom.com> + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <net/checksum.h> + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ + +unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) { + /* + * Experiments with ethernet and slip connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. We get at + * least a 2x speedup on 486 and Pentium if it is 4-byte aligned. + * Fortunately, it is easy to convert 2-byte alignment to 4-byte + * alignment for the unrolled loop. + */ + __asm__(" + testl $2, %%esi # Check alignment. + jz 2f # Jump if alignment is ok. + subl $2, %%ecx # Alignment uses up two bytes. + jae 1f # Jump if we had at least two bytes. + addl $2, %%ecx # ecx was < 2. Deal with it. + jmp 4f +1: movw (%%esi), %%bx + addl $2, %%esi + addw %%bx, %%ax + adcl $0, %%eax +2: + movl %%ecx, %%edx + shrl $5, %%ecx + jz 2f + testl %%esi, %%esi +1: movl (%%esi), %%ebx + adcl %%ebx, %%eax + movl 4(%%esi), %%ebx + adcl %%ebx, %%eax + movl 8(%%esi), %%ebx + adcl %%ebx, %%eax + movl 12(%%esi), %%ebx + adcl %%ebx, %%eax + movl 16(%%esi), %%ebx + adcl %%ebx, %%eax + movl 20(%%esi), %%ebx + adcl %%ebx, %%eax + movl 24(%%esi), %%ebx + adcl %%ebx, %%eax + movl 28(%%esi), %%ebx + adcl %%ebx, %%eax + lea 32(%%esi), %%esi + dec %%ecx + jne 1b + adcl $0, %%eax +2: movl %%edx, %%ecx + andl $0x1c, %%edx + je 4f + shrl $2, %%edx + testl %%esi, %%esi +3: adcl (%%esi), %%eax + lea 4(%%esi), %%esi + dec %%edx + jne 3b + adcl $0, %%eax +4: andl $3, %%ecx + jz 7f + cmpl $2, %%ecx + jb 5f + movw (%%esi),%%cx + leal 2(%%esi),%%esi + je 6f + shll $16,%%ecx +5: movb (%%esi),%%cl +6: addl %%ecx,%%eax + adcl $0, %%eax +7: " + : "=a"(sum) + : "0"(sum), "c"(len), "S"(buff) + : "bx", "cx", "dx", "si"); + return(sum); +} + + + +/* + * copy from ds while checksumming, otherwise like csum_partial + */ + +unsigned int csum_partial_copy(const char *src, char *dst, + int len, int sum) { + __asm__(" + testl $2, %%edi # Check alignment. + jz 2f # Jump if alignment is ok. + subl $2, %%ecx # Alignment uses up two bytes. + jae 1f # Jump if we had at least two bytes. + addl $2, %%ecx # ecx was < 2. Deal with it. + jmp 4f +1: movw (%%esi), %%bx + addl $2, %%esi + movw %%bx, (%%edi) + addl $2, %%edi + addw %%bx, %%ax + adcl $0, %%eax +2: + movl %%ecx, %%edx + shrl $5, %%ecx + jz 2f + testl %%esi, %%esi +1: movl (%%esi), %%ebx + adcl %%ebx, %%eax + movl %%ebx, (%%edi) + + movl 4(%%esi), %%ebx + adcl %%ebx, %%eax + movl %%ebx, 4(%%edi) + + movl 8(%%esi), %%ebx + adcl %%ebx, %%eax + movl %%ebx, 8(%%edi) + + movl 12(%%esi), %%ebx + adcl %%ebx, %%eax + movl %%ebx, 12(%%edi) + + movl 16(%%esi), %%ebx + adcl %%ebx, %%eax + movl %%ebx, 16(%%edi) + + movl 20(%%esi), %%ebx + adcl %%ebx, %%eax + movl %%ebx, 20(%%edi) + + movl 24(%%esi), %%ebx + adcl %%ebx, %%eax + movl %%ebx, 24(%%edi) + + movl 28(%%esi), %%ebx + adcl %%ebx, %%eax + movl %%ebx, 28(%%edi) + + lea 32(%%esi), %%esi + lea 32(%%edi), %%edi + dec %%ecx + jne 1b + adcl $0, %%eax +2: movl %%edx, %%ecx + andl $28, %%edx + je 4f + shrl $2, %%edx + testl %%esi, %%esi +3: movl (%%esi), %%ebx + adcl %%ebx, %%eax + movl %%ebx, (%%edi) + lea 4(%%esi), %%esi + lea 4(%%edi), %%edi + dec %%edx + jne 3b + adcl $0, %%eax +4: andl $3, %%ecx + jz 7f + cmpl $2, %%ecx + jb 5f + movw (%%esi), %%cx + leal 2(%%esi), %%esi + movw %%cx, (%%edi) + leal 2(%%edi), %%edi + je 6f + shll $16,%%ecx +5: movb (%%esi), %%cl + movb %%cl, (%%edi) +6: addl %%ecx, %%eax + adcl $0, %%eax +7: + " + : "=a" (sum) + : "0"(sum), "c"(len), "S"(src), "D" (dst) + : "bx", "cx", "dx", "si", "di" ); + return(sum); +} diff --git a/arch/i386/lib/semaphore.S b/arch/i386/lib/semaphore.S new file mode 100644 index 000000000..f2d11099f --- /dev/null +++ b/arch/i386/lib/semaphore.S @@ -0,0 +1,35 @@ +/* + * linux/arch/i386/lib/semaphore.S + * + * Copyright (C) 1996 Linus Torvalds + */ + +#include <linux/linkage.h> + +/* + * "down_failed" is called with the eventual return address + * in %eax, and the address of the semaphore in %ecx. We need + * to call "__down()", and then re-try until we succeed.. + */ +ENTRY(__down_failed) + pushl %eax /* return address */ + pushl %edx /* save %edx */ +1: pushl %ecx /* save %ecx (and argument) */ + call SYMBOL_NAME(__down) + popl %ecx /* restore %ecx (count on __down not changing it) */ +#ifdef __SMP__ + lock +#endif + decl (%ecx) + js 1b + popl %edx /* restore %edx */ + ret + +ENTRY(__up_wakeup) + pushl %eax /* return address */ + pushl %edx /* save %edx */ + pushl %ecx /* save %ecx (and argument) */ + call SYMBOL_NAME(__up) + popl %ecx /* restore %ecx (count on __up not changing it) */ + popl %edx /* restore %edx */ + ret diff --git a/arch/i386/math-emu/Makefile b/arch/i386/math-emu/Makefile index 2d391a9e6..1bd2cb40d 100644 --- a/arch/i386/math-emu/Makefile +++ b/arch/i386/math-emu/Makefile @@ -2,21 +2,17 @@ # Makefile for wm-FPU-emu # +L_TARGET := math.a + #DEBUG = -DDEBUGGING DEBUG = PARANOID = -DPARANOID -CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin - -.c.o: - $(CC) $(CFLAGS) $(MATH_EMULATION) -c $< +CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin $(MATH_EMULATION) .S.o: - $(CC) -D__ASSEMBLER__ $(PARANOID) -c $< - -.s.o: - $(CC) -c $< + $(CC) -D__ASSEMBLY__ $(PARANOID) -c $< -OBJS = fpu_entry.o div_small.o errors.o \ +L_OBJS =fpu_entry.o div_small.o errors.o \ fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \ load_store.o get_address.o \ poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \ @@ -28,25 +24,7 @@ OBJS = fpu_entry.o div_small.o errors.o \ div_Xsig.o polynom_Xsig.o round_Xsig.o \ shr_Xsig.o mul_Xsig.o -math.a: $(OBJS) - rm -f math.a - $(AR) rcs math.a $(OBJS) - sync - -dep: - $(CPP) -M *.c > .depend - $(CPP) -D__ASSEMBLER__ -M *.S >> .depend +include $(TOPDIR)/Rules.make proto: cproto -e -DMAKING_PROTO *.c >fpu_proto.h - -modules: - -dummy: - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif diff --git a/arch/i386/math-emu/README b/arch/i386/math-emu/README index 2c0acb423..5158b4b81 100644 --- a/arch/i386/math-emu/README +++ b/arch/i386/math-emu/README @@ -1,9 +1,9 @@ +---------------------------------------------------------------------------+ | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1995,1996 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | This program is free software; you can redistribute it and/or modify | | it under the terms of the GNU General Public License version 2 as | @@ -42,13 +42,11 @@ but is very close. See "Limitations" later in this file for a list of some differences. Please report bugs, etc to me at: - billm@vaxc.cc.monash.edu.au - or at: - billm@jacobi.maths.monash.edu.au + billm@suburbia.net --Bill Metzenthen - August 1994 + October 1996 ----------------------- Internals of wm-FPU-emu ----------------------- diff --git a/arch/i386/math-emu/control_w.h b/arch/i386/math-emu/control_w.h index ef5fced39..6bd7654b7 100644 --- a/arch/i386/math-emu/control_w.h +++ b/arch/i386/math-emu/control_w.h @@ -10,7 +10,7 @@ #ifndef _CONTROLW_H_ #define _CONTROLW_H_ -#ifdef __ASSEMBLER__ +#ifdef __ASSEMBLY__ #define _Const_(x) $##x #else #define _Const_(x) x diff --git a/arch/i386/math-emu/div_Xsig.S b/arch/i386/math-emu/div_Xsig.S index 67d8be964..fd83732fc 100644 --- a/arch/i386/math-emu/div_Xsig.S +++ b/arch/i386/math-emu/div_Xsig.S @@ -4,9 +4,9 @@ | | | Division subroutine for 96 bit quantities | | | - | Copyright (C) 1994 | + | Copyright (C) 1994,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ @@ -28,7 +28,7 @@ +---------------------------------------------------------------------------*/ #include "exception.h" -#include "fpu_asm.h" +#include "fpu_emu.h" #define XsigLL(x) (x) @@ -74,11 +74,7 @@ FPU_result_1: .text - .align 2,144 - -.globl _div_Xsig - -_div_Xsig: +ENTRY(div_Xsig) pushl %ebp movl %esp,%ebp #ifndef NON_REENTRANT_FPU diff --git a/arch/i386/math-emu/div_small.S b/arch/i386/math-emu/div_small.S index 0225a96d4..13ab2b7ae 100644 --- a/arch/i386/math-emu/div_small.S +++ b/arch/i386/math-emu/div_small.S @@ -4,8 +4,9 @@ | | | Divide a 64 bit integer by a 32 bit integer & return remainder. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1995 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ @@ -14,14 +15,10 @@ | unsigned long div_small(unsigned long long *x, unsigned long y) | +---------------------------------------------------------------------------*/ -#include "fpu_asm.h" +#include "fpu_emu.h" .text - .align 2,144 - -.globl _div_small - -_div_small: +ENTRY(div_small) pushl %ebp movl %esp,%ebp diff --git a/arch/i386/math-emu/errors.c b/arch/i386/math-emu/errors.c index e34eec942..f76020d1f 100644 --- a/arch/i386/math-emu/errors.c +++ b/arch/i386/math-emu/errors.c @@ -3,9 +3,9 @@ | | | The error handling functions for wm-FPU-emu | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1996 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@jacobi.maths.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ @@ -19,7 +19,7 @@ #include <linux/signal.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include "fpu_system.h" #include "exception.h" @@ -46,13 +46,13 @@ void Un_impl(void) { while ( 1 ) { - byte1 = get_fs_byte((unsigned char *) address); + get_user(byte1, (unsigned char *) address); if ( (byte1 & 0xf8) == 0xd8 ) break; printk("[%02x]", byte1); address++; } printk("%02x ", byte1); - FPU_modrm = get_fs_byte(1 + (unsigned char *) address); + get_user(FPU_modrm, 1 + (unsigned char *) address); if (FPU_modrm >= 0300) printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); @@ -82,10 +82,10 @@ void FPU_illegal(void) -void emu_printall() +void emu_printall(void) { int i; - static char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR", + static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR", "DeNorm", "Inf", "NaN", "Empty" }; unsigned char byte1, FPU_modrm; unsigned long address = FPU_ORIG_EIP; @@ -98,7 +98,7 @@ void emu_printall() #define MAX_PRINTED_BYTES 20 for ( i = 0; i < MAX_PRINTED_BYTES; i++ ) { - byte1 = get_fs_byte((unsigned char *) address); + get_user(byte1, (unsigned char *) address); if ( (byte1 & 0xf8) == 0xd8 ) { printk(" %02x", byte1); @@ -111,7 +111,7 @@ void emu_printall() printk(" [more..]\n"); else { - FPU_modrm = get_fs_byte(1 + (unsigned char *) address); + get_user(FPU_modrm, 1 + (unsigned char *) address); if (FPU_modrm >= 0300) printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); @@ -166,7 +166,8 @@ printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n", for ( i = 0; i < 8; i++ ) { FPU_REG *r = &st(i); - switch (r->tag) + char tagi = r->tag; + switch (tagi) { case TW_Empty: continue; @@ -190,29 +191,20 @@ printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n", r->exp - EXP_BIAS + 1); break; default: - printk("Whoops! Error in errors.c "); + printk("Whoops! Error in errors.c: tag%d is %d ", i, tagi); + continue; break; } - printk("%s\n", tag_desc[(int) (unsigned) r->tag]); + printk("%s\n", tag_desc[(int) (unsigned) tagi]); } -#ifdef OBSOLETE - printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ", - FPU_loaded_data.sign ? '-' : '+', - (long)(FPU_loaded_data.sigh >> 16), - (long)(FPU_loaded_data.sigh & 0xFFFF), - (long)(FPU_loaded_data.sigl >> 16), - (long)(FPU_loaded_data.sigl & 0xFFFF), - FPU_loaded_data.exp - EXP_BIAS + 1); - printk("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]); -#endif OBSOLETE RE_ENTRANT_CHECK_ON; } static struct { int type; - char *name; + const char *name; } exception_names[] = { { EX_StackOver, "stack overflow" }, { EX_StackUnder, "stack underflow" }, @@ -300,7 +292,7 @@ static struct { 0x242 in div_Xsig.S */ -void exception(int n) +void FPU_exception(int n) { int i, int_type; @@ -365,12 +357,8 @@ void exception(int n) /* * The 80486 generates an interrupt on the next non-control FPU * instruction. So we need some means of flagging it. - * We use the ES (Error Summary) bit for this, assuming that - * this is the way a real FPU does it (until I can check it out), - * if not, then some method such as the following kludge might - * be needed. + * We use the ES (Error Summary) bit for this. */ -/* regs[0].tag |= TW_FPU_Interrupt; */ } RE_ENTRANT_CHECK_ON; @@ -494,7 +482,7 @@ int set_precision_flag(int flags) } else { - exception(flags); + EXCEPTION(flags); return 1; } } @@ -506,7 +494,7 @@ asmlinkage void set_precision_flag_up(void) if ( control_word & CW_Precision ) partial_status |= (SW_Precision | SW_C1); /* The masked response */ else - exception(EX_Precision | SW_C1); + EXCEPTION(EX_Precision | SW_C1); } @@ -520,7 +508,7 @@ asmlinkage void set_precision_flag_down(void) partial_status |= SW_Precision; } else - exception(EX_Precision); + EXCEPTION(EX_Precision); } @@ -533,7 +521,7 @@ asmlinkage int denormal_operand(void) } else { - exception(EX_Denormal); + EXCEPTION(EX_Denormal); return 1; } } @@ -568,7 +556,7 @@ asmlinkage int arith_overflow(FPU_REG *dest) return !(control_word & CW_Precision); } - return !(control_word & CW_Overflow); + return 0; } @@ -599,7 +587,7 @@ asmlinkage int arith_underflow(FPU_REG *dest) return !(control_word & CW_Precision); } - return !(control_word & CW_Underflow); + return 0; } diff --git a/arch/i386/math-emu/exception.h b/arch/i386/math-emu/exception.h index 2e629a30c..b90e5e9c3 100644 --- a/arch/i386/math-emu/exception.h +++ b/arch/i386/math-emu/exception.h @@ -10,7 +10,7 @@ #define _EXCEPTION_H_ -#ifdef __ASSEMBLER__ +#ifdef __ASSEMBLY__ #define Const_(x) $##x #else #define Const_(x) x @@ -39,15 +39,15 @@ #define PRECISION_LOST_DOWN Const_(EX_Precision) -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ #ifdef DEBUG #define EXCEPTION(x) { printk("exception in %s at line %d\n", \ - __FILE__, __LINE__); exception(x); } + __FILE__, __LINE__); FPU_exception(x); } #else -#define EXCEPTION(x) exception(x) +#define EXCEPTION(x) FPU_exception(x) #endif -#endif __ASSEMBLER__ +#endif __ASSEMBLY__ #endif _EXCEPTION_H_ diff --git a/arch/i386/math-emu/fpu_asm.h b/arch/i386/math-emu/fpu_asm.h index 8eb60148d..d08fbc874 100644 --- a/arch/i386/math-emu/fpu_asm.h +++ b/arch/i386/math-emu/fpu_asm.h @@ -1,17 +1,18 @@ /*---------------------------------------------------------------------------+ | fpu_asm.h | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1995 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | +---------------------------------------------------------------------------*/ #ifndef _FPU_ASM_H_ #define _FPU_ASM_H_ -#include "fpu_emu.h" +#include <linux/linkage.h> -#define EXCEPTION _exception +#define EXCEPTION SYMBOL_NAME(FPU_exception) #define PARAM1 8(%ebp) diff --git a/arch/i386/math-emu/fpu_emu.h b/arch/i386/math-emu/fpu_emu.h index 9d2c5dd13..b8385db1f 100644 --- a/arch/i386/math-emu/fpu_emu.h +++ b/arch/i386/math-emu/fpu_emu.h @@ -28,7 +28,7 @@ */ #define PECULIAR_486 -#ifdef __ASSEMBLER__ +#ifdef __ASSEMBLY__ #include "fpu_asm.h" #define Const(x) $##x #else @@ -55,9 +55,11 @@ #define TW_Empty Const(7) /* empty */ -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ + +#include <asm/sigcontext.h> /* for struct _fpstate */ +#include <asm/math_emu.h> -#include <linux/math_emu.h> #include <linux/linkage.h> /* @@ -95,9 +97,9 @@ extern char emulating; struct address { unsigned int offset; - unsigned int selector:16; - unsigned int opcode:11; - unsigned int empty:5; + unsigned short selector; + unsigned short opcode:11, + empty:5; }; typedef void (*FUNC)(void); typedef struct fpu_reg FPU_REG; @@ -133,9 +135,9 @@ extern unsigned char const data_sizes_16[32]; #define reg_move(x, y) { \ - *(short *)&((y)->sign) = *(short *)&((x)->sign); \ - *(long *)&((y)->exp) = *(long *)&((x)->exp); \ - *(long long *)&((y)->sigl) = *(long long *)&((x)->sigl); } + *(short *)&((y)->sign) = *(const short *)&((x)->sign); \ + *(long *)&((y)->exp) = *(const long *)&((x)->exp); \ + *(long long *)&((y)->sigl) = *(const long long *)&((x)->sigl); } #define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] ) @@ -166,6 +168,6 @@ asmlinkage void round_reg(FPU_REG *arg, unsigned int extent, #include "fpu_proto.h" #endif -#endif __ASSEMBLER__ +#endif __ASSEMBLY__ #endif _FPU_EMU_H_ diff --git a/arch/i386/math-emu/fpu_entry.c b/arch/i386/math-emu/fpu_entry.c index b2777a722..07c32db9a 100644 --- a/arch/i386/math-emu/fpu_entry.c +++ b/arch/i386/math-emu/fpu_entry.c @@ -1,11 +1,11 @@ /*---------------------------------------------------------------------------+ | fpu_entry.c | | | - | The entry function for wm-FPU-emu | + | The entry functions for wm-FPU-emu | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1996 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@jacobi.maths.monash.edu.au | | | | See the files "README" and "COPYING" for further copyright and warranty | | information. | @@ -20,12 +20,13 @@ +---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------+ - | math_emulate() is the sole entry point for wm-FPU-emu | + | math_emulate(), restore_i387_soft() and save_i387_soft() are the only | + | entry points for wm-FPU-emu. | +---------------------------------------------------------------------------*/ #include <linux/signal.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include "fpu_system.h" #include "fpu_emu.h" @@ -159,8 +160,9 @@ asmlinkage void math_emulate(long arg) { /* Make sure that the registers are compatible with the assumptions of the emulator. */ - regs[i].exp = 0; - regs[i].sigh = 0x80000000; + if ( !((regs[i].exp == EXP_UNDER) && (regs[i].sigh == 0) + && (regs[i].sigl == 0)) ) + regs[i].sigh |= 0x80000000; } finit(); current->used_math = 1; @@ -262,7 +264,7 @@ do_another_FPU_instruction: RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - FPU_modrm = get_fs_byte((unsigned char *) FPU_EIP); + get_user(FPU_modrm, (unsigned char *) FPU_EIP); RE_ENTRANT_CHECK_ON; FPU_EIP++; @@ -283,24 +285,8 @@ do_another_FPU_instruction: /* * We need to simulate the action of the kernel to FPU * interrupts here. - * Currently, the "real FPU" part of the kernel (0.99.10) - * clears the exception flags, sets the registers to empty, - * and passes information back to the interrupted process - * via the cs selector and operand selector, so we do the same. */ do_the_FPU_interrupt: - instruction_address.selector = status_word(); - operand_address.selector = tag_word(); - partial_status = 0; - top = 0; - { - int r; - for (r = 0; r < 8; r++) - { - regs[r].tag = TW_Empty; - } - } - FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */ RE_ENTRANT_CHECK_OFF; @@ -605,7 +591,7 @@ static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - byte = get_fs_byte(ip); + get_user(byte, ip); RE_ENTRANT_CHECK_ON; while ( 1 ) @@ -651,7 +637,7 @@ static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, ip++; RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - byte = get_fs_byte(ip); + get_user(byte, ip); RE_ENTRANT_CHECK_ON; break; case FWAIT_OPCODE: @@ -688,3 +674,22 @@ void math_abort(struct info * info, unsigned int signal) printk("ERROR: wm-FPU-emu math_abort failed!\n"); #endif PARANOID } + + + +void restore_i387_soft(struct _fpstate *buf) +{ + fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0}; + + frstor(addr_modes, (char *)buf); +} + + +struct _fpstate * save_i387_soft(struct _fpstate * buf) +{ + fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0}; + + fsave(addr_modes, (char *)buf); + + return buf; +} diff --git a/arch/i386/math-emu/fpu_proto.h b/arch/i386/math-emu/fpu_proto.h index b4392fe57..d0e58ed0e 100644 --- a/arch/i386/math-emu/fpu_proto.h +++ b/arch/i386/math-emu/fpu_proto.h @@ -7,7 +7,7 @@ extern void stack_underflow(void); extern void stack_underflow_i(int i); extern void stack_underflow_pop(int i); extern int set_precision_flag(int flags); -asmlinkage void exception(int n); +asmlinkage void FPU_exception(int n); asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest); asmlinkage int arith_invalid(FPU_REG *dest); asmlinkage int divide_by_zero(int sign, FPU_REG *dest); diff --git a/arch/i386/math-emu/get_address.c b/arch/i386/math-emu/get_address.c index 6f3270ae3..0749cdc70 100644 --- a/arch/i386/math-emu/get_address.c +++ b/arch/i386/math-emu/get_address.c @@ -21,7 +21,7 @@ #include <linux/stddef.h> #include <linux/head.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include "fpu_system.h" #include "exception.h" @@ -56,12 +56,16 @@ static int reg_offset_vm86[] = { #define VM86_REG_(x) (*(unsigned short *) \ (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info)) +/* These are dummy, fs and gs are not saved on the stack. */ +#define ___FS ___ds +#define ___GS ___ds + static int reg_offset_pm[] = { offsetof(struct info,___cs), offsetof(struct info,___ds), offsetof(struct info,___es), - offsetof(struct info,___fs), - offsetof(struct info,___gs), + offsetof(struct info,___FS), + offsetof(struct info,___GS), offsetof(struct info,___ss), offsetof(struct info,___ds) }; @@ -78,7 +82,7 @@ static int sib(int mod, unsigned long *fpu_eip) RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - base = get_fs_byte((char *) (*fpu_eip)); /* The SIB byte */ + get_user(base, (unsigned char *) (*fpu_eip)); /* The SIB byte */ RE_ENTRANT_CHECK_ON; (*fpu_eip)++; ss = base >> 6; @@ -105,18 +109,22 @@ static int sib(int mod, unsigned long *fpu_eip) if (mod == 1) { /* 8 bit signed displacement */ + long displacement; RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - offset += (signed char) get_fs_byte((char *) (*fpu_eip)); + get_user(displacement, (signed char *) (*fpu_eip)); + offset += displacement; RE_ENTRANT_CHECK_ON; (*fpu_eip)++; } else if (mod == 2 || base == 5) /* The second condition also has mod==0 */ { /* 32 bit displacement */ + long displacement; RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(4); - offset += (signed) get_fs_long((unsigned long *) (*fpu_eip)); + get_user(displacement, (signed long *) (*fpu_eip)); + offset += displacement; RE_ENTRANT_CHECK_ON; (*fpu_eip) += 4; } @@ -149,7 +157,9 @@ static long pm_address(unsigned char FPU_modrm, unsigned char segment, unsigned long base_address, limit, address, seg_top; segment--; + #ifdef PARANOID + /* segment is unsigned, so this also detects if segment was 0: */ if ( segment > PREFIX_SS_ ) { EXCEPTION(EX_INTERNAL|0x132); @@ -157,7 +167,19 @@ static long pm_address(unsigned char FPU_modrm, unsigned char segment, } #endif PARANOID - *selector = PM_REG_(segment); + switch ( segment ) + { + /* fs and gs aren't used by the kernel, so they still have their + user-space values. */ + case PREFIX_FS_-1: + __asm__("mov %%fs,%0":"=r" (*selector)); + break; + case PREFIX_GS_-1: + __asm__("mov %%gs,%0":"=r" (*selector)); + break; + default: + *selector = PM_REG_(segment); + } descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); base_address = SEG_BASE_ADDR(descriptor); @@ -248,7 +270,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, /* Special case: disp32 */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(4); - address = get_fs_long((unsigned long *) (*fpu_eip)); + get_user(address, (unsigned long *) (*fpu_eip)); (*fpu_eip) += 4; RE_ENTRANT_CHECK_ON; addr->offset = address; @@ -265,7 +287,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, /* 8 bit signed displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - address = (signed char) get_fs_byte((char *) (*fpu_eip)); + get_user(address, (signed char *) (*fpu_eip)); RE_ENTRANT_CHECK_ON; (*fpu_eip)++; break; @@ -273,7 +295,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, /* 32 bit displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(4); - address = (signed) get_fs_long((unsigned long *) (*fpu_eip)); + get_user(address, (long *) (*fpu_eip)); (*fpu_eip) += 4; RE_ENTRANT_CHECK_ON; break; @@ -336,7 +358,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, /* Special case: disp16 */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(2); - address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip)); + get_user(address, (unsigned short *) (*fpu_eip)); (*fpu_eip) += 2; RE_ENTRANT_CHECK_ON; goto add_segment; @@ -346,7 +368,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, /* 8 bit signed displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - address = (signed char) get_fs_byte((signed char *) (*fpu_eip)); + get_user(address, (signed char *) (*fpu_eip)); RE_ENTRANT_CHECK_ON; (*fpu_eip)++; break; @@ -354,7 +376,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, /* 16 bit displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(2); - address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip)); + get_user(address, (unsigned short *) (*fpu_eip)); (*fpu_eip) += 2; RE_ENTRANT_CHECK_ON; break; diff --git a/arch/i386/math-emu/load_store.c b/arch/i386/math-emu/load_store.c index 6f0e167d6..cc288a9ab 100644 --- a/arch/i386/math-emu/load_store.c +++ b/arch/i386/math-emu/load_store.c @@ -18,7 +18,7 @@ | other processes using the emulator while swapping is in progress. | +---------------------------------------------------------------------------*/ -#include <asm/segment.h> +#include <asm/uaccess.h> #include "fpu_system.h" #include "exception.h" @@ -201,7 +201,7 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, case 024: /* fldcw */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, data_address, 2); - control_word = get_fs_word((unsigned short *) data_address); + get_user(control_word, (unsigned short *) data_address); RE_ENTRANT_CHECK_ON; if ( partial_status & ~control_word & CW_Exceptions ) partial_status |= (SW_Summary | SW_Backward); @@ -234,7 +234,7 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, case 034: /* fstcw m16int */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,data_address,2); - put_fs_word(control_word, (short *) data_address); + put_user(control_word, (unsigned short *) data_address); RE_ENTRANT_CHECK_ON; return 1; case 035: /* fstp m80real */ @@ -246,7 +246,7 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, case 036: /* fstsw m2byte */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,data_address,2); - put_fs_word(status_word(),(short *) data_address); + put_user(status_word(),(unsigned short *) data_address); RE_ENTRANT_CHECK_ON; return 1; case 037: /* fistp m64int */ diff --git a/arch/i386/math-emu/mul_Xsig.S b/arch/i386/math-emu/mul_Xsig.S index 1d88d4466..717785a53 100644 --- a/arch/i386/math-emu/mul_Xsig.S +++ b/arch/i386/math-emu/mul_Xsig.S @@ -3,9 +3,9 @@ | | | Multiply a 12 byte fixed point number by another fixed point number. | | | - | Copyright (C) 1992,1994 | + | Copyright (C) 1992,1994,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Call from C as: | | void mul32_Xsig(Xsig *x, unsigned b) | @@ -21,12 +21,10 @@ .file "mul_Xsig.S" -#include "fpu_asm.h" +#include "fpu_emu.h" .text - .align 2,144 -.globl _mul32_Xsig -_mul32_Xsig: +ENTRY(mul32_Xsig) pushl %ebp movl %esp,%ebp subl $16,%esp @@ -66,9 +64,7 @@ _mul32_Xsig: ret - .align 2,144 -.globl _mul64_Xsig -_mul64_Xsig: +ENTRY(mul64_Xsig) pushl %ebp movl %esp,%ebp subl $16,%esp @@ -121,9 +117,7 @@ _mul64_Xsig: - .align 2,144 -.globl _mul_Xsig_Xsig -_mul_Xsig_Xsig: +ENTRY(mul_Xsig_Xsig) pushl %ebp movl %esp,%ebp subl $16,%esp diff --git a/arch/i386/math-emu/polynom_Xsig.S b/arch/i386/math-emu/polynom_Xsig.S index 585221f96..17315c89f 100644 --- a/arch/i386/math-emu/polynom_Xsig.S +++ b/arch/i386/math-emu/polynom_Xsig.S @@ -3,9 +3,9 @@ | | | Fixed point arithmetic polynomial evaluation. | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Call from C as: | | void polynomial_Xsig(Xsig *accum, unsigned long long x, | @@ -23,7 +23,7 @@ +---------------------------------------------------------------------------*/ .file "polynomial_Xsig.S" -#include "fpu_asm.h" +#include "fpu_emu.h" #define TERM_SIZE $8 @@ -36,9 +36,7 @@ #define OVERFLOWED -16(%ebp) /* addition overflow flag */ .text - .align 2,144 -.globl _polynomial_Xsig -_polynomial_Xsig: +ENTRY(polynomial_Xsig) pushl %ebp movl %esp,%ebp subl $32,%esp diff --git a/arch/i386/math-emu/reg_constant.c b/arch/i386/math-emu/reg_constant.c index c1981ce24..1b2458eea 100644 --- a/arch/i386/math-emu/reg_constant.c +++ b/arch/i386/math-emu/reg_constant.c @@ -3,9 +3,9 @@ | | | All of the constant FPU_REGs | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1996 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ @@ -14,6 +14,7 @@ #include "fpu_emu.h" #include "status_w.h" #include "reg_constant.h" +#include "control_w.h" FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS, @@ -56,7 +57,7 @@ FPU_REG const CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x8000 -static void fld_const(FPU_REG const *c) +static void fld_const(FPU_REG const *c, int adj) { FPU_REG *st_new_ptr; @@ -67,50 +68,58 @@ static void fld_const(FPU_REG const *c) } push(); reg_move(c, st_new_ptr); + st_new_ptr->sigl += adj; /* For all our fldxxx constants, we don't need to + borrow or carry. */ clear_C1(); } +/* A fast way to find out whether x is one of RC_DOWN or RC_CHOP + (and not one of RC_RND or RC_UP). + */ +#define DOWN_OR_CHOP(x) (x & RC_DOWN) -static void fld1(void) +static void fld1(int rc) { - fld_const(&CONST_1); + fld_const(&CONST_1, 0); } -static void fldl2t(void) +static void fldl2t(int rc) { - fld_const(&CONST_L2T); + fld_const(&CONST_L2T, (rc == RC_UP) ? 1 : 0); } -static void fldl2e(void) +static void fldl2e(int rc) { - fld_const(&CONST_L2E); + fld_const(&CONST_L2E, DOWN_OR_CHOP(rc) ? -1 : 0); } -static void fldpi(void) +static void fldpi(int rc) { - fld_const(&CONST_PI); + fld_const(&CONST_PI, DOWN_OR_CHOP(rc) ? -1 : 0); } -static void fldlg2(void) +static void fldlg2(int rc) { - fld_const(&CONST_LG2); + fld_const(&CONST_LG2, DOWN_OR_CHOP(rc) ? -1 : 0); } -static void fldln2(void) +static void fldln2(int rc) { - fld_const(&CONST_LN2); + fld_const(&CONST_LN2, DOWN_OR_CHOP(rc) ? -1 : 0); } -static void fldz(void) +static void fldz(int rc) { - fld_const(&CONST_Z); + fld_const(&CONST_Z, 0); } -static FUNC constants_table[] = { - fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, FPU_illegal +typedef void (*FUNC_RC)(int); + +static FUNC_RC constants_table[] = { + fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, (FUNC_RC)FPU_illegal }; void fconst(void) { - (constants_table[FPU_rm])(); + (constants_table[FPU_rm])(control_word & CW_RC); } diff --git a/arch/i386/math-emu/reg_div.S b/arch/i386/math-emu/reg_div.S index 2fbc5f7c4..24d44ac6c 100644 --- a/arch/i386/math-emu/reg_div.S +++ b/arch/i386/math-emu/reg_div.S @@ -4,9 +4,9 @@ | | | Divide one FPU_REG by another and put the result in a destination FPU_REG.| | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Call from C as: | | void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, | @@ -15,14 +15,11 @@ +---------------------------------------------------------------------------*/ #include "exception.h" -#include "fpu_asm.h" +#include "fpu_emu.h" .text - .align 2 - -.globl _reg_div -_reg_div: +ENTRY(reg_div) pushl %ebp movl %esp,%ebp #ifndef NON_REENTRANT_FPU @@ -47,7 +44,7 @@ _reg_div: cmpl EXP_UNDER,EXP(%esi) jg xL_arg1_not_denormal - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit @@ -55,7 +52,7 @@ xL_arg1_not_denormal: cmpl EXP_UNDER,EXP(%ebx) jg xL_arg2_not_denormal - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit @@ -75,7 +72,7 @@ xL_arg2_not_denormal: addl EXP_BIAS,%edx movl %edx,EXP(%edi) - jmp _divide_kernel + jmp SYMBOL_NAME(divide_kernel) /*-----------------------------------------------------------------------*/ @@ -92,14 +89,14 @@ L_arg2_NaN: pushl %edi /* Destination */ pushl %esi pushl %ebx /* Ordering is important here */ - call _real_2op_NaN + call SYMBOL_NAME(real_2op_NaN) jmp LDiv_exit /* Invalid operations */ L_zero_zero: L_inf_inf: pushl %edi /* Destination */ - call _arith_invalid /* 0/0 or Infinity/Infinity */ + call SYMBOL_NAME(arith_invalid) /* 0/0 or Infinity/Infinity */ jmp LDiv_exit L_no_NaN_arg: @@ -126,7 +123,7 @@ L_inf_valid: cmpl EXP_UNDER,EXP(%ebx) jg L_copy_arg1 /* Answer is Inf */ - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit #endif DENORM_OPERAND @@ -151,7 +148,7 @@ L_arg1_not_inf: movb SIGN(%esi),%al xorb SIGN(%ebx),%al pushl %eax /* lower 8 bits have the sign */ - call _divide_by_zero + call SYMBOL_NAME(divide_by_zero) jmp LDiv_exit L_arg2_not_zero: @@ -165,7 +162,7 @@ L_arg2_not_zero: cmpl EXP_UNDER,EXP(%esi) jg L_return_zero /* Answer is zero */ - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit #endif DENORM_OPERAND @@ -185,7 +182,7 @@ L_arg2_not_inf: cmpl EXP_UNDER,EXP(%ebx) jg L_copy_arg1 /* Answer is zero */ - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit #endif DENORM_OPERAND @@ -241,11 +238,11 @@ L_unknown_tags: call EXCEPTION /* Generate a NaN for unknown tags */ - movl _CONST_QNaN,%eax + movl SYMBOL_NAME(CONST_QNaN),%eax movl %eax,(%edi) - movl _CONST_QNaN+4,%eax + movl SYMBOL_NAME(CONST_QNaN)+4,%eax movl %eax,SIGL(%edi) - movl _CONST_QNaN+8,%eax + movl SYMBOL_NAME(CONST_QNaN)+8,%eax movl %eax,SIGH(%edi) jmp LDiv_exit /* %eax is nz */ #endif PARANOID diff --git a/arch/i386/math-emu/reg_ld_str.c b/arch/i386/math-emu/reg_ld_str.c index efec9e010..3e258a0c6 100644 --- a/arch/i386/math-emu/reg_ld_str.c +++ b/arch/i386/math-emu/reg_ld_str.c @@ -3,9 +3,9 @@ | | | All of the functions which transfer data between user memory and FPU_REGs.| | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1996 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@jacobi.maths.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ @@ -17,7 +17,7 @@ | other processes using the emulator while swapping is in progress. | +---------------------------------------------------------------------------*/ -#include <asm/segment.h> +#include <asm/uaccess.h> #include "fpu_system.h" #include "exception.h" @@ -48,9 +48,9 @@ int reg_load_extended(long double *s, FPU_REG *loaded_data) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, s, 10); - sigl = get_fs_long((unsigned long *) s); - sigh = get_fs_long(1 + (unsigned long *) s); - exp = get_fs_word(4 + (unsigned short *) s); + get_user(sigl, (unsigned long *) s); + get_user(sigh, 1 + (unsigned long *) s); + get_user(exp, 4 + (unsigned short *) s); RE_ENTRANT_CHECK_ON; loaded_data->tag = TW_Valid; /* Default */ @@ -144,8 +144,8 @@ int reg_load_double(double *dfloat, FPU_REG *loaded_data) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, dfloat, 8); - m64 = get_fs_long(1 + (unsigned long *) dfloat); - l64 = get_fs_long((unsigned long *) dfloat); + get_user(m64, 1 + (unsigned long *) dfloat); + get_user(l64, (unsigned long *) dfloat); RE_ENTRANT_CHECK_ON; if (m64 & 0x80000000) @@ -221,7 +221,7 @@ int reg_load_single(float *single, FPU_REG *loaded_data) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, single, 4); - m32 = get_fs_long((unsigned long *) single); + get_user(m32, (unsigned long *) single); RE_ENTRANT_CHECK_ON; if (m32 & 0x80000000) @@ -289,8 +289,7 @@ void reg_load_int64(long long *_s, FPU_REG *loaded_data) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, _s, 8); - ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s); - ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s); + copy_from_user(&s,_s,8); RE_ENTRANT_CHECK_ON; if (s == 0) @@ -320,7 +319,7 @@ void reg_load_int32(long *_s, FPU_REG *loaded_data) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, _s, 4); - s = (long)get_fs_long((unsigned long *) _s); + get_user(s, _s); RE_ENTRANT_CHECK_ON; if (s == 0) @@ -351,7 +350,7 @@ void reg_load_int16(short *_s, FPU_REG *loaded_data) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, _s, 2); /* Cast as short to get the sign extended. */ - s = (short)get_fs_word((unsigned short *) _s); + get_user(s, _s); RE_ENTRANT_CHECK_ON; if (s == 0) @@ -389,7 +388,7 @@ void reg_load_bcd(char *s, FPU_REG *loaded_data) { l *= 10; RE_ENTRANT_CHECK_OFF; - bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos); + get_user(bcd, (unsigned char *) s+pos); RE_ENTRANT_CHECK_ON; l += bcd >> 4; l *= 10; @@ -397,9 +396,11 @@ void reg_load_bcd(char *s, FPU_REG *loaded_data) } RE_ENTRANT_CHECK_OFF; - loaded_data->sign = - ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ? - SIGN_NEG : SIGN_POS; + { + unsigned char sign; + get_user(sign, (unsigned char *) s+9); + loaded_data->sign = (sign & 0x80) ? SIGN_NEG : SIGN_POS; + } RE_ENTRANT_CHECK_ON; if (l == 0) @@ -445,9 +446,9 @@ int reg_store_extended(long double *d, FPU_REG *st0_ptr) /* Put out the QNaN indefinite */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,10); - put_fs_long(0, (unsigned long *) d); - put_fs_long(0xc0000000, 1 + (unsigned long *) d); - put_fs_word(0xffff, 4 + (short *) d); + put_user(0, (unsigned long *) d); + put_user(0xc0000000, 1 + (unsigned long *) d); + put_user(0xffff, 4 + (short *) d); RE_ENTRANT_CHECK_ON; return 1; } @@ -466,6 +467,7 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr) if (st0_tag == TW_Valid) { + int precision_loss; int exp; FPU_REG tmp; @@ -474,8 +476,6 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr) if ( exp < DOUBLE_Emin ) /* It may be a denormal */ { - int precision_loss; - /* A denormal will always underflow. */ #ifndef PECULIAR_486 /* An 80486 is supposed to be able to generate @@ -518,6 +518,7 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr) { if ( tmp.sigl & 0x000007ff ) { + precision_loss = 1; switch (control_word & CW_RC) { case RC_RND: @@ -541,8 +542,6 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr) if ( increment ) { - set_precision_flag_up(); - if ( tmp.sigl >= 0xfffff800 ) { /* the sigl part overflows */ @@ -566,9 +565,9 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr) tmp.sigl += 0x00000800; } } - else - set_precision_flag_down(); } + else + precision_loss = 0; l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21); l[1] = ((tmp.sigh >> 11) & 0xfffff); @@ -590,6 +589,13 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr) } else { + if ( precision_loss ) + { + if ( increment ) + set_precision_flag_up(); + else + set_precision_flag_down(); + } /* Add the exponent */ l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20); } @@ -631,8 +637,8 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr) /* Put out the QNaN indefinite */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8); - put_fs_long(0, (unsigned long *) dfloat); - put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat); + put_user(0, (unsigned long *) dfloat); + put_user(0xfff80000, 1 + (unsigned long *) dfloat); RE_ENTRANT_CHECK_ON; return 1; } @@ -644,8 +650,8 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8); - put_fs_long(l[0], (unsigned long *)dfloat); - put_fs_long(l[1], 1 + (unsigned long *)dfloat); + put_user(l[0], (unsigned long *)dfloat); + put_user(l[1], 1 + (unsigned long *)dfloat); RE_ENTRANT_CHECK_ON; return 1; @@ -661,6 +667,7 @@ int reg_store_single(float *single, FPU_REG *st0_ptr) if (st0_tag == TW_Valid) { + int precision_loss; int exp; FPU_REG tmp; @@ -669,8 +676,6 @@ int reg_store_single(float *single, FPU_REG *st0_ptr) if ( exp < SINGLE_Emin ) { - int precision_loss; - /* A denormal will always underflow. */ #ifndef PECULIAR_486 /* An 80486 is supposed to be able to generate @@ -715,6 +720,7 @@ int reg_store_single(float *single, FPU_REG *st0_ptr) unsigned long sigh = tmp.sigh; unsigned long sigl = tmp.sigl; + precision_loss = 1; switch (control_word & CW_RC) { case RC_RND: @@ -740,8 +746,6 @@ int reg_store_single(float *single, FPU_REG *st0_ptr) if (increment) { - set_precision_flag_up(); - if ( sigh >= 0xffffff00 ) { /* The sigh part overflows */ @@ -758,10 +762,11 @@ int reg_store_single(float *single, FPU_REG *st0_ptr) } else { - set_precision_flag_down(); tmp.sigh &= 0xffffff00; /* Finish the truncation */ } } + else + precision_loss = 0; templ = (tmp.sigh >> 8) & 0x007fffff; @@ -780,7 +785,17 @@ int reg_store_single(float *single, FPU_REG *st0_ptr) templ = 0x7f800000; } else - templ |= ((exp+SINGLE_Ebias) & 0xff) << 23; + { + if ( precision_loss ) + { + if ( increment ) + set_precision_flag_up(); + else + set_precision_flag_down(); + } + /* Add the exponent */ + templ |= ((exp+SINGLE_Ebias) & 0xff) << 23; + } } } else if (st0_tag == TW_Zero) @@ -815,7 +830,7 @@ int reg_store_single(float *single, FPU_REG *st0_ptr) /* Put out the QNaN indefinite */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,(void *)single,4); - put_fs_long(0xffc00000, (unsigned long *) single); + put_user(0xffc00000, (unsigned long *) single); RE_ENTRANT_CHECK_ON; return 1; } @@ -834,7 +849,7 @@ int reg_store_single(float *single, FPU_REG *st0_ptr) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,(void *)single,4); - put_fs_long(templ,(unsigned long *) single); + put_user(templ,(unsigned long *) single); RE_ENTRANT_CHECK_ON; return 1; @@ -892,8 +907,7 @@ int reg_store_int64(long long *d, FPU_REG *st0_ptr) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,(void *)d,8); - put_fs_long(((long *)&tll)[0],(unsigned long *) d); - put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d); + copy_to_user(d, &tll, 8); RE_ENTRANT_CHECK_ON; return 1; @@ -947,7 +961,7 @@ int reg_store_int32(long *d, FPU_REG *st0_ptr) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,4); - put_fs_long(t.sigl, (unsigned long *) d); + put_user(t.sigl, (unsigned long *) d); RE_ENTRANT_CHECK_ON; return 1; @@ -1001,7 +1015,7 @@ int reg_store_int16(short *d, FPU_REG *st0_ptr) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,2); - put_fs_word((short)t.sigl,(short *) d); + put_user((short)t.sigl,(short *) d); RE_ENTRANT_CHECK_ON; return 1; @@ -1042,10 +1056,10 @@ int reg_store_bcd(char *d, FPU_REG *st0_ptr) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,10); for ( i = 0; i < 7; i++) - put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */ - put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */ - put_fs_byte(0xff, (unsigned char *) d+8); - put_fs_byte(0xff, (unsigned char *) d+9); + put_user(0, (unsigned char *) d+i); /* These bytes "undefined" */ + put_user(0xc0, (unsigned char *) d+7); /* This byte "undefined" */ + put_user(0xff, (unsigned char *) d+8); + put_user(0xff, (unsigned char *) d+9); RE_ENTRANT_CHECK_ON; return 1; } @@ -1066,11 +1080,11 @@ int reg_store_bcd(char *d, FPU_REG *st0_ptr) b = div_small(&ll, 10); b |= (div_small(&ll, 10)) << 4; RE_ENTRANT_CHECK_OFF; - put_fs_byte(b,(unsigned char *) d+i); + put_user(b,(unsigned char *) d+i); RE_ENTRANT_CHECK_ON; } RE_ENTRANT_CHECK_OFF; - put_fs_byte(sign,(unsigned char *) d+9); + put_user(sign,(unsigned char *) d+9); RE_ENTRANT_CHECK_ON; return 1; @@ -1158,13 +1172,13 @@ char *fldenv(fpu_addr_modes addr_modes, char *s) { RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, s, 0x0e); - control_word = get_fs_word((unsigned short *) s); - partial_status = get_fs_word((unsigned short *) (s+2)); - tag_word = get_fs_word((unsigned short *) (s+4)); - instruction_address.offset = get_fs_word((unsigned short *) (s+6)); - instruction_address.selector = get_fs_word((unsigned short *) (s+8)); - operand_address.offset = get_fs_word((unsigned short *) (s+0x0a)); - operand_address.selector = get_fs_word((unsigned short *) (s+0x0c)); + get_user(control_word, (unsigned short *) s); + get_user(partial_status, (unsigned short *) (s+2)); + get_user(tag_word, (unsigned short *) (s+4)); + get_user(instruction_address.offset, (unsigned short *) (s+6)); + get_user(instruction_address.selector, (unsigned short *) (s+8)); + get_user(operand_address.offset, (unsigned short *) (s+0x0a)); + get_user(operand_address.selector, (unsigned short *) (s+0x0c)); RE_ENTRANT_CHECK_ON; s += 0x0e; if ( addr_modes.default_mode == VM86 ) @@ -1178,14 +1192,14 @@ char *fldenv(fpu_addr_modes addr_modes, char *s) { RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, s, 0x1c); - control_word = get_fs_word((unsigned short *) s); - partial_status = get_fs_word((unsigned short *) (s+4)); - tag_word = get_fs_word((unsigned short *) (s+8)); - instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c)); - instruction_address.selector = get_fs_word((unsigned short *) (s+0x10)); - instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12)); - operand_address.offset = get_fs_long((unsigned long *) (s+0x14)); - operand_address.selector = get_fs_long((unsigned long *) (s+0x18)); + get_user(control_word, (unsigned short *) s); + get_user(partial_status, (unsigned short *) (s+4)); + get_user(tag_word, (unsigned short *) (s+8)); + get_user(instruction_address.offset, (unsigned long *) (s+0x0c)); + get_user(instruction_address.selector, (unsigned short *) (s+0x10)); + get_user(instruction_address.opcode, (unsigned short *) (s+0x12)); + get_user(operand_address.offset, (unsigned long *) (s+0x14)); + get_user(operand_address.selector, (unsigned long *) (s+0x18)); RE_ENTRANT_CHECK_ON; s += 0x1c; } @@ -1296,25 +1310,25 @@ char *fstenv(fpu_addr_modes addr_modes, char *d) RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,14); #ifdef PECULIAR_486 - put_fs_long(control_word & ~0xe080, (unsigned short *) d); + put_user(control_word & ~0xe080, (unsigned long *) d); #else - put_fs_word(control_word, (unsigned short *) d); + put_user(control_word, (unsigned short *) d); #endif PECULIAR_486 - put_fs_word(status_word(), (unsigned short *) (d+2)); - put_fs_word(tag_word(), (unsigned short *) (d+4)); - put_fs_word(instruction_address.offset, (unsigned short *) (d+6)); - put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a)); + put_user(status_word(), (unsigned short *) (d+2)); + put_user(tag_word(), (unsigned short *) (d+4)); + put_user(instruction_address.offset, (unsigned short *) (d+6)); + put_user(operand_address.offset, (unsigned short *) (d+0x0a)); if ( addr_modes.default_mode == VM86 ) { - put_fs_word((instruction_address.offset & 0xf0000) >> 4, + put_user((instruction_address.offset & 0xf0000) >> 4, (unsigned short *) (d+8)); - put_fs_word((operand_address.offset & 0xf0000) >> 4, + put_user((operand_address.offset & 0xf0000) >> 4, (unsigned short *) (d+0x0c)); } else { - put_fs_word(instruction_address.selector, (unsigned short *) (d+8)); - put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c)); + put_user(instruction_address.selector, (unsigned short *) (d+8)); + put_user(operand_address.selector, (unsigned short *) (d+0x0c)); } RE_ENTRANT_CHECK_ON; d += 0x0e; @@ -1325,24 +1339,24 @@ char *fstenv(fpu_addr_modes addr_modes, char *d) FPU_verify_area(VERIFY_WRITE,d,28); #ifdef PECULIAR_486 /* An 80486 sets all the reserved bits to 1. */ - put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d); - put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4)); - put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8)); + put_user(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d); + put_user(0xffff0000 | status_word(), (unsigned long *) (d+4)); + put_user(0xffff0000 | tag_word(), (unsigned long *) (d+8)); #else - put_fs_word(control_word, (unsigned short *) d); - put_fs_word(status_word(), (unsigned short *) (d+4)); - put_fs_word(tag_word(), (unsigned short *) (d+8)); + put_user(control_word, (unsigned short *) d); + put_user(status_word(), (unsigned short *) (d+4)); + put_user(tag_word(), (unsigned short *) (d+8)); #endif PECULIAR_486 - put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c)); - put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10)); - put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12)); - put_fs_long(operand_address.offset, (unsigned long *) (d+0x14)); + put_user(instruction_address.offset, (unsigned long *) (d+0x0c)); + put_user(instruction_address.selector, (unsigned short *) (d+0x10)); + put_user(instruction_address.opcode, (unsigned short *) (d+0x12)); + put_user(operand_address.offset, (unsigned long *) (d+0x14)); #ifdef PECULIAR_486 /* An 80486 sets all the reserved bits to 1. */ - put_fs_word(operand_address.selector, (unsigned short *) (d+0x18)); - put_fs_word(0xffff, (unsigned short *) (d+0x1a)); + put_user(operand_address.selector, (unsigned short *) (d+0x18)); + put_user(0xffff, (unsigned short *) (d+0x1a)); #else - put_fs_long(operand_address.selector, (unsigned long *) (d+0x18)); + put_user(operand_address.selector, (unsigned long *) (d+0x18)); #endif PECULIAR_486 RE_ENTRANT_CHECK_ON; d += 0x1c; @@ -1411,8 +1425,8 @@ static void write_to_extended(FPU_REG *rp, char *d) { /* just copy the reg */ RE_ENTRANT_CHECK_OFF; - put_fs_long(rp->sigl, (unsigned long *) d); - put_fs_long(rp->sigh, (unsigned long *) (d + 4)); + put_user(rp->sigl, (unsigned long *) d); + put_user(rp->sigh, (unsigned long *) (d + 4)); RE_ENTRANT_CHECK_ON; } else @@ -1427,12 +1441,12 @@ static void write_to_extended(FPU_REG *rp, char *d) round_to_int(&tmp); e = 0; RE_ENTRANT_CHECK_OFF; - put_fs_long(tmp.sigl, (unsigned long *) d); - put_fs_long(tmp.sigh, (unsigned long *) (d + 4)); + put_user(tmp.sigl, (unsigned long *) d); + put_user(tmp.sigh, (unsigned long *) (d + 4)); RE_ENTRANT_CHECK_ON; } e |= rp->sign == SIGN_POS ? 0 : 0x8000; RE_ENTRANT_CHECK_OFF; - put_fs_word(e, (unsigned short *) (d + 8)); + put_user(e, (unsigned short *) (d + 8)); RE_ENTRANT_CHECK_ON; } diff --git a/arch/i386/math-emu/reg_norm.S b/arch/i386/math-emu/reg_norm.S index 9b7a9d77d..781a2d466 100644 --- a/arch/i386/math-emu/reg_norm.S +++ b/arch/i386/math-emu/reg_norm.S @@ -1,9 +1,9 @@ /*---------------------------------------------------------------------------+ | reg_norm.S | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Normalize the value in a FPU_REG. | | | @@ -14,15 +14,11 @@ | | +---------------------------------------------------------------------------*/ -#include "fpu_asm.h" +#include "fpu_emu.h" .text - - .align 2,144 -.globl _normalize - -_normalize: +ENTRY(normalize) pushl %ebp movl %esp,%ebp pushl %ebx @@ -34,7 +30,7 @@ _normalize: je L_ok pushl $0x220 - call _exception + call SYMBOL_NAME(FPU_exception) addl $4,%esp L_ok: @@ -86,23 +82,20 @@ L_zero: L_underflow: push %ebx - call _arith_underflow + call SYMBOL_NAME(arith_underflow) pop %ebx jmp L_exit L_overflow: push %ebx - call _arith_overflow + call SYMBOL_NAME(arith_overflow) pop %ebx jmp L_exit /* Normalise without reporting underflow or overflow */ - .align 2,144 -.globl _normalize_nuo - -_normalize_nuo: +ENTRY(normalize_nuo) pushl %ebp movl %esp,%ebp pushl %ebx @@ -114,7 +107,7 @@ _normalize_nuo: je L_ok_nuo pushl $0x221 - call _exception + call SYMBOL_NAME(FPU_exception) addl $4,%esp L_ok_nuo: diff --git a/arch/i386/math-emu/reg_round.S b/arch/i386/math-emu/reg_round.S index bd8a40dc4..4aac507a1 100644 --- a/arch/i386/math-emu/reg_round.S +++ b/arch/i386/math-emu/reg_round.S @@ -4,9 +4,9 @@ | | | Rounding/truncation/etc for FPU basic arithmetic functions. | | | - | Copyright (C) 1993 | + | Copyright (C) 1993,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | This code has four possible entry points. | | The following must be entered by a jmp instruction: | @@ -69,7 +69,7 @@ +---------------------------------------------------------------------------*/ -#include "fpu_asm.h" +#include "fpu_emu.h" #include "exception.h" #include "control_w.h" @@ -101,14 +101,12 @@ FPU_denormal: .text - .align 2,144 .globl fpu_reg_round .globl fpu_reg_round_sqrt .globl fpu_Arith_exit -.globl _round_reg /* Entry point when called from C */ -_round_reg: +ENTRY(round_reg) pushl %ebp movl %esp,%ebp pushl %esi @@ -435,7 +433,7 @@ fpu_Arith_exit: */ xL_precision_lost_up: push %eax - call _set_precision_flag_up + call SYMBOL_NAME(set_precision_flag_up) popl %eax jmp xL_no_precision_loss @@ -445,7 +443,7 @@ xL_precision_lost_up: */ xL_precision_lost_down: push %eax - call _set_precision_flag_down + call SYMBOL_NAME(set_precision_flag_down) popl %eax jmp xL_no_precision_loss @@ -598,7 +596,7 @@ LNormalise_shift_done: /* There must be a masked underflow */ push %eax pushl EX_Underflow - call _exception + call SYMBOL_NAME(FPU_exception) popl %eax popl %eax jmp xL_Normalised @@ -610,12 +608,12 @@ LNormalise_shift_done: */ L_underflow_to_zero: push %eax - call _set_precision_flag_down + call SYMBOL_NAME(set_precision_flag_down) popl %eax push %eax pushl EX_Underflow - call _exception + call SYMBOL_NAME(FPU_exception) popl %eax popl %eax @@ -628,7 +626,7 @@ L_underflow_to_zero: /* The operations resulted in a number too large to represent. */ L_overflow: push %edi - call _arith_overflow + call SYMBOL_NAME(arith_overflow) pop %edi jmp fpu_reg_round_exit diff --git a/arch/i386/math-emu/reg_u_add.S b/arch/i386/math-emu/reg_u_add.S index 4410f8fd4..1dc0d41df 100644 --- a/arch/i386/math-emu/reg_u_add.S +++ b/arch/i386/math-emu/reg_u_add.S @@ -5,9 +5,9 @@ | Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the | | result in a destination FPU_REG. | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Call from C as: | | void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | @@ -25,13 +25,11 @@ */ #include "exception.h" -#include "fpu_asm.h" +#include "fpu_emu.h" #include "control_w.h" .text - .align 2,144 -.globl _reg_u_add -_reg_u_add: +ENTRY(reg_u_add) pushl %ebp movl %esp,%ebp pushl %esi @@ -45,7 +43,7 @@ _reg_u_add: cmpl EXP_UNDER,EXP(%esi) jg xOp1_not_denorm - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit @@ -53,7 +51,7 @@ xOp1_not_denorm: cmpl EXP_UNDER,EXP(%edi) jg xOp2_not_denorm - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit diff --git a/arch/i386/math-emu/reg_u_div.S b/arch/i386/math-emu/reg_u_div.S index 328e9116e..5bba98dd8 100644 --- a/arch/i386/math-emu/reg_u_div.S +++ b/arch/i386/math-emu/reg_u_div.S @@ -4,9 +4,9 @@ | | | Core division routines | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ @@ -21,7 +21,7 @@ +---------------------------------------------------------------------------*/ #include "exception.h" -#include "fpu_asm.h" +#include "fpu_emu.h" #include "control_w.h" @@ -69,13 +69,7 @@ FPU_ovfl_flag: .text - .align 2,144 - -.globl _reg_u_div - -.globl _divide_kernel - -_reg_u_div: +ENTRY(reg_u_div) pushl %ebp movl %esp,%ebp #ifndef NON_REENTRANT_FPU @@ -95,7 +89,7 @@ _reg_u_div: cmpl EXP_UNDER,%eax jg xOp1_not_denorm - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit @@ -104,14 +98,14 @@ xOp1_not_denorm: cmpl EXP_UNDER,%eax jg xOp2_not_denorm - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit xOp2_not_denorm: #endif DENORM_OPERAND -_divide_kernel: +ENTRY(divide_kernel) #ifdef PARANOID /* testl $0x80000000, SIGH(%esi) // Dividend */ /* je L_bugged */ diff --git a/arch/i386/math-emu/reg_u_mul.S b/arch/i386/math-emu/reg_u_mul.S index 8250666bd..682fbec15 100644 --- a/arch/i386/math-emu/reg_u_mul.S +++ b/arch/i386/math-emu/reg_u_mul.S @@ -4,9 +4,9 @@ | | | Core multiplication routine | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ @@ -22,7 +22,7 @@ +---------------------------------------------------------------------------*/ #include "exception.h" -#include "fpu_asm.h" +#include "fpu_emu.h" #include "control_w.h" @@ -44,10 +44,7 @@ FPU_accum_1: .text - .align 2,144 - -.globl _reg_u_mul -_reg_u_mul: +ENTRY(reg_u_mul) pushl %ebp movl %esp,%ebp #ifndef NON_REENTRANT_FPU @@ -73,7 +70,7 @@ _reg_u_mul: cmpl EXP_UNDER,%eax jg xOp1_not_denorm - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit @@ -82,7 +79,7 @@ xOp1_not_denorm: cmpl EXP_UNDER,%eax jg xOp2_not_denorm - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit diff --git a/arch/i386/math-emu/reg_u_sub.S b/arch/i386/math-emu/reg_u_sub.S index fbec17dfb..891670260 100644 --- a/arch/i386/math-emu/reg_u_sub.S +++ b/arch/i386/math-emu/reg_u_sub.S @@ -4,9 +4,9 @@ | | | Core floating point subtraction routine. | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Call from C as: | | void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | @@ -26,13 +26,11 @@ */ #include "exception.h" -#include "fpu_asm.h" +#include "fpu_emu.h" #include "control_w.h" .text - .align 2,144 -.globl _reg_u_sub -_reg_u_sub: +ENTRY(reg_u_sub) pushl %ebp movl %esp,%ebp pushl %esi @@ -46,7 +44,7 @@ _reg_u_sub: cmpl EXP_UNDER,EXP(%esi) jg xOp1_not_denorm - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit @@ -54,7 +52,7 @@ xOp1_not_denorm: cmpl EXP_UNDER,EXP(%edi) jg xOp2_not_denorm - call _denormal_operand + call SYMBOL_NAME(denormal_operand) orl %eax,%eax jnz fpu_Arith_exit diff --git a/arch/i386/math-emu/round_Xsig.S b/arch/i386/math-emu/round_Xsig.S index 163755878..bbe0e8771 100644 --- a/arch/i386/math-emu/round_Xsig.S +++ b/arch/i386/math-emu/round_Xsig.S @@ -1,9 +1,9 @@ /*---------------------------------------------------------------------------+ | round_Xsig.S | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Normalize and round a 12 byte quantity. | | Call from C as: | @@ -18,15 +18,11 @@ +---------------------------------------------------------------------------*/ .file "round_Xsig.S" -#include "fpu_asm.h" +#include "fpu_emu.h" .text - - .align 2,144 -.globl _round_Xsig - -_round_Xsig: +ENTRY(round_Xsig) pushl %ebp movl %esp,%ebp pushl %ebx /* Reserve some space */ @@ -86,10 +82,7 @@ L_exit: - .align 2,144 -.globl _norm_Xsig - -_norm_Xsig: +ENTRY(norm_Xsig) pushl %ebp movl %esp,%ebp pushl %ebx /* Reserve some space */ diff --git a/arch/i386/math-emu/shr_Xsig.S b/arch/i386/math-emu/shr_Xsig.S index d6724a204..31cdd118e 100644 --- a/arch/i386/math-emu/shr_Xsig.S +++ b/arch/i386/math-emu/shr_Xsig.S @@ -4,9 +4,9 @@ | | | 12 byte right shift function | | | - | Copyright (C) 1992,1994 | + | Copyright (C) 1992,1994,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Call from C as: | | void shr_Xsig(Xsig *arg, unsigned nr) | @@ -18,13 +18,10 @@ | | +---------------------------------------------------------------------------*/ -#include "fpu_asm.h" +#include "fpu_emu.h" .text - .align 2,144 - - .globl _shr_Xsig -_shr_Xsig: +ENTRY(shr_Xsig) push %ebp movl %esp,%ebp pushl %esi diff --git a/arch/i386/math-emu/status_w.h b/arch/i386/math-emu/status_w.h index 96607d0e1..57d1ae6bb 100644 --- a/arch/i386/math-emu/status_w.h +++ b/arch/i386/math-emu/status_w.h @@ -12,7 +12,7 @@ #include "fpu_emu.h" /* for definition of PECULIAR_486 */ -#ifdef __ASSEMBLER__ +#ifdef __ASSEMBLY__ #define Const__(x) $##x #else #define Const__(x) x @@ -36,7 +36,7 @@ #define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */ -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ #define COMP_A_gt_B 1 #define COMP_A_eq_B 2 @@ -60,6 +60,6 @@ # define clear_C1() #endif PECULIAR_486 -#endif __ASSEMBLER__ +#endif __ASSEMBLY__ #endif _STATUS_H_ diff --git a/arch/i386/math-emu/version.h b/arch/i386/math-emu/version.h index 4c75a4792..d966ab193 100644 --- a/arch/i386/math-emu/version.h +++ b/arch/i386/math-emu/version.h @@ -2,11 +2,11 @@ | version.h | | | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1996 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@jacobi.maths.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ -#define FPU_VERSION "wm-FPU-emu version 1.20" +#define FPU_VERSION "wm-FPU-emu version 1.22" diff --git a/arch/i386/math-emu/wm_shrx.S b/arch/i386/math-emu/wm_shrx.S index bef0e1963..1ea7ff7da 100644 --- a/arch/i386/math-emu/wm_shrx.S +++ b/arch/i386/math-emu/wm_shrx.S @@ -4,8 +4,9 @@ | | | 64 bit right shift functions | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1995 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Call from C as: | | unsigned shrx(void *arg1, unsigned arg2) | @@ -14,11 +15,9 @@ | | +---------------------------------------------------------------------------*/ -#include "fpu_asm.h" +#include "fpu_emu.h" .text - .align 2,144 - /*---------------------------------------------------------------------------+ | unsigned shrx(void *arg1, unsigned arg2) | | | @@ -33,9 +32,7 @@ | Results returned in the 64 bit arg and eax. | +---------------------------------------------------------------------------*/ - .globl _shrx - -_shrx: +ENTRY(shrx) push %ebp movl %esp,%ebp pushl %esi @@ -113,8 +110,7 @@ L_more_than_95: | part which has been shifted out of the arg. | | Results returned in the 64 bit arg and eax. | +---------------------------------------------------------------------------*/ - .globl _shrxs -_shrxs: +ENTRY(shrxs) push %ebp movl %esp,%ebp pushl %esi diff --git a/arch/i386/math-emu/wm_sqrt.S b/arch/i386/math-emu/wm_sqrt.S index 4e028cb80..848796188 100644 --- a/arch/i386/math-emu/wm_sqrt.S +++ b/arch/i386/math-emu/wm_sqrt.S @@ -4,9 +4,9 @@ | | | Fixed point arithmetic square root evaluation. | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1995 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Call from C as: | | void wm_sqrt(FPU_REG *n, unsigned int control_word) | @@ -26,7 +26,7 @@ +---------------------------------------------------------------------------*/ #include "exception.h" -#include "fpu_asm.h" +#include "fpu_emu.h" #ifndef NON_REENTRANT_FPU @@ -74,10 +74,7 @@ FPU_fsqrt_arg_0: .text - .align 2,144 - -.globl _wm_sqrt -_wm_sqrt: +ENTRY(wm_sqrt) pushl %ebp movl %esp,%ebp #ifndef NON_REENTRANT_FPU diff --git a/arch/i386/mm/Makefile b/arch/i386/mm/Makefile index af75a20a4..cee7d4e6d 100644 --- a/arch/i386/mm/Makefile +++ b/arch/i386/mm/Makefile @@ -7,26 +7,7 @@ # # Note 2! The CFLAGS definition is now in the main makefile... -.c.o: - $(CC) $(CFLAGS) -c $< -.s.o: - $(AS) -o $*.o $< -.c.s: - $(CC) $(CFLAGS) -S $< +O_TARGET := mm.o +O_OBJS := init.o fault.o ioremap.o extable.o -OBJS = init.o fault.o - -mm.o: $(OBJS) - $(LD) -r -o mm.o $(OBJS) - -modules: - -dep: - $(CPP) -M *.c > .depend - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif +include $(TOPDIR)/Rules.make diff --git a/arch/i386/mm/extable.c b/arch/i386/mm/extable.c new file mode 100644 index 000000000..c43a8d43d --- /dev/null +++ b/arch/i386/mm/extable.c @@ -0,0 +1,63 @@ +/* + * linux/arch/i386/mm/extable.c + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + /* Some versions of the linker are buggy and do not align the + __start pointer along with the section, thus we may be low. */ + if ((long)first & 3) + (long)first = ((long)first | 3) + 1; + + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; +#ifdef CONFIG_MODULES + struct module *mp; +#endif + + /* Search the kernel's table first. */ + ret = search_one_table(__start___ex_table, + __stop___ex_table-1, addr); + if (ret) + return ret; + +#ifdef CONFIG_MODULES + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->exceptinfo.start != NULL) { + ret = search_one_table(mp->exceptinfo.start, + mp->exceptinfo.stop-1, addr); + if (ret) + return ret; + } + } +#endif + return 0; +} diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index 01c259f0f..50dcd0735 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c @@ -4,7 +4,6 @@ * Copyright (C) 1995 Linus Torvalds */ -#include <linux/config.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/head.h> @@ -17,10 +16,61 @@ #include <linux/mm.h> #include <asm/system.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/pgtable.h> -extern void die_if_kernel(char *,struct pt_regs *,long); +extern void die_if_kernel(const char *,struct pt_regs *,long); + +/* + * Ugly, ugly, but the goto's result in better assembly.. + */ +int __verify_write(const void * addr, unsigned long size) +{ + struct vm_area_struct * vma; + unsigned long start = (unsigned long) addr; + + if (!size) + return 1; + + vma = find_vma(current->mm, start); + if (!vma) + goto bad_area; + if (vma->vm_start > start) + goto check_stack; + +good_area: + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + size--; + size += start & ~PAGE_MASK; + size >>= PAGE_SHIFT; + start &= PAGE_MASK; + + for (;;) { + do_wp_page(current, vma, start, 1); + if (!size) + break; + size--; + start += PAGE_SIZE; + if (start < vma->vm_end) + continue; + vma = vma->vm_next; + if (!vma || vma->vm_start != start) + goto bad_area; + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area;; + } + return 1; + +check_stack: + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (expand_stack(vma, start) == 0) + goto good_area; + +bad_area: + return 0; +} /* * This routine handles page faults. It determines the address, @@ -34,58 +84,76 @@ extern void die_if_kernel(char *,struct pt_regs *,long); */ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) { + void (*handler)(struct task_struct *, + struct vm_area_struct *, + unsigned long, + int); + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->mm; struct vm_area_struct * vma; unsigned long address; unsigned long page; + unsigned long fixup; + int write; /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); - vma = find_vma(current, address); + down(&mm->mmap_sem); + vma = find_vma(mm, address); if (!vma) goto bad_area; if (vma->vm_start <= address) goto good_area; if (!(vma->vm_flags & VM_GROWSDOWN)) goto bad_area; - if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur) + if (error_code & 4) { + /* + * accessing the stack below %esp is always a bug. + * The "+ 32" is there due to some instructions (like + * pusha) doing pre-decrement on the stack and that + * doesn't show up until later.. + */ + if (address + 32 < regs->esp) + goto bad_area; + } + if (expand_stack(vma, address)) goto bad_area; - vma->vm_offset -= vma->vm_start - (address & PAGE_MASK); - vma->vm_start = (address & PAGE_MASK); /* * Ok, we have a good vm_area for this memory access, so * we can handle it.. */ good_area: - /* - * was it a write? - */ - if (error_code & 2) { - if (!(vma->vm_flags & VM_WRITE)) - goto bad_area; - } else { - /* read with protection fault? */ - if (error_code & 1) - goto bad_area; - if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + write = 0; + handler = do_no_page; + switch (error_code & 3) { + default: /* 3: write, present */ + handler = do_wp_page; +#ifdef TEST_VERIFY_AREA + if (regs->cs == KERNEL_CS) + printk("WP fault at %08lx\n", regs->eip); +#endif + /* fall through */ + case 2: /* write, not present */ + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + write++; + break; + case 1: /* read, present */ goto bad_area; + case 0: /* read, not present */ + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; } + handler(tsk, vma, address, write); + up(&mm->mmap_sem); /* * Did it hit the DOS screen memory VA from vm86 mode? */ if (regs->eflags & VM_MASK) { unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT; if (bit < 32) - current->tss.screen_bitmap |= 1 << bit; - } - if (error_code & 1) { -#ifdef CONFIG_TEST_VERIFY_AREA - if (regs->cs == KERNEL_CS) - printk("WP fault at %08x\n", regs->eip); -#endif - do_wp_page(vma, address, error_code & 2); - return; + tsk->tss.screen_bitmap |= 1 << bit; } - do_no_page(vma, address, error_code & 2); return; /* @@ -93,11 +161,20 @@ good_area: * Fix it, but check if it's kernel or user first.. */ bad_area: + up(&mm->mmap_sem); + + /* Are we prepared to handle this fault? */ + if ((fixup = search_exception_table(regs->eip)) != 0) { + printk("Exception at %lx (%lx)\n", regs->eip, fixup); + regs->eip = fixup; + return; + } + if (error_code & 4) { - current->tss.cr2 = address; - current->tss.error_code = error_code; - current->tss.trap_no = 14; - send_sig(SIGSEGV, current, 1); + tsk->tss.cr2 = address; + tsk->tss.error_code = error_code; + tsk->tss.trap_no = 14; + force_sig(SIGSEGV, tsk); return; } /* @@ -106,14 +183,14 @@ bad_area: * * First we check if it was the bootup rw-test, though.. */ - if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & 1)) { + if (wp_works_ok < 0 && !address && (error_code & 1)) { wp_works_ok = 1; pg0[0] = pte_val(mk_pte(0, PAGE_SHARED)); - invalidate(); + flush_tlb(); printk("This processor honours the WP bit even when in supervisor mode. Good.\n"); return; } - if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) { + if (address < PAGE_SIZE) { printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); pg0[0] = pte_val(mk_pte(0, PAGE_SHARED)); } else @@ -121,13 +198,13 @@ bad_area: printk(" at virtual address %08lx\n",address); __asm__("movl %%cr3,%0" : "=r" (page)); printk(KERN_ALERT "current->tss.cr3 = %08lx, %%cr3 = %08lx\n", - current->tss.cr3, page); - page = ((unsigned long *) page)[address >> 22]; + tsk->tss.cr3, page); + page = ((unsigned long *) __va(page))[address >> 22]; printk(KERN_ALERT "*pde = %08lx\n", page); if (page & 1) { page &= PAGE_MASK; address &= 0x003ff000; - page = ((unsigned long *) page)[address >> PAGE_SHIFT]; + page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; printk(KERN_ALERT "*pte = %08lx\n", page); } die_if_kernel("Oops", regs, error_code); diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 296595644..cf258d6de 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -15,13 +15,17 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#ifdef CONFIG_BLK_DEV_INITRD +#include <linux/blk.h> +#endif #include <asm/system.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/pgtable.h> +#include <asm/dma.h> -extern void scsi_mem_init(unsigned long); -extern void sound_mem_init(void); extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); @@ -29,7 +33,7 @@ extern void show_net_buffers(void); * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a * do_exit(), but using this instead means there is less risk - * for a process dying in kernel mode, possibly leaving a inode + * for a process dying in kernel mode, possibly leaving an inode * unused etc.. * * BAD_PAGETABLE is the accompanying page-table: it is initialized @@ -70,15 +74,15 @@ void show_mem(void) printk("Mem-info:\n"); show_free_areas(); printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = high_memory >> PAGE_SHIFT; + i = max_mapnr; while (i-- > 0) { total++; - if (mem_map[i] & MAP_PAGE_RESERVED) + if (PageReserved(mem_map+i)) reserved++; - else if (!mem_map[i]) + else if (!mem_map[i].count) free++; else - shared += mem_map[i]-1; + shared += mem_map[i].count-1; } printk("%d pages of RAM\n",total); printk("%d free pages\n",free); @@ -111,34 +115,90 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) * and SMM (for laptops with [34]86/SL chips) may need it. It is read * and write protected to detect null pointer references in the * kernel. + * It may also hold the MP configuration table when we are booting SMP. */ #if 0 memset((void *) 0, 0, PAGE_SIZE); #endif +#ifdef __SMP__ + if (!smp_scan_config(0x0,0x400)) /* Scan the bottom 1K for a signature */ + { + /* + * FIXME: Linux assumes you have 640K of base ram.. this continues + * the error... + */ + if (!smp_scan_config(639*0x400,0x400)) /* Scan the top 1K of base RAM */ + smp_scan_config(0xF0000,0x10000); /* Scan the 64K of bios */ + } + /* + * If it is an SMP machine we should know now, unless the configuration + * is in an EISA/MCA bus machine with an extended bios data area. I don't + * have such a machine so someone else can fill in the check of the EBDA + * here. + */ +/* smp_alloc_memory(8192); */ +#endif +#ifdef TEST_VERIFY_AREA + wp_works_ok = 0; +#endif start_mem = PAGE_ALIGN(start_mem); - address = 0; + address = PAGE_OFFSET; pg_dir = swapper_pg_dir; + /* unmap the original low memory mappings */ + pgd_val(pg_dir[0]) = 0; while (address < end_mem) { + /* + * The following code enabled 4MB page tables for the + * Intel Pentium cpu, unfortunately the SMP kernel can't + * handle the 4MB page table optimizations yet + */ +#ifndef __SMP__ + /* + * This will create page tables that + * span up to the next 4MB virtual + * memory boundary, but that's ok, + * we won't use that memory anyway. + */ + if (x86_capability & 8) { +#ifdef GAS_KNOWS_CR4 + __asm__("movl %%cr4,%%eax\n\t" + "orl $16,%%eax\n\t" + "movl %%eax,%%cr4" + : : :"ax"); +#else + __asm__(".byte 0x0f,0x20,0xe0\n\t" + "orl $16,%%eax\n\t" + ".byte 0x0f,0x22,0xe0" + : : :"ax"); +#endif + wp_works_ok = 1; + pgd_val(pg_dir[768]) = _PAGE_TABLE + _PAGE_4M + __pa(address); + pg_dir++; + address += 4*1024*1024; + continue; + } +#endif /* map the memory at virtual addr 0xC0000000 */ + /* pg_table is physical at this point */ pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768])); if (!pg_table) { - pg_table = (pte_t *) start_mem; + pg_table = (pte_t *) __pa(start_mem); start_mem += PAGE_SIZE; } - /* also map it temporarily at 0x0000000 for init */ - pgd_val(pg_dir[0]) = _PAGE_TABLE | (unsigned long) pg_table; pgd_val(pg_dir[768]) = _PAGE_TABLE | (unsigned long) pg_table; pg_dir++; + /* now change pg_table to kernel virtual addresses */ + pg_table = (pte_t *) __va(pg_table); for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { - if (address < end_mem) - *pg_table = mk_pte(address, PAGE_SHARED); - else - pte_clear(pg_table); + pte_t pte = mk_pte(address, PAGE_KERNEL); + if (address >= end_mem) + pte_val(pte) = 0; + set_pte(pg_table, pte); address += PAGE_SIZE; } } - invalidate(); + local_flush_tlb(); return free_area_init(start_mem, end_mem); } @@ -149,16 +209,25 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) int reservedpages = 0; int datapages = 0; unsigned long tmp; - extern int etext; + extern int _etext; end_mem &= PAGE_MASK; - high_memory = end_mem; + high_memory = (void *) end_mem; + max_mapnr = MAP_NR(end_mem); /* clear the zero-page */ memset(empty_zero_page, 0, PAGE_SIZE); /* mark usable pages in the mem_map[] */ - start_low_mem = PAGE_ALIGN(start_low_mem); + start_low_mem = PAGE_ALIGN(start_low_mem)+PAGE_OFFSET; + +#ifdef __SMP__ + /* + * But first pinch a few for the stack/trampoline stuff + */ + start_low_mem += PAGE_SIZE; /* 32bit startup code */ + start_low_mem = smp_alloc_memory(start_low_mem); /* AP processor stacks */ +#endif start_mem = PAGE_ALIGN(start_mem); /* @@ -166,53 +235,56 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) * They seem to have done something stupid with the floppy * controller as well.. */ - while (start_low_mem < 0x9f000) { - mem_map[MAP_NR(start_low_mem)] = 0; + while (start_low_mem < 0x9f000+PAGE_OFFSET) { + clear_bit(PG_reserved, &mem_map[MAP_NR(start_low_mem)].flags); start_low_mem += PAGE_SIZE; } - while (start_mem < high_memory) { - mem_map[MAP_NR(start_mem)] = 0; + while (start_mem < end_mem) { + clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags); start_mem += PAGE_SIZE; } -#ifdef CONFIG_SCSI - scsi_mem_init(high_memory); -#endif -#ifdef CONFIG_SOUND - sound_mem_init(); -#endif - for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) { - if (mem_map[MAP_NR(tmp)]) { - if (tmp >= 0xA0000 && tmp < 0x100000) + for (tmp = PAGE_OFFSET ; tmp < end_mem ; tmp += PAGE_SIZE) { + if (tmp >= MAX_DMA_ADDRESS) + clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags); + if (PageReserved(mem_map+MAP_NR(tmp))) { + if (tmp >= 0xA0000+PAGE_OFFSET && tmp < 0x100000+PAGE_OFFSET) reservedpages++; - else if (tmp < (unsigned long) &etext) + else if (tmp < (unsigned long) &_etext) codepages++; else datapages++; continue; } - mem_map[MAP_NR(tmp)] = 1; - free_page(tmp); + mem_map[MAP_NR(tmp)].count = 1; +#ifdef CONFIG_BLK_DEV_INITRD + if (!initrd_start || (tmp < initrd_start || tmp >= + initrd_end)) +#endif + free_page(tmp); } - tmp = nr_free_pages << PAGE_SHIFT; printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n", - tmp >> 10, - high_memory >> 10, + (unsigned long) nr_free_pages << (PAGE_SHIFT-10), + max_mapnr << (PAGE_SHIFT-10), codepages << (PAGE_SHIFT-10), reservedpages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10)); /* test if the WP bit is honoured in supervisor mode */ - wp_works_ok = -1; - pg0[0] = pte_val(mk_pte(0, PAGE_READONLY)); - invalidate(); - __asm__ __volatile__("movb 0,%%al ; movb %%al,0": : :"ax", "memory"); - pg0[0] = 0; - invalidate(); - if (wp_works_ok < 0) - wp_works_ok = 0; -#ifdef CONFIG_TEST_VERIFY_AREA - wp_works_ok = 0; -#endif + if (wp_works_ok < 0) { + unsigned char tmp_reg; + pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_READONLY)); + local_flush_tlb(); + __asm__ __volatile__( + "movb %0,%1 ; movb %1,%0" + :"=m" (*(char *) __va(0)), + "=q" (tmp_reg) + :/* no inputs */ + :"memory"); + pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_KERNEL)); + local_flush_tlb(); + if (wp_works_ok < 0) + wp_works_ok = 0; + } return; } @@ -220,18 +292,18 @@ void si_meminfo(struct sysinfo *val) { int i; - i = high_memory >> PAGE_SHIFT; + i = max_mapnr; val->totalram = 0; val->sharedram = 0; val->freeram = nr_free_pages << PAGE_SHIFT; val->bufferram = buffermem; while (i-- > 0) { - if (mem_map[i] & MAP_PAGE_RESERVED) + if (PageReserved(mem_map+i)) continue; val->totalram++; - if (!mem_map[i]) + if (!mem_map[i].count) continue; - val->sharedram += mem_map[i]-1; + val->sharedram += mem_map[i].count-1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; diff --git a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c new file mode 100644 index 000000000..42b386c1c --- /dev/null +++ b/arch/i386/mm/ioremap.c @@ -0,0 +1,109 @@ +/* + * arch/i386/mm/ioremap.c + * + * Re-map IO memory to kernel address space so that we can access it. + * This is needed for high PCI addresses that aren't mapped in the + * 640k-1MB IO memory area on PC's + * + * (C) Copyright 1995 1996 Linus Torvalds + */ + +#include <linux/vmalloc.h> + +#include <asm/io.h> + +static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long phys_addr) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + if (!pte_none(*pte)) + printk("remap_area_pte: page already exists\n"); + set_pte(pte, mk_pte_phys(phys_addr, PAGE_KERNEL)); + address += PAGE_SIZE; + phys_addr += PAGE_SIZE; + pte++; + } while (address < end); +} + +static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long phys_addr) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + do { + pte_t * pte = pte_alloc_kernel(pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address < end); + return 0; +} + +static int remap_area_pages(unsigned long address, unsigned long phys_addr, unsigned long size) +{ + pgd_t * dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + while (address < end) { + pmd_t *pmd = pmd_alloc_kernel(dir, address); + if (!pmd) + return -ENOMEM; + if (remap_area_pmd(pmd, address, end - address, phys_addr + address)) + return -ENOMEM; + set_pgdir(address, *dir); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } + flush_tlb_all(); + return 0; +} + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + */ +void * ioremap(unsigned long phys_addr, unsigned long size) +{ + void * addr; + struct vm_struct * area; + + if (phys_addr < virt_to_phys(high_memory)) + return phys_to_virt(phys_addr); + if (phys_addr & ~PAGE_MASK) + return NULL; + size = PAGE_ALIGN(size); + if (!size || size > phys_addr + size) + return NULL; + area = get_vm_area(size); + if (!area) + return NULL; + addr = area->addr; + if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size)) { + vfree(addr); + return NULL; + } + return addr; +} + +void iounmap(void *addr) +{ + if (addr > high_memory) + return vfree(addr); +} |