diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
commit | e7c2a72e2680827d6a733931273a93461c0d8d1b (patch) | |
tree | c9abeda78ef7504062bb2e816bcf3e3c9d680112 /arch | |
parent | ec6044459060a8c9ce7f64405c465d141898548c (diff) |
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'arch')
209 files changed, 38899 insertions, 13852 deletions
diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile index 35800431a..beff9f4ed 100644 --- a/arch/alpha/Makefile +++ b/arch/alpha/Makefile @@ -8,70 +8,28 @@ # Copyright (C) 1994 by Linus Torvalds # -AS =as -LD =ld -HOSTCC =gcc -I$(TOPDIR)/include -CC =gcc -D__KERNEL__ -I$(TOPDIR)/include -MAKE =make -CPP =$(CC) -E -AR =ar -STRIP =strip +NM := nm -B -zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem - $(MAKE) -C zBoot +LINKFLAGS = -non_shared -T 0xfffffc0000310000 -N +CFLAGS := $(CFLAGS) -mno-fp-regs -zImage: $(CONFIGURE) boot/bootsect boot/setup zBoot/zSystem tools/build - tools/build boot/bootsect boot/setup zBoot/zSystem $(ROOT_DEV) > zImage - sync +HEAD := arch/alpha/kernel/head.o -zdisk: zImage - dd bs=8192 if=zImage of=/dev/fd0 +SUBDIRS := $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib +ARCHIVES := arch/alpha/kernel/kernel.o arch/alpha/mm/mm.o $(ARCHIVES) +LIBS := $(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a -zlilo: $(CONFIGURE) zImage - if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi - if [ -f $(INSTALL_PATH)/zSystem.map ]; then mv $(INSTALL_PATH)/zSystem.map $(INSTALL_PATH)/zSystem.old; fi - cat zImage > $(INSTALL_PATH)/vmlinuz - cp zSystem.map $(INSTALL_PATH)/ - if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi - -tools/system: boot/head.o init/main.o tools/version.o linuxsubdirs - $(LD) $(LOWLDFLAGS) boot/head.o init/main.o tools/version.o \ - $(ARCHIVES) \ - $(FILESYSTEMS) \ - $(DRIVERS) \ - $(LIBS) \ - -o tools/system - nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ - sort > System.map - -boot/setup.o: boot/setup.s - $(AS) -o $@ $< - -boot/setup.s: boot/setup.S $(CONFIGURE) include/linux/config.h Makefile - $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ - -boot/bootsect.s: boot/bootsect.S $(CONFIGURE) include/linux/config.h Makefile - $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ - -tools/zSystem: boot/head.o init/main.o tools/version.o linuxsubdirs - $(LD) $(HIGHLDFLAGS) boot/head.o init/main.o tools/version.o \ - $(ARCHIVES) \ - $(FILESYSTEMS) \ - $(DRIVERS) \ - $(LIBS) \ - -o tools/zSystem - nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ - sort > zSystem.map +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot # -# Leave these dummy entries for now to tell people that they are going away.. +# my boot writes directly to a specific disk partition, I doubt most +# people will want to do that without changes.. # -lilo: - @echo - @echo Uncompressed kernel images no longer supported. Use - @echo \"make zlilo\" instead. - @echo - @exit 1 +msb my-special-boot: + @$(MAKEBOOT) msb archclean: - rm -f boot/bootsect boot/setup + @$(MAKEBOOT) clean + +archdep: + @$(MAKEBOOT) dep diff --git a/arch/alpha/boot/Makefile b/arch/alpha/boot/Makefile new file mode 100644 index 000000000..b05047c2a --- /dev/null +++ b/arch/alpha/boot/Makefile @@ -0,0 +1,53 @@ +# +# arch/alpha/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# + +.c.s: + $(CC) $(CFLAGS) -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c -o $*.o $< +.S.s: + $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + +OBJECTS = head.o main.o + +all: tools/lxboot tools/bootlx vmlinux + @echo run mkfloppy on machine with floppy drive + +msb: tools/lxboot tools/bootlx vmlinux + ( cat tools/lxboot tools/bootlx vmlinux ) > /dev/rz0a + disklabel -rw rz0 'linux' tools/lxboot tools/bootlx + +vmlinux: tools/build $(TOPDIR)/vmlinux + tools/build -v $(TOPDIR)/vmlinux > vmlinux + +tools/lxboot: tools/build + tools/build > tools/lxboot + +tools/bootlx: bootloader tools/build + tools/build -vb bootloader > tools/bootlx + +tools/build: tools/build.c + $(HOSTCC) tools/build.c -o tools/build + +bootloader: $(OBJECTS) + $(LD) -non_shared -T 0x20000000 -N \ + $(OBJECTS) \ + $(LIBS) \ + -o bootloader || \ + (rm -f bootloader && exit 1) + +clean: + rm -f vmlinux bootloader tools/build tools/bootlx tools/lxboot + +dep: diff --git a/arch/alpha/boot/head.S b/arch/alpha/boot/head.S index 03a2a9010..efca165e6 100644 --- a/arch/alpha/boot/head.S +++ b/arch/alpha/boot/head.S @@ -1,44 +1,23 @@ /* - * alpha/head.S + * arch/alpha/boot/head.S * - * initial boot stuff.. + * initial bootloader stuff.. */ #include <asm/system.h> #define halt .long PAL_halt -/* - * NOTE! The console bootstrap will load us at 0x20000000, but this image - * is linked to run at START_ADDR, so the first thing we do is to move - * ourself up to the right address.. We'd better be position-independent - * at that stage :-) - */ .set noreorder .globl __start .ent __start __start: bis $31,$31,$31 - br $1,$200 - .long START_ADDR, START_ADDR >> 32 /* strange bug in the assembler.. duh */ - .long START_SIZE, START_SIZE >> 32 -$200: ldq $30,0($1) /* new stack - below this */ - lda $2,-8($1) /* __start */ - bis $30,$30,$3 /* new address */ - subq $3,$2,$6 /* difference */ - ldq $4,8($1) /* size */ -$201: subq $4,8,$4 - ldq $5,0($2) - addq $2,8,$2 - stq $5,0($3) - addq $3,8,$3 - bne $4,$201 - br $1,$202 -$202: addq $1,$6,$1 - addq $1,12,$1 /* $203 in the new address space */ - jmp $31,($1),$203 -$203: br $27,$100 -$100: ldgp $29,0($27) + br 1f + /* room for the initial PCB, which comes here */ + .quad 0,0,0,0,0,0,0,0 +1: br $27,2f +2: ldgp $29,0($27) lda $27,start_kernel jsr $26,($27),start_kernel halt @@ -109,3 +88,44 @@ switch_to_osf_pal: __do_swppal: .long PAL_swppal .end switch_to_osf_pal + +.globl dispatch +.ent dispatch +dispatch: + subq $30,80,$30 + stq $26,0($30) + stq $29,8($30) + + stq $8,16($30) + stq $9,24($30) + stq $10,32($30) + stq $11,40($30) + stq $12,48($30) + stq $13,56($30) + stq $14,64($30) + stq $15,72($30) + + lda $1,0x10000000 /* hwrpb */ + ldq $2,0xc0($1) /* crb offset */ + addq $2,$1,$2 /* crb */ + ldq $27,0($2) /* dispatch procedure value */ + + ldq $2,8($27) /* dispatch call address */ + jsr $26,($2) /* call it (weird VMS call seq) */ + + ldq $26,0($30) + ldq $29,8($30) + + ldq $8,16($30) + ldq $9,24($30) + ldq $10,32($30) + ldq $11,40($30) + ldq $12,48($30) + ldq $13,56($30) + ldq $14,64($30) + ldq $15,72($30) + + addq $30,80,$30 + ret $31,($26) +.end dispatch + diff --git a/arch/alpha/boot/main.c b/arch/alpha/boot/main.c new file mode 100644 index 000000000..d122eb6f6 --- /dev/null +++ b/arch/alpha/boot/main.c @@ -0,0 +1,217 @@ +/* + * arch/alpha/boot/main.c + * + * Copyright (C) 1994, 1995 Linus Torvalds + * + * This file is the bootloader for the Linux/AXP kernel + */ +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/version.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/console.h> +#include <asm/hwrpb.h> + +#include <stdarg.h> + +extern int vsprintf(char *, const char *, va_list); +extern unsigned long switch_to_osf_pal(unsigned long nr, + struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, + unsigned long vptb, unsigned long *kstk); + +int printk(const char * fmt, ...) +{ + va_list args; + int i; + static char buf[1024]; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + puts(buf,i); + return i; +} + +#define hwrpb (*INIT_HWRPB) + +/* + * Find a physical address of a virtual object.. + * + * This is easy using the virtual page table address. + */ +struct pcb_struct * find_pa(unsigned long *vptb, struct pcb_struct * pcb) +{ + unsigned long address = (unsigned long) pcb; + unsigned long result; + + result = vptb[address >> 13]; + result >>= 32; + result <<= 13; + result |= address & 0x1fff; + return (struct pcb_struct *) result; +} + +/* + * This function moves into OSF/1 pal-code, and has a temporary + * PCB for that. The kernel proper should replace this PCB with + * the real one as soon as possible. + * + * The page table muckery in here depends on the fact that the boot + * code has the L1 page table identity-map itself in the second PTE + * in the L1 page table. Thus the L1-page is virtually addressable + * itself (through three levels) at virtual address 0x200802000. + * + * As we don't want it there anyway, we also move the L1 self-map + * up as high as we can, so that the last entry in the L1 page table + * maps the page tables. + * + * As a result, the OSF/1 pal-code will instead use a virtual page table + * map located at 0xffffffe00000000. + */ +#define pcb_va ((struct pcb_struct *) 0x20000000) +#define old_vptb (0x0000000200000000UL) +#define new_vptb (0xfffffffe00000000UL) +void pal_init(void) +{ + unsigned long i, rev, sum; + unsigned long *L1, *l; + struct percpu_struct * percpu; + struct pcb_struct * pcb_pa; + + /* Find the level 1 page table and duplicate it in high memory */ + L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */ + L1[1023] = L1[1]; + + percpu = (struct percpu_struct *) (hwrpb.processor_offset + (unsigned long) &hwrpb), + + pcb_va->ksp = 0; + pcb_va->usp = 0; + pcb_va->ptbr = L1[1] >> 32; + pcb_va->asn = 0; + pcb_va->pcc = 0; + pcb_va->unique = 0; + pcb_va->flags = 1; + pcb_pa = find_pa((unsigned long *) old_vptb, pcb_va); + printk("Switching to OSF PAL-code .. "); + /* + * a0 = 2 (OSF) + * a1 = return address, but we give the asm the virtual addr of the PCB + * a2 = physical addr of PCB + * a3 = new virtual page table pointer + * a4 = KSP (but we give it 0, asm sets it) + */ + i = switch_to_osf_pal( + 2, + pcb_va, + pcb_pa, + new_vptb, + 0); + if (i) { + printk("failed, code %ld\n", i); + halt(); + } + rev = percpu->pal_revision = percpu->palcode_avail[2]; + + hwrpb.vptb = new_vptb; + + /* update checksum: */ + sum = 0; + for (l = (unsigned long *) &hwrpb; l < (unsigned long *) &hwrpb.chksum; ++l) + sum += *l; + hwrpb.chksum = sum; + + printk("Ok (rev %lx)\n", rev); + /* remove the old virtual page-table mapping */ + L1[1] = 0; + invalidate_all(); +} + +extern int _end; + +static inline long openboot(void) +{ + char bootdev[256]; + long result; + + result = dispatch(CCB_GET_ENV, ENV_BOOTED_DEV, bootdev, 255); + if (result < 0) + return result; + return dispatch(CCB_OPEN, bootdev, result & 255); +} + +static inline long close(long dev) +{ + return dispatch(CCB_CLOSE, dev); +} + +static inline long load(long dev, unsigned long addr, unsigned long count) +{ + char bootfile[256]; + long result; + + result = dispatch(CCB_GET_ENV, ENV_BOOTED_FILE, bootfile, 255); + if (result < 0) + return result; + result &= 255; + bootfile[result] = '\0'; + if (result) + printk("Boot file specification (%s) not implemented\n", bootfile); + return dispatch(CCB_READ, dev, count, addr, BOOT_SIZE/512 + 1); +} + +/* + * Start the kernel. + */ +static void runkernel(void) +{ + __asm__ __volatile__( + "bis %1,%1,$30\n\t" + "bis %0,%0,$26\n\t" + "ret ($26)" + : /* no outputs: it doesn't even return */ + : "r" (START_ADDR), + "r" (PAGE_SIZE + INIT_STACK)); +} + +void start_kernel(void) +{ + long i; + long dev; + int nbytes; + char envval[256]; + + printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n"); + if (hwrpb.pagesize != 8192) { + printk("Expected 8kB pages, got %ldkB\n", hwrpb.pagesize >> 10); + return; + } + pal_init(); + dev = openboot(); + if (dev < 0) { + printk("Unable to open boot device: %016lx\n", dev); + return; + } + dev &= 0xffffffff; + printk("Loading vmlinux ..."); + i = load(dev, START_ADDR, START_SIZE); + close(dev); + if (i != START_SIZE) { + printk("Failed (%lx)\n", i); + return; + } + + nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS, + envval, sizeof(envval)); + if (nbytes > 0) { + envval[nbytes] = '\0'; + strcpy((char*)ZERO_PGE, envval); + } + + printk(" Ok\nNow booting the kernel\n"); + runkernel(); + for (i = 0 ; i < 0x100000000 ; i++) + /* nothing */; + halt(); +} diff --git a/arch/alpha/boot/tools/build.c b/arch/alpha/boot/tools/build.c new file mode 100644 index 000000000..040655a54 --- /dev/null +++ b/arch/alpha/boot/tools/build.c @@ -0,0 +1,173 @@ +/* + * arch/alpha/boot/tools/build.c + * + * Build a bootable image from the vmlinux binary + */ +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +#include <a.out.h> + +#include <asm/system.h> + +#define MAXSECT 10 +#define MAXBUF 8192 + +int verbose = 0; +int pad = 0; +char * program = "tools/build"; +char buffer[MAXBUF]; +unsigned long bootblock[64]; +struct filehdr fhdr; +struct aouthdr ahdr; +struct scnhdr shdr[MAXSECT]; + +char * usage = "'build [-b] system > secondary' or 'build > primary'"; + +static void die(char * str) +{ + fprintf(stderr,"%s: %s\n", program, str); + exit(1); +} + +static int comp(struct scnhdr * a, struct scnhdr * b) +{ + return a->s_vaddr - b->s_vaddr; +} + +int main(int argc, char ** argv) +{ + int fd, i; + unsigned long tmp, start; + unsigned long system_start, system_size; + char * infile = NULL; + + system_start = START_ADDR; + system_size = START_SIZE; + if (argc) { + program = *(argv++); + argc--; + } + while (argc > 0) { + if (**argv == '-') { + while (*++*argv) { + switch (**argv) { + case 'b': + system_start = BOOT_ADDR; + system_size = BOOT_SIZE; + pad = 1; + break; + case 'v': + verbose++; + break; + default: + die(usage); + } + } + } else if (infile) + die(usage); + else + infile = *argv; + argv++; + argc--; + } + if (!infile) { + memcpy(bootblock, "Linux Test", 10); + bootblock[60] = BOOT_SIZE / 512; /* count */ + bootblock[61] = 1; /* starting LBM */ + bootblock[62] = 0; /* flags */ + tmp = 0; + for (i = 0 ; i < 63 ; i++) + tmp += bootblock[i]; + bootblock[63] = tmp; + if (write(1, (char *) bootblock, 512) != 512) { + perror("bbwrite"); + exit(1); + } + return 0; + } + fd = open(infile, O_RDONLY); + if (fd < 0) { + perror(infile); + exit(1); + } + if (read(fd, &fhdr, sizeof(struct filehdr)) != sizeof(struct filehdr)) + die("unable to read file header"); + if (fhdr.f_nscns > MAXSECT) + die("Too many sections"); + if (fhdr.f_opthdr != AOUTHSZ) + die("optional header doesn't look like a.out"); + if (read(fd, &ahdr, sizeof(struct aouthdr)) != sizeof(struct aouthdr)) + die("unable to read a.out header"); + for (i = 0 ; i < fhdr.f_nscns ; i++) { + if (read(fd, i+shdr, sizeof(struct scnhdr)) != sizeof(struct scnhdr)) + die("unable to read section header"); + if (shdr[i].s_paddr != shdr[i].s_vaddr) + die("unable to handle different phys/virt addresses"); + if (shdr[i].s_relptr) + die("Unable to handle relocation info"); + if (verbose) { + fprintf(stderr, "section %d (%.8s):\t%lx - %lx (at %x)\n", + i, shdr[i].s_name, + shdr[i].s_vaddr, + shdr[i].s_vaddr + shdr[i].s_size, + shdr[i].s_scnptr); + } + } + qsort(shdr, fhdr.f_nscns, sizeof(shdr[1]), comp); + start = system_start; + for (i = 0 ; i < fhdr.f_nscns ; i++) { + unsigned long size, offset; + memset(buffer, 0, MAXBUF); + if (!strcmp(shdr[i].s_name, ".comment")) + continue; + if (shdr[i].s_vaddr != start) + die("Unordered or badly placed segments"); + size = shdr[i].s_size; + start += size; + offset = shdr[i].s_scnptr; + if (lseek(fd, offset, SEEK_SET) != offset) + die("Unable to seek in in-file"); + while (size > 0) { + unsigned long num = size; + if (num > MAXBUF) + num = MAXBUF; + if (offset) + if (read(fd, buffer, num) != num) + die("partial read"); + if (write(1, buffer, num) != num) + die("partial write"); + size -= num; + } + if (verbose) { + fprintf(stderr, "section %d (%.8s):\t%lx - %lx (at %x)\n", + i, shdr[i].s_name, + shdr[i].s_vaddr, + shdr[i].s_vaddr + shdr[i].s_size, + shdr[i].s_scnptr); + } + } + if (start > system_start + system_size) { + fprintf(stderr, "Boot image too large\n"); + exit(1); + } + if (pad) { + unsigned long count = (system_start + system_size) - start; + memset(buffer, 0, MAXBUF); + while (count > 0) { + int i = MAXBUF; + if (i > count) + i = count; + i = write(1, buffer, i); + if (i <= 0) { + perror("pad write"); + exit(1); + } + count -= i; + } + } + + return 0; +} diff --git a/arch/alpha/config.in b/arch/alpha/config.in index fef727364..8906e8c8c 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -5,17 +5,38 @@ comment 'General setup' -bool 'Normal harddisk support' CONFIG_BLK_DEV_HD n +bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y +bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 n +if [ "$CONFIG_ST506" = "y" ]; then + comment 'Please see drivers/block/README.ide for help/info on IDE drives' + bool ' Use old (reliable) 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 'System V IPC' CONFIG_SYSVIPC y +bool 'PCI alpha motherboard' CONFIG_PCI n +bool 'System V IPC' CONFIG_SYSVIPC n bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y +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 +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 comment '(it is safe to leave these untouched)' bool 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n bool 'Reverse ARP' CONFIG_INET_RARP n @@ -23,6 +44,7 @@ bool 'Assume subnets are local' CONFIG_INET_SNARL y bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n fi bool 'The IPX protocol' CONFIG_IPX n +#bool 'Appletalk DDP' CONFIG_ATALK n #bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n fi @@ -38,10 +60,10 @@ 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 n -bool 'Scsi generic support' CONFIG_CHR_DEV_SG n +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 'SCSI low-level drivers' @@ -50,18 +72,21 @@ 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 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n -bool 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 n +if [ "$CONFIG_PCI" = "y" ]; then + bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +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 'EISA EATA support' CONFIG_SCSI_EATA 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 fi @@ -79,18 +104,19 @@ else bool 'Dummy net driver support' CONFIG_DUMMY n bool 'SLIP (serial line) support' CONFIG_SLIP n if [ "$CONFIG_SLIP" = "y" ]; then - bool ' CSLIP compressed headers' SL_COMPRESSED y + bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y + bool ' 16 channels instead of 4' SL_SLIP_LOTS n # bool ' SLIP debugging on' SL_DUMP y fi bool 'PPP (point-to-point) support' CONFIG_PPP n bool 'PLIP (parallel port) support' CONFIG_PLIP n -bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING 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 n if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then bool '3c501 support' CONFIG_EL1 n @@ -101,42 +127,53 @@ if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then fi bool '3c509/3c579 support' CONFIG_EL3 y fi -bool 'Other ISA cards' CONFIG_NET_ISA y +bool 'Other ISA cards' CONFIG_NET_ISA n if [ "$CONFIG_NET_ISA" = "y" ]; then - bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n - bool 'Cabletron E21xx support (not recommended)' CONFIG_E2100 n + bool 'Cabletron E21xx support' CONFIG_E2100 n bool 'DEPCA support' CONFIG_DEPCA y bool 'EtherWorks 3 support' CONFIG_EWRK3 n if [ "$CONFIG_NET_ALPHA" = "y" ]; then - bool 'EtherExpress support' CONFIG_EEXPRESS n + bool 'Arcnet support' CONFIG_ARCNET n 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 support' CONFIG_HPLAN n - bool 'HP PCLAN PLUS support' CONFIG_HPLAN_PLUS n + 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 n bool 'SK_G16 support' CONFIG_SK_G16 n fi -bool 'EISA and on board controllers' CONFIG_NET_EISA n +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 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n - bool 'Zenith Z-Note support' CONFIG_ZNET 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 fi fi -comment 'CD-ROM drivers' +comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n -bool 'Mitsumi CDROM driver support' CONFIG_MCD 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 @@ -147,6 +184,8 @@ if [ "$CONFIG_SBPCD" = "y" ]; then fi fi fi +bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n +bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n comment 'Filesystems' @@ -162,7 +201,7 @@ 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" ]; then +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 @@ -172,6 +211,7 @@ bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n comment 'character devices' +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 y @@ -180,7 +220,7 @@ bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y fi bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n -bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION n + bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n if [ "$CONFIG_QIC02_TAPE" = "y" ]; then @@ -210,6 +250,9 @@ comment 'Kernel hacking' #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n bool 'Kernel profiling support' CONFIG_PROFILE n +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 +bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y fi diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile new file mode 100644 index 000000000..2843d4481 --- /dev/null +++ b/arch/alpha/kernel/Makefile @@ -0,0 +1,47 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# 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.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +OBJS = entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \ + lca.o bios32.o + +all: kernel.o head.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/asm-alpha/system.h + $(CPP) -traditional -o $*.s $< + +kernel.o: $(OBJS) + $(LD) -r -o kernel.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/alpha/kernel/bios32.c b/arch/alpha/kernel/bios32.c new file mode 100644 index 000000000..37c566fb1 --- /dev/null +++ b/arch/alpha/kernel/bios32.c @@ -0,0 +1,478 @@ +#define DEBUG +/* + * bios32.c - PCI BIOS functions for Alpha systems not using BIOS + * emulation code. + * + * Written by Dave Rusling (david.rusling@reo.mts.dec.com) + * + * Adapted to 64-bit kernel and then rewritten by David Mosberger + * (davidm@cs.arizona.edu) + * + * For more information, please consult + * + * PCI BIOS Specification Revision + * PCI Local Bus Specification + * PCI System Design Guide + * + * PCI Special Interest Group + * M/S HF3-15A + * 5200 N.E. Elam Young Parkway + * Hillsboro, Oregon 97124-6497 + * +1 (503) 696-2000 + * +1 (800) 433-5177 + * + * Manuals are $25 each or $50 for all three, plus $7 shipping + * within the United States, $35 abroad. + */ +#include <linux/config.h> + +#ifndef CONFIG_PCI + +int pcibios_present(void) +{ + return 0; +} + +#else /* CONFIG_PCI */ + +#include <linux/kernel.h> +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/malloc.h> +#include <linux/mm.h> + +#include <asm/hwrpb.h> +#include <asm/io.h> + + +#define KB 1024 +#define MB (1024*KB) +#define GB (1024*MB) + +#define MAJOR_REV 0 +#define MINOR_REV 2 + +/* + * Align VAL to ALIGN, which must be a power of two. + */ +#define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1)) + + +/* + * Temporary internal macro. If this 0, then do not write to any of + * the PCI registers, merely read them (i.e., use configuration as + * determined by SRM). The SRM seem do be doing a less than perfect + * job in configuring PCI devices, so for now we do it ourselves. + * Reconfiguring PCI devices breaks console (RPB) callbacks, but + * those don't work properly with 64 bit addresses anyways. + * + * The accepted convention seems to be that the console (POST + * software) should fully configure boot devices and configure the + * interrupt routing of *all* devices. In particular, the base + * addresses of non-boot devices need not be initialized. For + * example, on the AXPpci33 board, the base address a #9 GXE PCI + * graphics card reads as zero (this may, however, be due to a bug in + * the graphics card---there have been some rumor that the #9 BIOS + * incorrectly resets that address to 0...). + */ +#define PCI_MODIFY 1 + + +extern struct hwrpb_struct *hwrpb; + + +#if PCI_MODIFY + +static unsigned int io_base = 64*KB; /* <64KB are (E)ISA ports */ +static unsigned int mem_base = 16*MB; /* <16MB is ISA memory */ + + +/* + * Layout memory and I/O for a device: + */ +static void layout_dev(struct pci_dev *dev) +{ + struct pci_bus *bus; + unsigned short cmd; + unsigned int base, mask, size, reg; + + bus = dev->bus; + pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd); + + for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + /* + * Figure out how much space and of what type this + * device wants. + */ + pcibios_write_config_dword(bus->number, dev->devfn, reg, + 0xffffffff); + pcibios_read_config_dword(bus->number, dev->devfn, reg, &base); + if (!base) { + break; /* done with this device */ + } + /* + * We've read the base address register back after + * writing all ones and so now we must decode it. + */ + if (base & PCI_BASE_ADDRESS_SPACE_IO) { + /* + * I/O space base address register. + */ + cmd |= PCI_COMMAND_IO; + + base &= PCI_BASE_ADDRESS_IO_MASK; + mask = (~base << 1) | 0x1; + size = (mask & base) & 0xffffffff; + base = ALIGN(io_base, size); + io_base = base + size; + pcibios_write_config_dword(bus->number, dev->devfn, + reg, base | 0x1); + } else { + unsigned int type; + /* + * Memory space base address register. + */ + cmd |= PCI_COMMAND_MEMORY; + + type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK; + base &= PCI_BASE_ADDRESS_MEM_MASK; + mask = (~base << 1) | 0x1; + size = (mask & base) & 0xffffffff; + switch (type) { + case PCI_BASE_ADDRESS_MEM_TYPE_32: + break; + + case PCI_BASE_ADDRESS_MEM_TYPE_64: + printk("bios32 WARNING: " + "ignoring 64-bit device in " + "slot %d, function %d: \n", + PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + reg += 4; /* skip extra 4 bytes */ + continue; + + case PCI_BASE_ADDRESS_MEM_TYPE_1M: + /* + * Allocating memory below 1MB is *very* + * tricky, as there may be all kinds of + * ISA devices lurking that we don't know + * about. For now, we just cross fingers + * and hope nobody tries to do this on an + * Alpha (or that the console has set it + * up properly). + */ + printk("bios32 WARNING: slot %d, function %d " + "requests memory below 1MB---don't " + "know how to do that.\n", + PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + continue; + } + /* + * The following holds at least for the Low Cost + * Alpha implementation of the PCI interface: + * + * In sparse memory address space, the first + * octant (16MB) of every 128MB segment is + * aliased to the the very first 16MB of the + * address space (i.e., it aliases the ISA + * memory address space). Thus, we try to + * avoid allocating PCI devices in that range. + * Can be allocated in 2nd-7th octant only. + * Devices that need more than 112MB of + * address space must be accessed through + * dense memory space only! + */ + base = ALIGN(mem_base, size); + if (size > 7 * 16*MB) { + printk("bios32 WARNING: slot %d, function %d " + "requests %dB of contiguous address " + " space---don't use sparse memory " + " accesses on this device!!\n", + PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), size); + } else { + if (((base / 16*MB) & 0x7) == 0) { + base &= ~(128*MB - 1); + base += 16*MB; + base = ALIGN(base, size); + } + if (base / 128*MB != (base + size) / 128*MB) { + base &= ~(128*MB - 1); + base += (128 + 16)*MB; + base = ALIGN(base, size); + } + } + mem_base = base + size; + pcibios_write_config_dword(bus->number, dev->devfn, + reg, base); + } + } + /* enable device: */ + pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND, + cmd | PCI_COMMAND_MASTER); +} + + +static void layout_bus(struct pci_bus *bus) +{ + unsigned int l, tio, bio, tmem, bmem; + struct pci_bus *child; + struct pci_dev *dev; + + if (!bus->devices && !bus->children) + return; + + /* + * Align the current bases on appropriate boundaries (4K for + * IO and 1MB for memory). + */ + bio = io_base = ALIGN(io_base, 4*KB); + bmem = mem_base = ALIGN(mem_base, 1*MB); + + /* + * Allocate space to each device: + */ + for (dev = bus->devices; dev; dev = dev->sibling) { + if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) { + layout_dev(dev); + } + } + /* + * Recursively allocate space for all of the sub-buses: + */ + for (child = bus->children; child; child = child->next) { + layout_bus(child); + } + /* + * Align the current bases on 4K and 1MB boundaries: + */ + tio = io_base = ALIGN(io_base, 4*KB); + tmem = mem_base = ALIGN(mem_base, 1*MB); + + if (bus->self) { + struct pci_dev *bridge = bus->self; + /* + * Set up the top and bottom of the I/O memory segment + * for this bus. + */ + pcibios_read_config_dword(bridge->bus->number, bridge->devfn, + 0x1c, &l); + l = l | (bio >> 8) | ((tio - 1) & 0xf000); + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x1c, l); + + l = ((bmem & 0xfff00000) >> 16) | ((tmem - 1) & 0xfff00000); + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x20, l); + /* + * Turn off downstream PF memory address range: + */ + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x24, 0x0000ffff); + /* + * Tell bridge that there is an ISA bus in the system: + */ + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x3c, 0x00040000); + /* + * Clear status bits, enable I/O (for downstream I/O), + * turn on master enable (for upstream I/O), turn on + * memory enable (for downstream memory), turn on + * master enable (for upstream memory and I/O). + */ + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x4, 0xffff0007); + } +} + +#endif /* !PCI_MODIFY */ + + +/* + * Given the vendor and device ids, find the n'th instance of that device + * in the system. + */ +int pcibios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, + unsigned char *devfn) +{ + unsigned int current = 0; + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->vendor == vendor && dev->device == device_id) { + if (current == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++current; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + + +/* + * Given the class, find the n'th instance of that device + * in the system. + */ +int pcibios_find_class (unsigned int class_code, unsigned short index, + unsigned char *bus, unsigned char *devfn) +{ + unsigned int current = 0; + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->class == class_code) { + if (current == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++current; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + + +int pcibios_present(void) +{ + return 1; +} + + +unsigned long pcibios_init(unsigned long mem_start, + unsigned long mem_end) +{ + printk("Alpha PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV); + +#if !PCI_MODIFY + printk("...NOT modifying existing (SRM) PCI configuration\n"); +#endif + return mem_start; +} + + +/* + * Fixup configuration for Noname boards (AXPpci33). + */ +static void noname_fixup(void) +{ + struct pci_dev *dev; + + /* + * The Noname board has 5 PCI slots with each of the 4 + * interrupt pins routed to different pins on the PCI/ISA + * bridge (PIRQ0-PIRQ3). I don't have any information yet as + * to how INTB, INTC, and INTD get routed (4/12/95, + * davidm@cs.arizona.edu). + */ + static const char pirq_tab[5][4] = { + { 3, -1, -1, -1}, /* slot 6 (53c810) */ + {-1, -1, -1, -1}, /* slot 7 (PCI/ISA bridge) */ + { 2, -1, -1, -1}, /* slot 8 (slot closest to ISA) */ + { 1, -1, -1, -1}, /* slot 9 (middle slot) */ + { 0, -1, -1, -1}, /* slot 10 (slot furthest from ISA) */ + }; + /* + * route_tab selects irq routing in PCI/ISA bridge so that: + * PIRQ0 -> irq 15 + * PIRQ1 -> irq 9 + * PIRQ2 -> irq 10 + * PIRQ3 -> irq 11 + */ + const unsigned int route_tab = 0x0b0a090f; + unsigned char pin; + int pirq; + + pcibios_write_config_dword(0, PCI_DEVFN(7, 0), 0x60, route_tab); + + /* ensure irq 9, 10, 11, and 15 are level sensitive: */ + outb((1<<(9-8)) | (1<<(10-8)) | (1<<(11-8)) | (1<<(15-8)), 0x4d1); + + /* + * Go through all devices, fixing up irqs as we see fit: + */ + for (dev = pci_devices; dev; dev = dev->next) { + dev->irq = 0; + if (dev->bus->number != 0 || + PCI_SLOT(dev->devfn) < 6 || PCI_SLOT(dev->devfn) > 10) + { + printk("noname_set_irq: no dev on bus %d, slot %d!!\n", + dev->bus->number, PCI_SLOT(dev->devfn)); + continue; + } + + pcibios_read_config_byte(dev->bus->number, dev->devfn, + PCI_INTERRUPT_PIN, &pin); + if (!pin) { + if (dev->vendor == PCI_VENDOR_ID_S3 && + (dev->device == PCI_DEVICE_ID_S3_864_1 || + dev->device == PCI_DEVICE_ID_S3_864_2)) + { + pin = 1; + } else { + continue; /* no interrupt line */ + } + } + pirq = pirq_tab[PCI_SLOT(dev->devfn) - 6][pin - 1]; + if (pirq < 0) { + continue; + } + dev->irq = (route_tab >> (8 * pirq)) & 0xff; +#if PCI_MODIFY + /* tell the device: */ + pcibios_write_config_byte(dev->bus->number, dev->devfn, + PCI_INTERRUPT_LINE, dev->irq); +#endif + } + +#if PCI_MODIFY + { + unsigned char hostid; + /* + * SRM console version X3.9 seems to reset the SCSI + * host-id to 0 no matter what console environment + * variable pka0_host_id is set to. Thus, if the + * host-id reads out as a zero, we set it to 7. The + * SCSI controller is on the motherboard on bus 0, + * slot 6 + */ + if (pcibios_read_config_byte(0, PCI_DEVFN(6, 0), 0x84, &hostid) + == PCIBIOS_SUCCESSFUL && (hostid == 0)) + { + pcibios_write_config_byte(0, PCI_DEVFN(6, 0), + 0x84, 7); + } + } +#endif /* !PCI_MODIFY */ +} + + +unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end) +{ +#if PCI_MODIFY + /* + * Scan the tree, allocating PCI memory and I/O space. + */ + layout_bus(&pci_root); +#endif + + /* + * Now is the time to do all those dirty little deeds... + */ + switch (hwrpb->sys_type) { + case ST_DEC_AXPPCI_33: noname_fixup(); break; + + default: + printk("pcibios_fixup: don't know how to fixup sys type %ld\n", + hwrpb->sys_type); + break; + } + return mem_start; +} + +#endif /* CONFIG_PCI */ diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S new file mode 100644 index 000000000..b54ea220b --- /dev/null +++ b/arch/alpha/kernel/entry.S @@ -0,0 +1,554 @@ +/* + * alpha/entry.S + * + * kernel entry-points + */ + +#include <asm/system.h> + +#define halt .long PAL_halt +#define rti .long PAL_rti + +#define NR_SYSCALLS 310 +#define osf_vfork sys_fork + +/* + * These offsets must match with "struct hae" in io.h: + */ +#define HAE_CACHE 0 +#define HAE_REG 8 + +/* + * stack offsets + */ +#define SP_OFF 160 + +#define SWITCH_STACK_SIZE 320 + +/* + * task structure offsets + */ +#define TASK_STATE 0 +#define TASK_COUNTER 8 +#define TASK_PRIORITY 16 +#define TASK_SIGNAL 24 +#define TASK_BLOCKED 32 +#define TASK_FLAGS 40 + +/* + * This defines the normal kernel pt-regs layout. + * + * regs 9-15 preserved by C code + * regs 16-18 saved by PAL-code + * regs 29-30 saved and set up by PAL-code + */ +#define SAVE_ALL \ + subq $30,160,$30; \ + stq $0,0($30); \ + stq $1,8($30); \ + stq $2,16($30); \ + stq $3,24($30); \ + stq $4,32($30); \ + stq $5,40($30); \ + stq $6,48($30); \ + stq $7,56($30); \ + stq $8,64($30); \ + stq $19,72($30); \ + stq $20,80($30); \ + stq $21,88($30); \ + stq $22,96($30); \ + stq $23,104($30); \ + stq $24,112($30); \ + stq $25,120($30); \ + stq $26,128($30); \ + stq $27,136($30); \ + stq $28,144($30); \ + lda $2,hae; \ + ldq $2,HAE_CACHE($2); \ + stq $2,152($30) + +#define RESTORE_ALL \ + lda $8,hae; \ + ldq $7,HAE_CACHE($8); \ + ldq $6,152($30); \ + subq $7,$6,$5; \ + beq $5,99f; \ + ldq $7,HAE_REG($8); \ + addq $31,7,$16; \ + call_pal PAL_swpipl; \ + stq $6,HAE_CACHE($8); \ + stq $6,0($7); \ + mb; \ + bis $0,$0,$16; \ + call_pal PAL_swpipl; \ +99:; \ + ldq $0,0($30); \ + ldq $1,8($30); \ + ldq $2,16($30); \ + ldq $3,24($30); \ + ldq $4,32($30); \ + ldq $5,40($30); \ + ldq $6,48($30); \ + ldq $7,56($30); \ + ldq $8,64($30); \ + ldq $19,72($30); \ + ldq $20,80($30); \ + ldq $21,88($30); \ + ldq $22,96($30); \ + ldq $23,104($30); \ + ldq $24,112($30); \ + ldq $25,120($30); \ + ldq $26,128($30); \ + ldq $27,136($30); \ + ldq $28,144($30); \ + addq $30,160,$30 + +.text +.set noat + +.align 3 +.globl entInt +.ent entInt +entInt: + SAVE_ALL +/* start atomic operation with respect to software interrupts */ + lda $0,intr_count + ldq $1,0($0) + addq $1,1,$1 + stq $1,0($0) +/* set up the arguments to the C interrupt handler */ + lda $27,do_entInt + jsr $26,($27),do_entInt +/* ok, check if we need to do software interrupts */ +1: lda $0,intr_count + ldq $1,0($0) + subq $1,1,$1 + bne $1,2f /* interrupt within interrupt: return now */ + lda $2,bh_active + ldq $3,0($2) + lda $2,bh_mask + ldq $2,0($2) + and $2,$3,$2 + bne $2,3f + stq $1,0($0) + br $31,ret_from_sys_call +.align 3 +2: stq $1,0($0) + br $31,restore_all +.align 3 +3: lda $27,do_bottom_half + jsr $26,($27),do_bottom_half + br $31,1b +.end entInt + +.align 3 +.globl entMM +.ent entMM +entMM: + SAVE_ALL + lda $27,do_page_fault + lda $26,ret_from_sys_call + jsr $31,($27),do_page_fault +.end entMM + +.align 3 +.globl entArith +.ent entArith +entArith: + SAVE_ALL + lda $27,do_entArith + lda $26,ret_from_sys_call + jsr $31,($27),do_entArith +.end entArith + +.align 3 +.globl entIF +.ent entIF +entIF: + SAVE_ALL + lda $27,do_entIF + lda $26,ret_from_sys_call + jsr $31,($27),do_entIF +.end entIF + +/* + * Fork() is one of the special system calls: it needs to + * save the callee-saved regs so that the regs can be found + * for the new process.. We save them in the "context switch" + * stack format (see arch/alpha/kernel/process.c). + * + * Also, for the kernel fork, we need to fake the system call + * stack buildup, as we can't do system calls from kernel space. + */ +.align 3 +.globl kernel_fork +.ent kernel_fork +kernel_fork: + subq $30,6*8,$30 + stq $31,0($30) + stq $26,8($30) + stq $29,16($30) + stq $16,24($30) + stq $17,32($30) + stq $18,40($30) + SAVE_ALL + lda $27,sys_fork + jsr $26,($27),sys_fork + br ret_from_sys_call +.end kernel_fork + +.align 3 +.ent do_switch_stack +do_switch_stack: + lda $30,-SWITCH_STACK_SIZE($30) + stq $9,0($30) + stq $10,8($30) + stq $11,16($30) + stq $12,24($30) + stq $13,32($30) + stq $14,40($30) + stq $15,48($30) + stq $26,56($30) + stt $f0,64($30) + stt $f1,72($30) + stt $f2,80($30) + stt $f3,88($30) + stt $f4,96($30) + stt $f5,104($30) + stt $f6,112($30) + stt $f7,120($30) + stt $f8,128($30) + stt $f9,136($30) + stt $f10,144($30) + stt $f11,152($30) + stt $f12,160($30) + stt $f13,168($30) + stt $f14,176($30) + stt $f15,184($30) + stt $f16,192($30) + stt $f17,200($30) + stt $f18,208($30) + stt $f19,216($30) + stt $f20,224($30) + stt $f21,232($30) + stt $f22,240($30) + stt $f23,248($30) + stt $f24,256($30) + stt $f25,264($30) + stt $f26,272($30) + stt $f27,280($30) + stt $f28,288($30) + stt $f29,296($30) + stt $f30,304($30) + ret $31,($0),1 +.end do_switch_stack + +.align 3 +.ent undo_switch_stack +undo_switch_stack: + ldq $9,0($30) + ldq $10,8($30) + ldq $11,16($30) + ldq $12,24($30) + ldq $13,32($30) + ldq $14,40($30) + ldq $15,48($30) + ldq $26,56($30) + ldt $f0,64($30) + ldt $f1,72($30) + ldt $f2,80($30) + ldt $f3,88($30) + ldt $f4,96($30) + ldt $f5,104($30) + ldt $f6,112($30) + ldt $f7,120($30) + ldt $f8,128($30) + ldt $f9,136($30) + ldt $f10,144($30) + ldt $f11,152($30) + ldt $f12,160($30) + ldt $f13,168($30) + ldt $f14,176($30) + ldt $f15,184($30) + ldt $f16,192($30) + ldt $f17,200($30) + ldt $f18,208($30) + ldt $f19,216($30) + ldt $f20,224($30) + ldt $f21,232($30) + ldt $f22,240($30) + ldt $f23,248($30) + ldt $f24,256($30) + ldt $f25,264($30) + ldt $f26,272($30) + ldt $f27,280($30) + ldt $f28,288($30) + ldt $f29,296($30) + ldt $f30,304($30) + lda $30,SWITCH_STACK_SIZE($30) + ret $31,($0),1 +.end undo_switch_stack + +.align 3 +.globl entUna +.ent entUna +entUna: + lda $30,-256($30) + stq $0,0($30) + stq $1,8($30) + stq $2,16($30) + stq $3,24($30) + stq $4,32($30) + stq $5,40($30) + stq $6,48($30) + stq $7,56($30) + stq $8,64($30) + stq $9,72($30) + stq $10,80($30) + stq $11,88($30) + stq $12,96($30) + stq $13,104($30) + stq $14,112($30) + stq $15,120($30) + /* 16-18 PAL-saved */ + stq $19,152($30) + stq $20,160($30) + stq $21,168($30) + stq $22,176($30) + stq $23,184($30) + stq $24,192($30) + stq $25,200($30) + stq $26,208($30) + stq $27,216($30) + stq $28,224($30) + stq $29,232($30) + stq $30,240($30) + stq $31,248($30) + lda $27,do_entUna + jsr $26,($27),do_entUna + ldq $0,0($30) + ldq $1,8($30) + ldq $2,16($30) + ldq $3,24($30) + ldq $4,32($30) + ldq $5,40($30) + ldq $6,48($30) + ldq $7,56($30) + ldq $8,64($30) + ldq $9,72($30) + ldq $10,80($30) + ldq $11,88($30) + ldq $12,96($30) + ldq $13,104($30) + ldq $14,112($30) + ldq $15,120($30) + /* 16-18 PAL-saved */ + ldq $19,152($30) + ldq $20,160($30) + ldq $21,168($30) + ldq $22,176($30) + ldq $23,184($30) + ldq $24,192($30) + ldq $25,200($30) + ldq $26,208($30) + ldq $27,216($30) + ldq $28,224($30) + ldq $29,232($30) + ldq $30,240($30) + lda $30,256($30) + rti +.end entUna + +.align 3 +.globl sys_fork +.ent sys_fork +sys_fork: + br $0,do_switch_stack + bis $30,$30,$16 + lda $27,alpha_fork + jsr $26,($27),alpha_fork + br $0,undo_switch_stack + ldq $0,0($30) + ret $31,($26),1 +.end sys_fork + +.align 3 +.globl alpha_switch_to +.ent alpha_switch_to +alpha_switch_to: + br $0,do_switch_stack + call_pal PAL_swpctx + br $0,undo_switch_stack + ret $31,($26),1 +.end alpha_switch_to + +/* + * Oh, well.. Disassembling OSF/1 binaries to find out how the + * system calls work isn't much fun. + * + * entSys is special in that the PAL-code doesn't save a0-a2, so + * we start off by doing that by hand. + */ +.align 3 +.globl entSys +.globl ret_from_sys_call +.ent entSys +entSys: + stq $16,24($30) + stq $17,32($30) + stq $18,40($30) + SAVE_ALL + lda $1,NR_SYSCALLS($31) + lda $2,sys_call_table + lda $27,do_entSys + cmpult $0,$1,$1 + s8addq $0,$2,$2 + beq $1,1f + ldq $27,0($2) +1: jsr $26,($27),do_entSys + bis $31,$31,$1 + bge $0,2f + bis $31,$31,$26 /* tell "ret_from_sys_call" that we can restart */ + ldq $19,0($30) /* .. with this syscall nr */ + ldq $20,72($30) /* .. and this a3 */ + addq $31,1,$1 /* set a3 for errno return */ + subq $31,$0,$0 /* with error in v0 */ +2: stq $0,0($30) + stq $1,72($30) /* a3 for return */ +.align 3 +ret_from_sys_call: + ldq $0,SP_OFF($30) + cmovne $26,0,$19 + and $0,8,$0 + beq $0,restore_all +ret_from_reschedule: + lda $0,need_resched + lda $1,current + ldl $2,0($0) + lda $4,init_task + ldq $3,0($1) + bne $2,reschedule + subq $4,$3,$4 + beq $4,restore_all + ldq $4,TASK_SIGNAL($3) + ldq $16,TASK_BLOCKED($3) + bic $4,$16,$4 + bne $4,signal_return +restore_all: + RESTORE_ALL + rti +.align 3 +signal_return: + bis $30,$30,$17 + br $0,do_switch_stack + bis $30,$30,$18 + lda $27,do_signal + jsr $26,($27),do_signal + lda $30,SWITCH_STACK_SIZE($30) + br $31,restore_all +.end entSys + +.align 3 +.ent reschedule +reschedule: + subq $30,16,$30 + stq $19,0($30) + stq $20,8($30) + lda $27,schedule + jsr $26,($27),schedule + ldq $19,0($30) + ldq $20,8($30) + addq $30,16,$30 + br $31,ret_from_reschedule +.end reschedule + +.align 3 +.ent sys_sigreturn +sys_sigreturn: + bis $30,$30,$17 + lda $30,-SWITCH_STACK_SIZE($30) + bis $30,$30,$18 + lda $27,do_sigreturn + jsr $26,($27),do_sigreturn + br $0,undo_switch_stack + br $31,ret_from_sys_call +.end sys_sigreturn + +.align 3 +.ent sys_sigsuspend +sys_sigsuspend: + bis $30,$30,$17 + br $0,do_switch_stack + bis $30,$30,$18 + lda $27,do_sigsuspend + jsr $26,($27),do_sigsuspend + lda $30,SWITCH_STACK_SIZE($30) + br $31,ret_from_sys_call +.end sys_sigreturn + + .align 3 + .globl sys_call_table +sys_call_table: +/*0*/ .quad do_entSys, sys_exit, sys_fork, sys_read, sys_write + .quad do_entSys, sys_close, sys_wait4, do_entSys, sys_link + .quad sys_unlink, do_entSys, sys_chdir, sys_fchdir, sys_mknod + .quad sys_chmod, sys_chown, sys_brk, do_entSys, sys_lseek + .quad sys_getxpid, osf_mount, osf_umount, sys_setuid, sys_getxuid + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, sys_access, do_entSys + .quad do_entSys, sys_sync, sys_kill, do_entSys, sys_setpgid + .quad do_entSys, sys_dup, sys_pipe, do_entSys, do_entSys + .quad sys_open, do_entSys, sys_getxgid, osf_sigprocmask, do_entSys +/*50*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, sys_ioctl + .quad do_entSys, do_entSys, sys_symlink, sys_readlink, sys_execve + .quad sys_umask, do_entSys, do_entSys, sys_getpgrp, sys_getpagesize + .quad do_entSys, osf_vfork, sys_newstat, sys_newlstat, do_entSys + .quad do_entSys, osf_mmap, do_entSys, sys_munmap, sys_mprotect + .quad sys_madvise, do_entSys, do_entSys, do_entSys, sys_getgroups + .quad do_entSys, do_entSys, do_entSys, sys_setitimer, do_entSys + .quad do_entSys, sys_getitimer, sys_gethostname, do_entSys, sys_getdtablesize + .quad sys_dup2, sys_newfstat, sys_fcntl, sys_select, do_entSys + .quad sys_fsync, do_entSys, sys_socket, do_entSys, do_entSys +/*100*/ .quad do_entSys, do_entSys, do_entSys, sys_sigreturn, sys_bind + .quad do_entSys, sys_listen, do_entSys, do_entSys, do_entSys + .quad do_entSys, sys_sigsuspend, do_entSys, do_entSys, do_entSys + .quad do_entSys, sys_gettimeofday, sys_getrusage, do_entSys, do_entSys + .quad do_entSys, do_entSys, sys_settimeofday, sys_fchown, sys_fchmod + .quad do_entSys, sys_setreuid, sys_setregid, sys_rename, sys_truncate + .quad sys_ftruncate, do_entSys, sys_setgid, do_entSys, do_entSys + .quad do_entSys, sys_mkdir, sys_rmdir, sys_utimes, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, sys_getrlimit + .quad sys_setrlimit, do_entSys, sys_setsid, do_entSys, do_entSys +/*150*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, sys_sigaction, do_entSys, do_entSys, osf_getdirentries + .quad osf_statfs, osf_fstatfs, do_entSys, do_entSys, do_entSys + .quad osf_getdomainname, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, osf_swapon +/*200*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, osf_utsname, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys +/*250*/ .quad do_entSys, osf_usleep_thread, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys +/* linux-specific system calls start at 300 */ +/*300*/ .quad sys_bdflush, sys_sethae, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys diff --git a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S new file mode 100644 index 000000000..2cff1159a --- /dev/null +++ b/arch/alpha/kernel/head.S @@ -0,0 +1,97 @@ +/* + * alpha/boot/head.S + * + * initial boot stuff.. At this point, the bootloader has already + * switched into OSF/1 PAL-code, and loaded us at the correct address + * (START_ADDR). So there isn't much left for us to do: just set up + * the kernel global pointer and jump to the kernel entry-point. + */ + +#define __ASSEMBLY__ +#include <asm/system.h> +#include <linux/fd.h> + +#define halt .long PAL_halt + +.globl swapper_pg_dir +swapper_pg_dir=SWAPPER_PGD + + .set noreorder + .globl __start + .ent __start +__start: + br $27,1f +1: ldgp $29,0($27) + lda $27,start_kernel + jsr $26,($27),start_kernel + halt + .end __start + + .align 3 + .globl wrent + .ent wrent +wrent: + .long PAL_wrent + ret ($26) + .end wrent + + .align 3 + .globl wrkgp + .ent wrkgp +wrkgp: + .long PAL_wrkgp + ret ($26) + .end wrkgp + + .align 3 + .globl wrusp + .ent wrusp +wrusp: + .long PAL_wrusp + ret ($26) + .end wrusp + + .align 3 + .globl rdusp + .ent rdusp +rdusp: + .long PAL_rdusp + ret ($26) + .end rdusp + + .align 3 + .globl tbi + .ent tbi +tbi: + .long PAL_tbi + ret ($26) + .end tbi + + .align 3 + .globl imb + .ent imb +imb: + .long PAL_imb + ret ($26) + .end imb + + .align 3 + .globl rdmces + .ent rdmces +rdmces: + call_pal PAL_rdmces + ret ($26) + .end rdmces + + .align 3 + .globl wrmces + .ent wrmces +wrmces: + call_pal PAL_wrmces + ret ($26) + .end wrmces + +.align 9 +.globl floppy_track_buffer +floppy_track_buffer: + .space 512*2*MAX_BUFFER_SECTORS,1 diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c new file mode 100644 index 000000000..a59077539 --- /dev/null +++ b/arch/alpha/kernel/irq.c @@ -0,0 +1,414 @@ +/* + * linux/arch/alpha/kernel/irq.c + * + * Copyright (C) 1995 Linus Torvalds + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +#include <linux/config.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/dma.h> + +static unsigned char cache_21 = 0xff; +static unsigned char cache_A1 = 0xff; + +void disable_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; + } + cli(); + cache_A1 |= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +void enable_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; + } + cli(); + cache_A1 &= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +/* + * 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 } +}; + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct irqaction * action = irq_action; + + for (i = 0 ; i < 16 ; i++, action++) { + if (!action->handler) + continue; + len += sprintf(buf+len, "%2d: %8d %c %s\n", + i, kstat.interrupts[i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + } + return len; +} + +static inline void ack_irq(int irq) +{ + /* ACK the interrupt making it the lowest priority */ + /* First the slave .. */ + if (irq > 7) { + outb(0xE0 | (irq - 8), 0xa0); + irq = 2; + } + /* .. then the master */ + outb(0xE0 | irq, 0x20); +} + +static inline void mask_irq(int irq) +{ + if (irq < 8) { + cache_21 |= 1 << irq; + outb(cache_21, 0x21); + } else { + cache_A1 |= 1 << (irq - 8); + outb(cache_A1, 0xA1); + } +} + +static inline void unmask_irq(unsigned long irq) +{ + if (irq < 8) { + cache_21 &= ~(1 << irq); + outb(cache_21, 0x21); + } else { + cache_A1 &= ~(1 << (irq - 8)); + outb(cache_A1, 0xA1); + } +} + +int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), + unsigned long irqflags, const char * devname) +{ + 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->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + if (irq < 8) { + if (irq) { + 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; +} + +void free_irq(unsigned int irq) +{ + struct irqaction * action = irq + irq_action; + 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(); + mask_irq(irq); + action->handler = NULL; + action->flags = 0; + action->mask = 0; + action->name = NULL; + restore_flags(flags); +} + +static void handle_nmi(struct pt_regs * regs) +{ + printk("Whee.. NMI received. Probable hardware error\n"); + printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461)); +} + +static void unexpected_irq(int irq, struct pt_regs * regs) +{ + int i; + + printk("IO device interrupt, irq = %d\n", irq); + printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps); + printk("Expecting: "); + for (i = 0; i < 16; i++) + if (irq_action[i].handler) + printk("[%s:%d] ", irq_action[i].name, i); + printk("\n"); + printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n", + inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa)); + outb(0x0c, 0x3fc); + outb(0x0c, 0x2fc); + outb(0,0x61); + outb(0,0x461); +} + +static inline void handle_irq(int irq, struct pt_regs * regs) +{ + struct irqaction * action = irq + irq_action; + + kstat.interrupts[irq]++; + if (!action->handler) { + unexpected_irq(irq, regs); + return; + } + action->handler(irq, regs); +} + +static void local_device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + switch (vector) { + /* com1: map to irq 4 */ + case 0x900: + handle_irq(4, regs); + return; + + /* com2: map to irq 3 */ + case 0x920: + handle_irq(3, regs); + return; + + /* keyboard: map to irq 1 */ + case 0x980: + handle_irq(1, regs); + return; + + /* mouse: map to irq 9 */ + case 0x990: + handle_irq(9, regs); + return; + default: + printk("Unknown local interrupt %lx\n", vector); + } +} + +/* + * The vector is 0x8X0 for EISA interrupt X, and 0x9X0 for the local + * motherboard interrupts.. This is for the Jensen. + * + * 0x660 - NMI + * + * 0x800 - IRQ0 interval timer (not used, as we use the RTC timer) + * 0x810 - IRQ1 line printer (duh..) + * 0x860 - IRQ6 floppy disk + * 0x8E0 - IRQ14 SCSI controller + * + * 0x900 - COM1 + * 0x920 - COM2 + * 0x980 - keyboard + * 0x990 - mouse + * + * The PCI version is more sane: it doesn't have the local interrupts at + * all, and has only normal PCI interrupts from devices. Happily it's easy + * enough to do a sane mapping from the Jensen.. Note that this means + * that we may have to do a hardware "ack" to a different interrupt than + * we report to the rest of the world.. + */ +static void device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + int irq, ack; + struct irqaction * action; + + if (vector == 0x660) { + handle_nmi(regs); + return; + } + + ack = irq = (vector - 0x800) >> 4; +#ifndef CONFIG_PCI + if (vector >= 0x900) { + local_device_interrupt(vector, regs); + return; + } + /* irq1 is supposed to be the keyboard, silly Jensen */ + if (irq == 1) + irq = 7; +#endif + kstat.interrupts[irq]++; + action = irq_action + irq; + /* quick interrupts get executed with no extra overhead */ + if (action->flags & SA_INTERRUPT) { + action->handler(irq, regs); + ack_irq(ack); + return; + } + /* + * For normal interrupts, we mask it out, and then ACK it. + * This way another (more timing-critical) interrupt can + * come through while we're doing this one. + * + * Note! A irq without a handler gets masked and acked, but + * never unmasked. The autoirq stuff depends on this (it looks + * at the masks before and after doing the probing). + */ + mask_irq(ack); + ack_irq(ack); + if (!action->handler) + return; + action->handler(irq, regs); + unmask_irq(ack); +} + +/* + * Start listening for interrupts.. + */ +unsigned int probe_irq_on(void) +{ + unsigned int i, irqs = 0, irqmask; + unsigned long delay; + + for (i = 15; i > 0; i--) { + if (!irq_action[i].handler) { + enable_irq(i); + irqs |= (1 << i); + } + } + + /* wait for spurious interrupts to mask themselves out again */ + for (delay = jiffies + HZ/10; delay > jiffies; ) + /* about 100 ms delay */; + + /* now filter out any obviously spurious interrupts */ + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int) cache_21; + irqs &= ~irqmask; + return irqs; +} + +/* + * Get the result of the IRQ probe.. A negative result means that + * we have several candidates (but we return the lowest-numbered + * one). + */ +int probe_irq_off(unsigned int irqs) +{ + unsigned int i, irqmask; + + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; + irqs &= irqmask; + if (!irqs) + return 0; + i = ffz(~irqs); + if (irqs != (1 << i)) + i = -i; + return i; +} + +static void machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) +{ + printk("Machine check\n"); +} + +asmlinkage void do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + switch (type) { + case 0: + printk("Interprocessor interrupt? You must be kidding\n"); + break; + case 1: + /* timer interrupt.. */ + handle_irq(0, ®s); + return; + case 2: + machine_check(vector, la_ptr, ®s); + break; + case 3: + device_interrupt(vector, ®s); + return; + case 4: + printk("Performance counter interrupt\n"); + break;; + default: + printk("Hardware intr %ld %lx? Huh?\n", type, vector); + } + printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps); +} + +extern asmlinkage void entInt(void); + +void init_IRQ(void) +{ + wrent(entInt, 0); + dma_outb(0, DMA1_RESET_REG); + dma_outb(0, DMA2_RESET_REG); + dma_outb(0, DMA1_CLR_MASK_REG); + dma_outb(0, DMA2_CLR_MASK_REG); +} diff --git a/arch/alpha/kernel/lca.c b/arch/alpha/kernel/lca.c new file mode 100644 index 000000000..c32c308fb --- /dev/null +++ b/arch/alpha/kernel/lca.c @@ -0,0 +1,304 @@ +/* + * Code common to all LCA chips. + * + * Written by David Mosberger (davidm@cs.arizona.edu) with some code + * taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit + * bios code. + */ +#include <linux/kernel.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/bios32.h> +#include <linux/pci.h> + +#include <asm/system.h> +#include <asm/io.h> + +/* + * BIOS32-style PCI interface: + */ + +#ifdef CONFIG_PCI + +#define vulp volatile unsigned long * + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the LCA_IOC_CONF register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ +static int mk_conf_addr(unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned long *pci_addr) +{ + unsigned long addr; + + if (bus == 0) { + int device = device_fn >> 3; + int func = device_fn & 0x7; + + /* type 0 configuration cycle: */ + + if (device > 12) { + return -1; + } /* if */ + + *((volatile unsigned long*) LCA_IOC_CONF) = 0; + addr = (1 << (11 + device)) | (func << 8) | where; + } else { + /* type 1 configuration cycle: */ + *((volatile unsigned long*) LCA_IOC_CONF) = 1; + addr = (bus << 16) | (device_fn << 8) | where; + } /* if */ + *pci_addr = addr; + + return 0; +} + + +static unsigned int conf_read(unsigned long addr) +{ + unsigned long old_ipl, code, stat0; + unsigned int value; + + old_ipl = swpipl(7); /* avoid getting hit by machine check */ + + /* reset status register to avoid loosing errors: */ + stat0 = *((volatile unsigned long*)LCA_IOC_STAT0); + *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + mb(); + + /* access configuration space: */ + + value = *((volatile unsigned int*)addr); + draina(); + + stat0 = *((unsigned long*)LCA_IOC_STAT0); + if (stat0 & LCA_IOC_STAT0_ERR) { + code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) + & LCA_IOC_STAT0_CODE_MASK); + if (code != 1) { + printk("lca.c:conf_read: got stat0=%lx\n", stat0); + } + + /* reset error status: */ + *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + mb(); + wrmces(0x7); /* reset machine check */ + + value = 0xffffffff; + } + swpipl(old_ipl); + + return value; +} + + +static void conf_write(unsigned long addr, unsigned int value) +{ + unsigned long old_ipl, code, stat0; + + old_ipl = swpipl(7); /* avoid getting hit by machine check */ + + /* reset status register to avoid loosing errors: */ + stat0 = *((volatile unsigned long*)LCA_IOC_STAT0); + *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + mb(); + + /* access configuration space: */ + + *((volatile unsigned int*)addr) = value; + draina(); + + stat0 = *((unsigned long*)LCA_IOC_STAT0); + if (stat0 & LCA_IOC_STAT0_ERR) { + code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) + & LCA_IOC_STAT0_CODE_MASK); + if (code != 1) { + printk("lca.c:conf_write: got stat0=%lx\n", stat0); + } + + /* reset error status: */ + *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + mb(); + wrmces(0x7); /* reset machine check */ + } + swpipl(old_ipl); +} + + +int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + *value = 0xff; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x00; + + *value = conf_read(addr) >> ((where & 3) * 8); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + *value = 0xffff; + + if (where & 0x1) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } /* if */ + + if (mk_conf_addr(bus, device_fn, where, &pci_addr)) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x08; + + *value = conf_read(addr) >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + *value = 0xffffffff; + + if (where & 0x3) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } /* if */ + + if (mk_conf_addr(bus, device_fn, where, &pci_addr)) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x18; + + *value = conf_read(addr); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x00; + + conf_write(addr, value << ((where & 3) * 8)); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x08; + + conf_write(addr, value << ((where & 3) * 8)); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x18; + + conf_write(addr, value << ((where & 3) * 8)); + + return PCIBIOS_SUCCESSFUL; +} + + +unsigned long lca_init(unsigned long mem_start, unsigned long mem_end) +{ + /* + * Set up the PCI->physical memory translation windows. + * For now, window 1 is disabled. In the future, we may + * want to use it to do scatter/gather DMA. Window 0 + * goes at 1 GB and is 1 GB large. + */ + *(vulp)LCA_IOC_W_BASE1 = 0UL<<33; + *(vulp)LCA_IOC_W_BASE0 = 1UL<<33 | LCA_DMA_WIN_BASE; + *(vulp)LCA_IOC_W_MASK0 = LCA_DMA_WIN_SIZE - 1; + *(vulp)LCA_IOC_T_BASE0 = 0; + + return mem_start; +} + +#endif /* CONFIG_PCI */ + + /*** end of lca.c ***/ diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c new file mode 100644 index 000000000..ddf090283 --- /dev/null +++ b/arch/alpha/kernel/osf_sys.c @@ -0,0 +1,494 @@ +/* + * linux/arch/alpha/kernel/osf_sys.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles some of the stranger OSF/1 system call interfaces. + * Some of the system calls expect a non-C calling standard, others have + * special parameter blocks.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#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/utsname.h> +#include <linux/time.h> +#include <linux/major.h> +#include <linux/stat.h> +#include <linux/mman.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/io.h> + +extern int do_mount(dev_t, const char *, char *, int, void *); +extern int do_pipe(int *); + +extern struct file_operations * get_blkfops(unsigned int); +extern struct file_operations * get_chrfops(unsigned int); + +extern dev_t get_unnamed_dev(void); +extern void put_unnamed_dev(dev_t); + +extern asmlinkage int sys_umount(char *); +extern asmlinkage int sys_swapon(const char *specialfile); + +/* + * OSF/1 directory handling functions... + * + * The "getdents()" interface is much more sane: the "basep" stuff is + * braindamage (it can't really handle filesystems where the directory + * offset differences aren't the same as "d_reclen"). + */ +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+7) & ~7) + +struct osf_dirent { + unsigned int d_ino; + unsigned short d_reclen; + unsigned short d_namlen; + char d_name[1]; +}; + +struct osf_dirent_callback { + struct osf_dirent * dirent; + long *basep; + int count; + int error; +}; + +static int osf_filldir(void * __buf, char * name, int namlen, off_t offset, ino_t ino) +{ + struct osf_dirent * dirent; + struct osf_dirent_callback * buf = (struct osf_dirent_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* unly used if we fail */ + if (reclen > buf->count) + return -EINVAL; + if (buf->basep) { + put_user(offset, buf->basep); + buf->basep = NULL; + } + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(namlen, &dirent->d_namlen); + put_user(reclen, &dirent->d_reclen); + memcpy_tofs(dirent->d_name, name, namlen); + put_fs_byte(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->dirent = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage int osf_getdirentries(unsigned int fd, struct osf_dirent * dirent, + unsigned int count, long *basep) +{ + int error; + struct file * file; + struct osf_dirent_callback buf; + + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + return -EBADF; + if (!file->f_op || !file->f_op->readdir) + return -ENOTDIR; + error = verify_area(VERIFY_WRITE, dirent, count); + if (error) + return error; + if (basep) { + error = verify_area(VERIFY_WRITE, basep, sizeof(long)); + if (error) + return error; + } + buf.dirent = dirent; + buf.basep = basep; + buf.count = count; + buf.error = 0; + error = file->f_op->readdir(file->f_inode, file, dirent, osf_filldir); + if (error < 0) + return error; + if (count == buf.count) + return buf.error; + return count - buf.count; +} + +/* + * Heh. As documented by DEC.. + */ +asmlinkage unsigned long sys_madvise(void) +{ + return 0; +} + +asmlinkage unsigned long sys_getxuid(int a0, int a1, int a2, int a3, int a4, int a5, + struct pt_regs regs) +{ + (®s)->r20 = current->euid; + return current->uid; +} + +asmlinkage unsigned long sys_getxgid(int a0, int a1, int a2, int a3, int a4, int a5, + struct pt_regs regs) +{ + (®s)->r20 = current->egid; + return current->gid; +} + +asmlinkage unsigned long sys_getxpid(int a0, int a1, int a2, int a3, int a4, int a5, + struct pt_regs regs) +{ + (®s)->r20 = current->p_opptr->pid; + return current->pid; +} + +#define OSF_MAP_ANONYMOUS 0x0010 +#define OSF_MAP_FIXED 0x0100 +#define OSF_MAP_HASSEMAPHORE 0x0200 +#define OSF_MAP_INHERIT 0x0400 +#define OSF_MAP_UNALIGNED 0x0800 + +asmlinkage unsigned long osf_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long osf_flags, unsigned long fd, + unsigned long off) +{ + struct file * file = NULL; + unsigned long flags = osf_flags & 0x0f; + + if (osf_flags & (OSF_MAP_HASSEMAPHORE | OSF_MAP_INHERIT | OSF_MAP_UNALIGNED)) + printk("%s: unimplemented OSF mmap flags %04lx\n", current->comm, osf_flags); + if (osf_flags & OSF_MAP_FIXED) + flags |= MAP_FIXED; + if (osf_flags & OSF_MAP_ANONYMOUS) + flags |= MAP_ANONYMOUS; + else { + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + return -EBADF; + } + return do_mmap(file, addr, len, prot, flags, off); +} + +asmlinkage int osf_statfs(char * path, struct statfs * buffer, unsigned long bufsiz) +{ + struct inode * inode; + int retval; + + if (bufsiz > sizeof(struct statfs)) + bufsiz = sizeof(struct statfs); + retval = verify_area(VERIFY_WRITE, buffer, bufsiz); + if (retval) + return retval; + retval = namei(path, &inode); + if (retval) + return retval; + if (!inode->i_sb->s_op->statfs) { + iput(inode); + return -ENOSYS; + } + inode->i_sb->s_op->statfs(inode->i_sb, buffer, bufsiz); + iput(inode); + return 0; +} + +asmlinkage int osf_fstatfs(unsigned long fd, struct statfs * buffer, unsigned long bufsiz) +{ + struct file * file; + struct inode * inode; + int retval; + + retval = verify_area(VERIFY_WRITE, buffer, bufsiz); + if (retval) + return retval; + if (bufsiz > sizeof(struct statfs)) + bufsiz = sizeof(struct statfs); + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + return -EBADF; + if (!(inode = file->f_inode)) + return -ENOENT; + if (!inode->i_sb->s_op->statfs) + return -ENOSYS; + inode->i_sb->s_op->statfs(inode->i_sb, buffer, bufsiz); + return 0; +} + +/* + * Uhh.. OSF/1 mount parameters aren't exactly obvious.. + * + * Although to be frank, neither are the native Linux/i386 ones.. + */ +struct ufs_args { + char * devname; + int flags; + uid_t exroot; +}; + +struct cdfs_args { + char * devname; + int flags; + uid_t exroot; +/* + * this has lots more here, which linux handles with the option block + * but I'm too lazy to do the translation into ascii.. + */ +}; + +struct procfs_args { + char * devname; + int flags; + uid_t exroot; +}; + +static int getdev(const char * name, int rdonly, struct inode ** ino) +{ + dev_t dev; + struct inode * inode; + struct file_operations * fops; + int retval; + + retval = namei(name, &inode); + if (retval) + return retval; + if (!S_ISBLK(inode->i_mode)) { + iput(inode); + return -ENOTBLK; + } + if (IS_NODEV(inode)) { + iput(inode); + return -EACCES; + } + dev = inode->i_rdev; + if (MAJOR(dev) >= MAX_BLKDEV) { + iput(inode); + return -ENXIO; + } + fops = get_blkfops(MAJOR(dev)); + if (!fops) { + iput(inode); + return -ENODEV; + } + if (fops->open) { + struct file dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.f_inode = inode; + dummy.f_mode = rdonly ? 1 : 3; + retval = fops->open(inode, &dummy); + if (retval) { + iput(inode); + return retval; + } + } + *ino = inode; + return 0; +} + +static void putdev(struct inode * inode) +{ + struct file_operations * fops; + + fops = get_blkfops(MAJOR(inode->i_rdev)); + if (fops->release) + fops->release(inode, NULL); +} + +/* + * We can't actually handle ufs yet, so we translate UFS mounts to + * ext2fs mounts... I wouldn't mind a USF filesystem, but the UFS + * layout is so braindead it's a major headache doing it.. + */ +static int osf_ufs_mount(char * dirname, struct ufs_args * args, int flags) +{ + int retval; + struct inode * inode; + struct cdfs_args tmp; + + retval = verify_area(VERIFY_READ, args, sizeof(*args)); + if (retval) + return retval; + memcpy_fromfs(&tmp, args, sizeof(tmp)); + retval = getdev(tmp.devname, 0, &inode); + if (retval) + return retval; + retval = do_mount(inode->i_rdev, dirname, "ext2", flags, NULL); + if (retval) + putdev(inode); + iput(inode); + return retval; +} + +static int osf_cdfs_mount(char * dirname, struct cdfs_args * args, int flags) +{ + int retval; + struct inode * inode; + struct cdfs_args tmp; + + retval = verify_area(VERIFY_READ, args, sizeof(*args)); + if (retval) + return retval; + memcpy_fromfs(&tmp, args, sizeof(tmp)); + retval = getdev(tmp.devname, 1, &inode); + if (retval) + return retval; + retval = do_mount(inode->i_rdev, dirname, "iso9660", flags, NULL); + if (retval) + putdev(inode); + iput(inode); + return retval; +} + +static int osf_procfs_mount(char * dirname, struct procfs_args * args, int flags) +{ + dev_t dev; + int retval; + struct procfs_args tmp; + + retval = verify_area(VERIFY_READ, args, sizeof(*args)); + if (retval) + return retval; + memcpy_fromfs(&tmp, args, sizeof(tmp)); + dev = get_unnamed_dev(); + if (!dev) + return -ENODEV; + retval = do_mount(dev, dirname, "proc", flags, NULL); + if (retval) + put_unnamed_dev(dev); + return retval; +} + +asmlinkage int osf_mount(unsigned long typenr, char * path, int flag, void * data) +{ + int retval; + + retval = -EINVAL; + switch (typenr) { + case 1: + retval = osf_ufs_mount(path, (struct ufs_args *) data, flag); + break; + case 6: + retval = osf_cdfs_mount(path, (struct cdfs_args *) data, flag); + break; + case 9: + retval = osf_procfs_mount(path, (struct procfs_args *) data, flag); + break; + default: + printk("osf_mount(%ld, %x)\n", typenr, flag); + } + return retval; +} + +asmlinkage int osf_umount(char * path, int flag) +{ + return sys_umount(path); +} + +/* + * I don't know what the parameters are: the first one + * seems to be a timeval pointer, and I suspect the second + * one is the time remaining.. Ho humm.. No documentation. + */ +asmlinkage int osf_usleep_thread(struct timeval * sleep, struct timeval * remain) +{ + struct timeval tmp; + unsigned long ticks; + int retval; + + retval = verify_area(VERIFY_READ, sleep, sizeof(*sleep)); + if (retval) + return retval; + if (remain && (retval = verify_area(VERIFY_WRITE, remain, sizeof(*remain)))) + return retval; + memcpy_fromfs(&tmp, sleep, sizeof(*sleep)); + ticks = tmp.tv_usec; + ticks = (ticks + (1000000 / HZ) - 1) / (1000000 / HZ); + ticks += tmp.tv_sec * HZ; + current->timeout = ticks + jiffies; + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (!remain) + return 0; + ticks = jiffies; + if (ticks < current->timeout) + ticks = current->timeout - ticks; + else + ticks = 0; + current->timeout = 0; + tmp.tv_sec = ticks / HZ; + tmp.tv_usec = ticks % HZ; + memcpy_tofs(remain, &tmp, sizeof(*remain)); + return 0; +} + +asmlinkage int osf_utsname(char * name) +{ + int error = verify_area(VERIFY_WRITE, name, 5*32); + if (error) + return error; + memcpy_tofs(name + 0, system_utsname.sysname, 32); + memcpy_tofs(name + 32, system_utsname.nodename, 32); + memcpy_tofs(name + 64, system_utsname.release, 32); + memcpy_tofs(name + 96, system_utsname.version, 32); + memcpy_tofs(name + 128, system_utsname.machine, 32); + return 0; +} + +asmlinkage int osf_swapon(const char * path, int flags, int lowat, int hiwat) +{ + /* for now, simply ignore flags, lowat and hiwat... */ + return sys_swapon(path); +} + +asmlinkage unsigned long sys_getpagesize(void) +{ + return PAGE_SIZE; +} + +asmlinkage unsigned long sys_getdtablesize(void) +{ + return NR_OPEN; +} + +asmlinkage int sys_pipe(int a0, int a1, int a2, int a3, int a4, int a5, + struct pt_regs regs) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (error) + return error; + (®s)->r20 = fd[1]; + return fd[0]; +} + +/* + * For compatibility with OSF/1 only. Use utsname(2) instead. + */ +asmlinkage int osf_getdomainname(char *name, int namelen) +{ + unsigned len; + int i, error; + + error = verify_area(VERIFY_WRITE, name, namelen); + if (error) + return error; + + len = namelen; + if (namelen > 32) + len = 32; + + for (i = 0; i < len; ++i) { + put_user(system_utsname.domainname[i], name + i); + if (system_utsname.domainname[i] == '\0') + break; + } + return 0; +} diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c new file mode 100644 index 000000000..257f431a5 --- /dev/null +++ b/arch/alpha/kernel/process.c @@ -0,0 +1,186 @@ +/* + * linux/arch/alpha/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#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/utsname.h> +#include <linux/time.h> +#include <linux/major.h> +#include <linux/stat.h> +#include <linux/mman.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/io.h> + +asmlinkage int sys_sethae(unsigned long hae, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + (®s)->hae = hae; + return 0; +} + +asmlinkage int sys_idle(void) +{ + if (current->pid != 0) + return -EPERM; + + /* endless idle loop with no priority at all */ + current->counter = -100; + for (;;) { + schedule(); + } +} + +void hard_reset_now(void) +{ + halt(); +} + +void show_regs(struct pt_regs * regs) +{ + printk("\nps: %04lx pc: %016lx\n", regs->ps, regs->pc); + printk("rp: %016lx sp: %p\n", regs->r26, regs+1); + printk(" r0: %016lx r1: %016lx r2: %016lx r3: %016lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); + printk(" r4: %016lx r5: %016lx r6: %016lx r7: %016lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); + printk(" r8: %016lx r16: %016lx r17: %016lx r18: %016lx\n", + regs->r8, regs->r16, regs->r17, regs->r18); + printk("r19: %016lx r20: %016lx r21: %016lx r22: %016lx\n", + regs->r19, regs->r20, regs->r21, regs->r22); + printk("r23: %016lx r24: %016lx r25: %016lx r26: %016lx\n", + regs->r23, regs->r24, regs->r25, regs->r26); + printk("r27: %016lx r28: %016lx r29: %016lx hae: %016lx\n", + regs->r27, regs->r28, regs->gp, regs->hae); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ +} + +void flush_thread(void) +{ +} + +/* + * "alpha_fork()".. By the time we get here, the + * non-volatile registers have also been saved on the + * stack. We do some ugly pointer stuff here.. (see + * also copy_thread) + */ +int alpha_fork(struct switch_stack * swstack) +{ + return do_fork(COPYVM | SIGCHLD, + rdusp(), + (struct pt_regs *) (swstack+1)); +} + +extern void ret_from_sys_call(void); +/* + * Copy an alpha thread.. + * + * Note the "stack_offset" stuff: when returning to kernel mode, we need + * to have some extra stack-space for the kernel stack that still exists + * after the "ret_from_sys_call". When returning to user mode, we only + * want the space needed by the syscall stack frame (ie "struct pt_regs"). + * Use the passed "regs" pointer to determine how much space we need + * for a kernel fork(). + */ +void copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs * childregs; + struct switch_stack * childstack, *stack; + unsigned long stack_offset; + + stack_offset = PAGE_SIZE - sizeof(struct pt_regs); + if (!(regs->ps & 8)) + stack_offset = (PAGE_SIZE-1) & (unsigned long) regs; + childregs = (struct pt_regs *) (p->kernel_stack_page + stack_offset); + + *childregs = *regs; + childregs->r0 = 0; + childregs->r19 = 0; + childregs->r20 = 1; /* OSF/1 has some strange fork() semantics.. */ + regs->r0 = p->pid; + regs->r20 = 0; + stack = ((struct switch_stack *) regs) - 1; + childstack = ((struct switch_stack *) childregs) - 1; + *childstack = *stack; + childstack->r26 = (unsigned long) ret_from_sys_call; + p->tss.usp = usp; + p->tss.ksp = (unsigned long) childstack; + p->tss.flags = 1; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ +} + +/* + * sys_execve() executes a new program. + * + * This works due to the alpha calling sequence: the first 6 args + * are gotten from registers, while the rest is on the stack, so + * we get a0-a5 for free, and then magically find "struct pt_regs" + * on the stack for us.. + * + * Don't do this at home. + */ +asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + int error; + char * filename; + + error = getname((char *) a0, &filename); + if (error) + return error; + error = do_execve(filename, (char **) a1, (char **) a2, ®s); + putname(filename); + return error; +} + +/* + * This doesn't actually work correctly like this: we need to do the + * same stack setups that fork() does first. + */ +asmlinkage int sys_clone(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + unsigned long clone_flags = a0; + unsigned long newsp; + + newsp = rdusp(); + if (newsp == a1 || !a1) + clone_flags |= COPYVM; + else + newsp = a1; + return do_fork(clone_flags, newsp, ®s); +} diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c new file mode 100644 index 000000000..23a2efd65 --- /dev/null +++ b/arch/alpha/kernel/setup.c @@ -0,0 +1,172 @@ +/* + * linux/arch/alpha/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#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/delay.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/hwrpb.h> +#include <asm/dma.h> +#include <asm/io.h> + +struct hae hae = { + 0, + (unsigned long*) HAE_ADDRESS +}; + +struct hwrpb_struct *hwrpb; + +unsigned char aux_device_present; + +/* + * This is setup by the secondary bootstrap loader. Because + * the zero page is zeroed out as soon as the vm system is + * initialized, we need to copy things out into a more permanent + * place. + */ +#define PARAM ZERO_PGE +#define COMMAND_LINE ((char*)(PARAM + 0x0000)) +#define COMMAND_LINE_SIZE 256 + +static char command_line[COMMAND_LINE_SIZE] = { 0, }; + +/* + * The format of "screen_info" is strange, and due to early + * i386-setup code. This is just enough to make the console + * code think we're on a EGA+ colour display. + */ +struct screen_info screen_info = { + 0, 0, /* orig-x, orig-y */ + { 0, 0 }, /* unused */ + 0, /* orig-video-page */ + 0, /* orig-video-mode */ + 80, /* orig-video-cols */ + 0,0,0, /* ega_ax, ega_bx, ega_cx */ + 25 /* orig-video-lines */ +}; + +static unsigned long find_end_memory(void) +{ + int i; + unsigned long high = 0; + struct memclust_struct * cluster; + struct memdesc_struct * memdesc; + + memdesc = (struct memdesc_struct *) (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB); + cluster = memdesc->cluster; + for (i = memdesc->numclusters ; i > 0; i--, cluster++) { + unsigned long tmp; + tmp = (cluster->start_pfn + cluster->numpages) << PAGE_SHIFT; + if (tmp > high) + high = tmp; + } + /* round it up to an even number of pages.. */ + high = (high + PAGE_SIZE) & (PAGE_MASK*2); + return PAGE_OFFSET + high; +} + +void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + extern int _end; + + hwrpb = (struct hwrpb_struct*)(IDENT_ADDR + INIT_HWRPB->phys_addr); + + set_hae(hae.cache); /* sync HAE register w/hae_cache */ + + ROOT_DEV = 0x0802; /* sda2 */ +#ifndef CONFIG_PCI + aux_device_present = 0xaa; +#else + aux_device_present = 0x00; +#endif + command_line[COMMAND_LINE_SIZE - 1] = '\0'; + strcpy(command_line, COMMAND_LINE); + + *cmdline_p = command_line; + *memory_start_p = (unsigned long) &_end; + *memory_end_p = find_end_memory(); + +#ifdef CONFIG_PCI + *memory_start_p = lca_init(*memory_start_p, *memory_end_p); +#endif +} + +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + return -EIO; +} + + +/* + * BUFFER is PAGE_SIZE bytes long. + */ +int get_cpuinfo(char *buffer) +{ + const char *cpu_name[] = { + "EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45" + }; + const char *systype_name[] = { + "ADU", "Cobra", "Ruby", "Flamingo", "Unknown 1", "Jensen", + "Pelican", "Unknown 2", "Sable", "AXPvme", "Noname", + "Turbolaser", "Avanti", "Mustang", "Alcor", "Unknown 3", + "Mikasa", "Unknown3", "EB66", "EB64+" + }; + struct percpu_struct *cpu; + unsigned int cpu_index, system_index; +# define N(a) (sizeof(a)/sizeof(a[0])) + + cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); + cpu_index = (unsigned) (cpu->type - 1); + system_index = (unsigned) (hwrpb->sys_type - 1); + + return sprintf(buffer, + "cpu\t\t\t: Alpha\n" + "cpu model\t\t: %s\n" + "cpu variation\t\t: %ld\n" + "cpu revision\t\t: %ld\n" + "cpu serial number\t: %s\n" + "system type\t\t: %s\n" + "system variation\t: %ld\n" + "system revision\t\t: %ld\n" + "system serial number\t: %s\n" + "cycle frequency [Hz]\t: %lu\n" + "timer frequency [Hz]\t: %lu.%02lu\n" + "page size [bytes]\t: %ld\n" + "phys. address bits\t: %ld\n" + "max. addr. space #\t: %ld\n" + "BogoMIPS\t\t: %lu.%02lu\n", + + (cpu_index < N(cpu_name) ? cpu_name[cpu_index] : "Unknown"), + cpu->variation, cpu->revision, (char*)cpu->serial_no, + (system_index < N(systype_name) ? systype_name[system_index] : "Unknown"), + hwrpb->sys_variation, hwrpb->sys_revision, + (char*)hwrpb->ssn, + hwrpb->cycle_freq, + hwrpb->intr_freq / 4096, + (100 * hwrpb->intr_freq / 4096) % 100, + hwrpb->pagesize, + hwrpb->pa_bits, + hwrpb->max_asn, + loops_per_sec / 500000, (loops_per_sec / 5000) % 100); +# undef N +} diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c new file mode 100644 index 000000000..cc7069728 --- /dev/null +++ b/arch/alpha/kernel/signal.c @@ -0,0 +1,319 @@ +/* + * linux/arch/alpha/kernel/signal.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/mm.h> + +#include <asm/bitops.h> +#include <asm/segment.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 void ret_from_sys_call(void); +asmlinkage int do_signal(unsigned long, struct pt_regs *, struct switch_stack *, + unsigned long, unsigned long); +asmlinkage void imb(void); + +/* + * The OSF/1 sigprocmask calling sequence is different from the + * C sigprocmask() sequence.. + */ +asmlinkage unsigned long osf_sigprocmask(int how, unsigned long newmask) +{ + unsigned long oldmask = current->blocked; + + newmask &= _BLOCKABLE; + switch (how) { + case SIG_BLOCK: + current->blocked |= newmask; + return oldmask; + case SIG_UNBLOCK: + current->blocked &= ~newmask; + return oldmask; + case SIG_SETMASK: + current->blocked = newmask; + return oldmask; + } + return -EINVAL; +} + +/* + * atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int do_sigsuspend(unsigned long mask, struct pt_regs * regs, struct switch_stack * sw) +{ + unsigned long oldmask = current->blocked; + current->blocked = mask & _BLOCKABLE; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(oldmask,regs, sw, 0, 0)) + return -EINTR; + } +} + +/* + * Do a signal return; undo the signal stack. + */ +asmlinkage void do_sigreturn(struct sigcontext_struct * sc, + struct pt_regs * regs, struct switch_stack * sw) +{ + unsigned long mask; + int i; + + /* verify that it's a good sigcontext before using it */ + if (verify_area(VERIFY_READ, sc, sizeof(*sc))) + do_exit(SIGSEGV); + if (get_fs_quad(&sc->sc_ps) != 8) + do_exit(SIGSEGV); + mask = get_fs_quad(&sc->sc_mask); + if (mask & ~_BLOCKABLE) + do_exit(SIGSEGV); + + /* ok, looks fine, start restoring */ + wrusp(get_fs_quad(sc->sc_regs+30)); + regs->pc = get_fs_quad(&sc->sc_pc); + sw->r26 = (unsigned long) ret_from_sys_call; + current->blocked = mask; + + regs->r0 = get_fs_quad(sc->sc_regs+0); + regs->r1 = get_fs_quad(sc->sc_regs+1); + regs->r2 = get_fs_quad(sc->sc_regs+2); + regs->r3 = get_fs_quad(sc->sc_regs+3); + regs->r4 = get_fs_quad(sc->sc_regs+4); + regs->r5 = get_fs_quad(sc->sc_regs+5); + regs->r6 = get_fs_quad(sc->sc_regs+6); + regs->r7 = get_fs_quad(sc->sc_regs+7); + regs->r8 = get_fs_quad(sc->sc_regs+8); + sw->r9 = get_fs_quad(sc->sc_regs+9); + sw->r10 = get_fs_quad(sc->sc_regs+10); + sw->r11 = get_fs_quad(sc->sc_regs+11); + sw->r12 = get_fs_quad(sc->sc_regs+12); + sw->r13 = get_fs_quad(sc->sc_regs+13); + sw->r14 = get_fs_quad(sc->sc_regs+14); + sw->r15 = get_fs_quad(sc->sc_regs+15); + regs->r16 = get_fs_quad(sc->sc_regs+16); + regs->r17 = get_fs_quad(sc->sc_regs+17); + regs->r18 = get_fs_quad(sc->sc_regs+18); + regs->r19 = get_fs_quad(sc->sc_regs+19); + regs->r20 = get_fs_quad(sc->sc_regs+20); + regs->r21 = get_fs_quad(sc->sc_regs+21); + regs->r22 = get_fs_quad(sc->sc_regs+22); + regs->r23 = get_fs_quad(sc->sc_regs+23); + regs->r24 = get_fs_quad(sc->sc_regs+24); + regs->r25 = get_fs_quad(sc->sc_regs+25); + regs->r26 = get_fs_quad(sc->sc_regs+26); + regs->r27 = get_fs_quad(sc->sc_regs+27); + regs->r28 = get_fs_quad(sc->sc_regs+28); + regs->gp = get_fs_quad(sc->sc_regs+29); + for (i = 0; i < 31; i++) + sw->fp[i] = get_fs_quad(sc->sc_fpregs+i); +} + +/* + * Set up a signal frame... + */ +static void setup_frame(struct sigaction * sa, struct sigcontext_struct ** fp, unsigned long pc, + struct pt_regs * regs, struct switch_stack * sw, int signr, unsigned long oldmask) +{ + int i; + struct sigcontext_struct * sc; + + sc = *fp; + /* check here if we would need to switch stacks.. */ + sc--; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + do_exit(SIGSEGV); + + put_fs_quad(oldmask, &sc->sc_mask); + put_fs_quad(8, &sc->sc_ps); + put_fs_quad(pc, &sc->sc_pc); + put_fs_quad((unsigned long)*fp, sc->sc_regs+30); + + put_fs_quad(regs->r0 , sc->sc_regs+0); + put_fs_quad(regs->r1 , sc->sc_regs+1); + put_fs_quad(regs->r2 , sc->sc_regs+2); + put_fs_quad(regs->r3 , sc->sc_regs+3); + put_fs_quad(regs->r4 , sc->sc_regs+4); + put_fs_quad(regs->r5 , sc->sc_regs+5); + put_fs_quad(regs->r6 , sc->sc_regs+6); + put_fs_quad(regs->r7 , sc->sc_regs+7); + put_fs_quad(regs->r8 , sc->sc_regs+8); + put_fs_quad(sw->r9 , sc->sc_regs+9); + put_fs_quad(sw->r10 , sc->sc_regs+10); + put_fs_quad(sw->r11 , sc->sc_regs+11); + put_fs_quad(sw->r12 , sc->sc_regs+12); + put_fs_quad(sw->r13 , sc->sc_regs+13); + put_fs_quad(sw->r14 , sc->sc_regs+14); + put_fs_quad(sw->r15 , sc->sc_regs+15); + put_fs_quad(regs->r16, sc->sc_regs+16); + put_fs_quad(regs->r17, sc->sc_regs+17); + put_fs_quad(regs->r18, sc->sc_regs+18); + put_fs_quad(regs->r19, sc->sc_regs+19); + put_fs_quad(regs->r20, sc->sc_regs+20); + put_fs_quad(regs->r21, sc->sc_regs+21); + put_fs_quad(regs->r22, sc->sc_regs+22); + put_fs_quad(regs->r23, sc->sc_regs+23); + put_fs_quad(regs->r24, sc->sc_regs+24); + put_fs_quad(regs->r25, sc->sc_regs+25); + put_fs_quad(regs->r26, sc->sc_regs+26); + put_fs_quad(regs->r27, sc->sc_regs+27); + put_fs_quad(regs->r28, sc->sc_regs+28); + put_fs_quad(regs->gp , sc->sc_regs+29); + for (i = 0; i < 31; i++) + put_fs_quad(sw->fp[i], sc->sc_fpregs+i); + + /* + * The following is: + * + * bis $30,$30,$16 + * addq $31,0x67,$0 + * call_pal callsys + * + * ie, "sigreturn(stack-pointer)" + */ + put_fs_quad(0x43ecf40047de0410, sc->sc_retcode+0); + put_fs_quad(0x0000000000000083, sc->sc_retcode+1); + regs->r26 = (unsigned long) sc->sc_retcode; + regs->r16 = signr; + *fp = sc; +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + * + * "r0" and "r19" are the registers we need to restore for system call + * restart. "r0" is also used as an indicator whether we can restart at + * all (if we get here from anything but a syscall return, it will be 0) + */ +asmlinkage int do_signal(unsigned long oldmask, + struct pt_regs * regs, + struct switch_stack * sw, + unsigned long r0, unsigned long r19) +{ + unsigned long mask = ~current->blocked; + unsigned long handler_signal = 0; + struct sigcontext_struct *frame = NULL; + unsigned long pc = 0; + unsigned long signr; + struct sigaction * sa; + + while ((signr = current->signal & mask) != 0) { + signr = ffz(~signr); + clear_bit(signr, ¤t->signal); + sa = current->sigaction + signr; + signr++; + if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current); + schedule(); + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + if (signr == SIGSTOP) + continue; + if (_S(signr) & current->blocked) { + current->signal |= _S(signr); + continue; + } + sa = current->sigaction + signr - 1; + } + if (sa->sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* check for SIGCHLD: it's special */ + while (sys_waitpid(-1,NULL,WNOHANG) > 0) + /* nothing */; + continue; + } + if (sa->sa_handler == SIG_DFL) { + if (current->pid == 1) + continue; + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (current->flags & PF_PTRACED) + continue; + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & + SA_NOCLDSTOP)) + notify_parent(current); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + if (current->binfmt && current->binfmt->core_dump) { + if (current->binfmt->core_dump(signr, regs)) + signr |= 0x80; + } + /* fall through */ + default: + current->signal |= _S(signr & 0x7f); + do_exit(signr); + } + } + /* + * OK, we're invoking a handler + */ + if (r0) { + if (regs->r0 == ERESTARTNOHAND || + (regs->r0 == ERESTARTSYS && !(sa->sa_flags & SA_RESTART))) + regs->r0 = EINTR; + } + handler_signal |= 1 << (signr-1); + mask &= ~sa->sa_mask; + } + if (r0 && + (regs->r0 == ERESTARTNOHAND || + regs->r0 == ERESTARTSYS || + regs->r0 == ERESTARTNOINTR)) { + regs->r0 = r0; + regs->r19 = r19; + regs->pc -= 4; + } + if (!handler_signal) /* no handler will be called - return 0 */ + return 0; + pc = regs->pc; + frame = (struct sigcontext_struct *) rdusp(); + 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,pc,regs,sw,signr,oldmask); + pc = (unsigned long) sa->sa_handler; + regs->r27 = pc; + if (sa->sa_flags & SA_ONESHOT) + sa->sa_handler = NULL; + current->blocked |= sa->sa_mask; + oldmask |= sa->sa_mask; + } + imb(); + wrusp((unsigned long) frame); + regs->pc = pc; /* "return" to the first handler */ + return 1; +} diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c new file mode 100644 index 000000000..4e3512ddd --- /dev/null +++ b/arch/alpha/kernel/traps.c @@ -0,0 +1,159 @@ +/* + * kernel/traps.c + * + * (C) Copyright 1994 Linus Torvalds + */ + +/* + * This file initializes the trap entry points + */ + +#include <linux/sched.h> +#include <linux/tty.h> + +#include <asm/unaligned.h> + +void die_if_kernel(char * str, struct pt_regs * regs, long err) +{ + long i; + unsigned long sp; + unsigned int * pc; + + printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); + sp = (unsigned long) (regs+1); + if (regs->ps & 8) + sp = rdusp(); + printk("pc = %lx ps = %04lx\n", regs->pc, regs->ps); + printk("rp = %lx sp = %lx\n", regs->r26, sp); + printk("r0=%lx r1=%lx r2=%lx r3=%lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); + printk("r8=%lx\n", regs->r8); + printk("r16=%lx r17=%lx r18=%lx r19=%lx\n", + regs->r16, regs->r17, regs->r18, regs->r19); + printk("r20=%lx r21=%lx r22=%lx r23=%lx\n", + regs->r20, regs->r21, regs->r22, regs->r23); + printk("r24=%lx r25=%lx r26=%lx r27=%lx\n", + regs->r24, regs->r25, regs->r26, regs->r27); + printk("r28=%lx r29=%lx r30=%lx\n", + regs->r28, regs->gp, sp); + if (regs->ps & 8) + return; + printk("Code:"); + pc = (unsigned int *) regs->pc; + for (i = -3; i < 6; i++) + printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>'); + printk("\n"); + for (i = 0 ; i < 5000000000 ; i++) + /* pause */; + halt(); +} + +asmlinkage void do_entArith(unsigned long summary, unsigned long write_mask, + unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + printk("Arithmetic trap: %02lx %016lx\n", summary, write_mask); + die_if_kernel("Arithmetic fault", ®s, 0); +} + +asmlinkage void do_entIF(unsigned long type, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + die_if_kernel("Instruction fault", ®s, type); +} + +/* + * entUna has a different register layout to be reasonably simple. It + * needs access to all the integer registers (the kernel doesn't use + * fp-regs), and it needs to have them in order for simpler access. + * + * Due to the non-standard register layout (and because we don't want + * to handle floating-point regs), we disallow user-mode unaligned + * accesses (we'd need to do "verify_area()" checking, as well as + * do a full "ret_from_sys_call" return). + * + * Oh, btw, we don't handle the "gp" register correctly, but if we fault + * on a gp-register unaligned load/store, something is _very_ wrong + * in the kernel anyway.. + */ +struct allregs { + unsigned long regs[32]; + unsigned long ps, pc, gp, a0, a1, a2; +}; + +asmlinkage void do_entUna(void * va, unsigned long opcode, unsigned long reg, + unsigned long a3, unsigned long a4, unsigned long a5, + struct allregs regs) +{ + static int cnt = 0; + + if (regs.ps & 8) + do_exit(SIGSEGV); + if (++cnt < 5) + printk("Unaligned trap at %016lx: %p %lx %ld\n", + regs.pc, va, opcode, reg); + /* $16-$18 are PAL-saved, and are offset by 19 entries */ + if (reg >= 16 && reg <= 18) + reg += 19; + switch (opcode) { + case 0x28: /* ldl */ + *(reg+regs.regs) = (int) ldl_u(va); + return; + case 0x29: /* ldq */ + *(reg+regs.regs) = ldq_u(va); + return; + case 0x2c: /* stl */ + stl_u(*(reg+regs.regs), va); + return; + case 0x2d: /* stq */ + stq_u(*(reg+regs.regs), va); + return; + } + printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n", + regs.pc, va, opcode, reg); + do_exit(SIGSEGV); +} + +/* + * DEC means people to use the "retsys" instruction for return from + * a system call, but they are clearly misguided about this. We use + * "rti" in all cases, and fill in the stack with the return values. + * That should make signal handling etc much cleaner. + * + * Even more horribly, DEC doesn't allow system calls from kernel mode. + * "Security" features letting the user do something the kernel can't + * are a thinko. DEC palcode is strange. The PAL-code designers probably + * got terminally tainted by VMS at some point. + */ +asmlinkage long do_entSys(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) +{ + printk("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2); + return -1; +} + +extern asmlinkage void entMM(void); +extern asmlinkage void entIF(void); +extern asmlinkage void entArith(void); +extern asmlinkage void entUna(void); +extern asmlinkage void entSys(void); + +void trap_init(void) +{ + unsigned long gptr; + + /* + * Tell PAL-code what global pointer we want in the kernel.. + */ + __asm__("br %0,___tmp\n" + "___tmp:\tldgp %0,0(%0)" + : "=r" (gptr)); + wrkgp(gptr); + + wrent(entArith, 1); + wrent(entMM, 2); + wrent(entIF, 3); + wrent(entUna, 4); + wrent(entSys, 5); +} diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile new file mode 100644 index 000000000..9ac9310b2 --- /dev/null +++ b/arch/alpha/lib/Makefile @@ -0,0 +1,38 @@ +# +# Makefile for alpha-specific library files.. +# + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< + +OBJS = __divqu.o __remqu.o __divlu.o __remlu.o memset.o memcpy.o io.o + +lib.a: $(OBJS) + $(AR) rcs lib.a $(OBJS) + sync + +__divqu.o: divide.S + $(CC) -DDIV -c -o __divqu.o divide.S + +__remqu.o: divide.S + $(CC) -DREM -c -o __remqu.o divide.S + +__divlu.o: divide.S + $(CC) -DDIV -DINTSIZE -c -o __divlu.o divide.S + +__remlu.o: divide.S + $(CC) -DREM -DINTSIZE -c -o __remlu.o divide.S + +dep: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + diff --git a/arch/alpha/lib/divide.S b/arch/alpha/lib/divide.S new file mode 100644 index 000000000..9388d3f34 --- /dev/null +++ b/arch/alpha/lib/divide.S @@ -0,0 +1,160 @@ +/* + * arch/alpha/lib/divide.S + * + * (C) 1995 Linus Torvalds + * + * Alpha division.. + */ + +/* + * The alpha chip doesn't provide hardware division, so we have to do it + * by hand. The compiler expects the functions + * + * __divqu: 64-bit unsigned long divide + * __remqu: 64-bit unsigned long remainder + * __divqs/__remqs: signed 64-bit + * __divlu/__remlu: unsigned 32-bit + * __divls/__remls: signed 32-bit + * + * These are not normal C functions: instead of the normal + * calling sequence, these expect their arguments in registers + * $24 and $25, and return the result in $27. Register $28 may + * be clobbered (assembly temporary), anything else must be saved. + * + * In short: painful. + * + * This is a rather simple bit-at-a-time algorithm: it's very good + * at dividing random 64-bit numbers, but the more usual case where + * the divisor is small is handled better by the DEC algorithm + * using lookup tables. This uses much less memory, though, and is + * nicer on the cache.. Besides, I don't know the copyright status + * of the DEC code. + */ + +/* + * My temporaries: + * $0 - current bit + * $1 - shifted divisor + * $2 - modulus/quotient + * + * $23 - return address + * $24 - dividend + * $25 - divisor + * + * $27 - quotient/modulus + * $28 - compare status + */ + +#define halt .long 0 + +/* + * Select function type and registers + */ +#define mask $0 +#define divisor $1 +#define compare $28 + +#ifdef DIV +#define func(x) __div##x +#define modulus $2 +#define quotient $27 +#define GETSIGN(x) xor $24,$25,x +#else +#define func(x) __rem##x +#define modulus $27 +#define quotient $2 +#define GETSIGN(x) bis $24,$24,x +#endif + +/* + * For 32-bit operations, we need to extend to 64-bit + */ +#ifdef INTSIZE +#define ufunction func(lu) +#define sfunction func(l) +#define LONGIFY(x) zapnot x,15,x +#define SLONGIFY(x) addl x,0,x +#else +#define ufunction func(qu) +#define sfunction func(q) +#define LONGIFY(x) +#define SLONGIFY(x) +#endif + +.set noat +.globl ufunction +.ent ufunction +ufunction: + subq $30,32,$30 + stq $0, 0($30) + stq $1, 8($30) + stq $2,16($30) + + bis $25,$25,divisor + bis $24,$24,modulus + bis $31,$31,quotient + LONGIFY(divisor) + LONGIFY(modulus) + beq divisor, 9f /* div by zero */ + bis $31,1,mask + + /* shift divisor left */ +1: cmpult divisor,modulus,compare + blt divisor, 3f + addq divisor,divisor,divisor + addq mask,mask,mask + bne compare,1b + + /* ok, start to go right again.. */ +2: srl divisor,1,divisor + beq mask,9f + srl mask,1,mask +3: cmpule divisor,modulus,compare + beq compare,2b + addq quotient,mask,quotient + beq mask,9f + subq modulus,divisor,modulus + br 2b + +9: ldq $0, 0($30) + ldq $1, 8($30) + ldq $2, 16($30) + addq $30,32,$30 + ret $31,($23),1 + .end ufunction + +/* + * Uhh.. Ugly signed division. I'd rather not have it at all, but + * it's needed in some circumstances. There are different ways to + * handle this, really. This does: + * -a / b = a / -b = -(a / b) + * -a % b = -(a % b) + * a % -b = a % b + * which is probably not the best solution, but at least should + * have the property that (x/y)*y + (x%y) = x. + */ +.globl sfunction +.ent sfunction +sfunction: + bis $24,$25,$28 + SLONGIFY($28) + bge $28,ufunction + subq $30,32,$30 + stq $23,0($30) + stq $24,8($30) + stq $25,16($30) + subq $31,$24,$28 + cmovlt $24,$28,$24 /* abs($24) */ + subq $31,$25,$28 + cmovlt $25,$28,$25 /* abs($25) */ + bsr $23,ufunction + ldq $23,0($30) + ldq $24,8($30) + ldq $25,16($30) + addq $30,32,$30 + GETSIGN($28) + SLONGIFY($28) + bge $28,1f + subq $31,$27,$27 +1: ret $31,($23),1 + .end sfunction diff --git a/arch/alpha/lib/io.c b/arch/alpha/lib/io.c new file mode 100644 index 000000000..b5f7290ab --- /dev/null +++ b/arch/alpha/lib/io.c @@ -0,0 +1,98 @@ +/* + * Alpha IO and memory functions.. Just expand the inlines in the header + * files.. + */ +#include <asm/io.h> + +/* + * Jensen has a separate "local" and "bus" IO space for + * byte-wide IO. + */ +#ifdef __is_local +#undef __bus_inb +unsigned int __bus_inb(unsigned long addr) +{ + return ___bus_inb(addr); +} + +#undef __bus_outb +void __bus_outb(unsigned char b, unsigned long addr) +{ + ___bus_outb(b, addr); +} +#endif + +#undef inb +unsigned int inb(unsigned long addr) +{ + return __inb(addr); +} + +#undef inw +unsigned int inw(unsigned long addr) +{ + return __inw(addr); +} + +#undef inl +unsigned int inl(unsigned long addr) +{ + return __inl(addr); +} + + +#undef outb +void outb(unsigned char b, unsigned long addr) +{ + __outb(b, addr); +} + +#undef outw +void outw(unsigned short b, unsigned long addr) +{ + __outw(b, addr); +} + +#undef outl +void outl(unsigned int b, unsigned long addr) +{ + __outl(b, addr); +} + + +#undef readb +unsigned long readb(unsigned long addr) +{ + return __readb(addr); +} + +#undef readw +unsigned long readw(unsigned long addr) +{ + return __readw(addr); +} + +#undef readl +unsigned long readl(unsigned long addr) +{ + return __readl(addr); +} + + +#undef writeb +void writeb(unsigned short b, unsigned long addr) +{ + __writeb(b, addr); +} + +#undef writew +void writew(unsigned short b, unsigned long addr) +{ + __writew(b, addr); +} + +#undef writel +void writel(unsigned int b, unsigned long addr) +{ + __writel(b, addr); +} diff --git a/arch/alpha/lib/memcpy.c b/arch/alpha/lib/memcpy.c new file mode 100644 index 000000000..4bbf92f69 --- /dev/null +++ b/arch/alpha/lib/memcpy.c @@ -0,0 +1,74 @@ +/* + * linux/arch/alpha/lib/memcpy.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This is reasonably optimized for the quad-word-aligned case, which + * happens with page/buffer copies. It's horribly bad for the unaligned + * case: it could be made much better, but that would require lots of + * assembly (unaligned 8-byte load + shift + aligned 4-byte store, for + * example). + */ + +#include <linux/types.h> + +static inline void __memcpy_b(unsigned long d, unsigned long s, long n) +{ + while (--n >= 0) + *(char *) (d++) = *(char *) (s++); +} + +static inline void __memcpy_q(unsigned long d, unsigned long s, long n) +{ + /* this first part could be done in one go with ldq_u*2/mask/stq_u */ + while (d & 7) { + if (--n < 0) + return; + *(char *) d = *(char *) s; + d++; + s++; + } + while ((n -= 8) >= 0) { + *(unsigned long *) d = *(unsigned long *) s; + d += 8; + s += 8; + } + /* as could this.. */ + __memcpy_b(d,s,n+8); +} + +static inline void __memcpy_l(unsigned long d, unsigned long s, long n) +{ + while (d & 3) { + if (--n < 0) + return; + *(char *) d = *(char *) s; + d++; + s++; + } + while ((n -= 4) >= 0) { + *(unsigned int *) d = *(unsigned int *) s; + d += 4; + s += 4; + } + __memcpy_b(d,s,n+4); +} + +void * __memcpy(void * dest, const void *src, size_t n) +{ + unsigned long differ; + differ = ((unsigned long) dest ^ (unsigned long) src) & 7; + + if (!differ) { + __memcpy_q((unsigned long) dest, (unsigned long) src, n); + return dest; + } + if (differ == 4) { + __memcpy_l((unsigned long) dest, (unsigned long) src, n); + return dest; + } + __memcpy_b((unsigned long) dest, (unsigned long) src, n); + return dest; +} diff --git a/arch/alpha/lib/memset.c b/arch/alpha/lib/memset.c new file mode 100644 index 000000000..d22d44424 --- /dev/null +++ b/arch/alpha/lib/memset.c @@ -0,0 +1,44 @@ +/* + * linux/arch/alpha/lib/memset.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * These are only slightly optimized so far.. + */ + +#include <linux/types.h> + +void * __constant_c_memset(void * s, unsigned long c, long count) +{ + unsigned long xs = (unsigned long) s; + + /* + * the first and last parts could be done with just one + * unaligned load/store, but I don't want to think about it + */ + while (count > 0 && (xs & 7)) { + *(char *) xs = c; + count--; xs++; + } + while (count > 7) { + *(unsigned long *) xs = c; + count -=8; xs += 8; + } + while (count > 0) { + *(char *) xs = c; + count--; xs++; + } + return s; +} + +void * __memset(void * s,char c,size_t count) +{ + char *xs = (char *) s; + + while (count--) + *xs++ = c; + + return s; +} diff --git a/arch/alpha/mm/Makefile b/arch/alpha/mm/Makefile new file mode 100644 index 000000000..9d825b683 --- /dev/null +++ b/arch/alpha/mm/Makefile @@ -0,0 +1,32 @@ +# +# Makefile for the linux alpha-specific parts of the memory manager. +# +# 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 definition is now in the main makefile... + +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) -S $< + +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 diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c new file mode 100644 index 000000000..10ac8f496 --- /dev/null +++ b/arch/alpha/mm/fault.c @@ -0,0 +1,100 @@ +/* + * linux/arch/alpha/mm/fault.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> + +extern void die_if_kernel(char *,struct pt_regs *,long); +extern void tbi(unsigned long type, unsigned long arg); +#define tbisi(x) tbi(1,(x)) +#define tbisd(x) tbi(2,(x)) +#define tbis(x) tbi(3,(x)) + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to handle_mm_fault(). + * + * mmcsr: + * 0 = translation not valid + * 1 = access violation + * 2 = fault-on-read + * 3 = fault-on-execute + * 4 = fault-on-write + * + * cause: + * -1 = instruction fetch + * 0 = load + * 1 = store + */ +asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, long cause, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + struct vm_area_struct * vma; + + vma = find_vma(current, 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) + 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: + if (cause < 0) { + if (!(vma->vm_flags & VM_EXEC)) + goto bad_area; + } else if (!cause) { + /* Allow reads even for write-only mappings */ + if (!(vma->vm_flags & (VM_READ | VM_WRITE))) + goto bad_area; + } else { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } + tbis(address); + handle_mm_fault(vma, address, cause > 0); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + if (user_mode(®s)) { + printk("memory violation at pc=%08lx (%08lx)\n", regs.pc, address); + die_if_kernel("oops", ®s, cause); + send_sig(SIGSEGV, current, 1); + return; + } +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + printk(KERN_ALERT "Unable to handle kernel paging request at virtual address %08lx\n",address); + die_if_kernel("Oops", ®s, cause); + do_exit(SIGKILL); +} diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c new file mode 100644 index 000000000..62203ef84 --- /dev/null +++ b/arch/alpha/mm/init.c @@ -0,0 +1,204 @@ +/* + * linux/arch/alpha/mm/init.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> +#include <asm/hwrpb.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); + +/* + * 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 + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +pmd_t * __bad_pagetable(void) +{ + memset((void *) EMPTY_PGT, 0, PAGE_SIZE); + return (pmd_t *) EMPTY_PGT; +} + +pte_t __bad_page(void) +{ + memset((void *) EMPTY_PGE, 0, PAGE_SIZE); + return pte_mkdirty(mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED)); +} + +unsigned long __zero_page(void) +{ + memset((void *) ZERO_PGE, 0, PAGE_SIZE); + return (unsigned long) ZERO_PGE; +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0; + + printk("\nMem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = MAP_NR(high_memory); + while (i-- > 0) { + total++; + if (mem_map[i] & MAP_PAGE_RESERVED) + reserved++; + else if (!mem_map[i]) + free++; + else + shared += mem_map[i]-1; + } + printk("%d pages of RAM\n",total); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + show_buffers(); +#ifdef CONFIG_NET + show_net_buffers(); +#endif +} + +extern unsigned long free_area_init(unsigned long, unsigned long); + +static void load_PCB(struct thread_struct * pcb) +{ + __asm__ __volatile__( + "stq $30,0(%0)\n\t" + "bis %0,%0,$16\n\t" + "call_pal %1" + : /* no outputs */ + : "r" (pcb), "i" (PAL_swpctx) + : "$0", "$1", "$16", "$22", "$23", "$24", "$25"); +} + +/* + * paging_init() sets up the page tables: in the alpha version this actually + * unmaps the bootup page table (as we're now in KSEG, so we don't need it). + */ +unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) +{ + int i; + unsigned long newptbr; + struct memclust_struct * cluster; + struct memdesc_struct * memdesc; + + /* initialize mem_map[] */ + start_mem = free_area_init(start_mem, end_mem); + + /* find free clusters, update mem_map[] accordingly */ + memdesc = (struct memdesc_struct *) (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB); + cluster = memdesc->cluster; + for (i = memdesc->numclusters ; i > 0; i--, cluster++) { + unsigned long pfn, nr; + if (cluster->usage & 1) + continue; + pfn = cluster->start_pfn; + nr = cluster->numpages; + + /* non-volatile memory. We might want to mark this for later */ + if (cluster->usage & 2) + continue; + + while (nr--) + mem_map[pfn++] = 0; + } + + /* unmap the console stuff: we don't need it, and we don't want it */ + /* Also set up the real kernel PCB while we're at it.. */ + memset((void *) ZERO_PGE, 0, PAGE_SIZE); + memset(swapper_pg_dir, 0, PAGE_SIZE); + newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT; + pgd_val(swapper_pg_dir[1023]) = (newptbr << 32) | pgprot_val(PAGE_KERNEL); + init_task.tss.ptbr = newptbr; + init_task.tss.flags = 1; + init_task.kernel_stack_page = INIT_STACK; + load_PCB(&init_task.tss); + + invalidate_all(); + return start_mem; +} + +void mem_init(unsigned long start_mem, unsigned long end_mem) +{ + unsigned long tmp; + + end_mem &= PAGE_MASK; + high_memory = end_mem; + start_mem = PAGE_ALIGN(start_mem); + + /* + * Mark the pages used by the kernel as reserved.. + */ + tmp = KERNEL_START; + while (tmp < start_mem) { + mem_map[MAP_NR(tmp)] = MAP_PAGE_RESERVED; + tmp += PAGE_SIZE; + } + + +#ifdef CONFIG_SCSI + scsi_mem_init(high_memory); +#endif +#ifdef CONFIG_SOUND + sound_mem_init(); +#endif + + for (tmp = PAGE_OFFSET ; tmp < high_memory ; tmp += PAGE_SIZE) { + if (mem_map[MAP_NR(tmp)]) + continue; + mem_map[MAP_NR(tmp)] = 1; + free_page(tmp); + } + tmp = nr_free_pages << PAGE_SHIFT; + printk("Memory: %luk available\n", tmp >> 10); + return; +} + +void si_meminfo(struct sysinfo *val) +{ + int i; + + i = MAP_NR(high_memory); + 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) + continue; + val->totalram++; + if (!mem_map[i]) + continue; + val->sharedram += mem_map[i]-1; + } + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + return; +} diff --git a/arch/i386/Makefile b/arch/i386/Makefile index b1aebc71a..0b296da44 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -1,6 +1,11 @@ # # i386/Makefile # +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# # This file is subject to the terms and conditions of the GNU General Public # License. See the file "COPYING" in the main directory of this archive # for more details. @@ -8,100 +13,69 @@ # Copyright (C) 1994 by Linus Torvalds # -AS86 =as86 -0 -a -LD86 =ld86 -0 -AS =as -LD =ld -HOSTCC =gcc -I$(TOPDIR)/include -CC =gcc -D__KERNEL__ -I$(TOPDIR)/include -MAKE =make -CPP =$(CC) -E -AR =ar -STRIP =strip +# +# Set these to indicate how to link it.. +# +# -zmagic: +# +# ZLINKFLAGS = -Ttext 0x1000 +# LINKFLAGS = -Ttext 0x100000 +# +# -qmagic (we need to remove the 32 byte header for bootup purposes) +# +ZLINKFLAGS =-qmagic -Ttext 0xfe0 +LINKFLAGS =-qmagic -Ttext 0xfffe0 +CFLAGS := $(CFLAGS) -pipe ifdef CONFIG_M486 CFLAGS := $(CFLAGS) -m486 else +ifdef CONFIG_M586 +CFLAGS := $(CFLAGS) -mpentium +else CFLAGS := $(CFLAGS) -m386 endif +endif -zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem - $(MAKE) -C zBoot +HEAD := arch/i386/kernel/head.o -zImage: $(CONFIGURE) boot/bootsect boot/setup zBoot/zSystem tools/build - tools/build boot/bootsect boot/setup zBoot/zSystem $(ROOT_DEV) > zImage - sync +SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm +ARCHIVES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(ARCHIVES) -zdisk: zImage - dd bs=8192 if=zImage of=/dev/fd0 +ifdef CONFIG_IBCS +SUBDIRS := $(SUBDIRS) arch/i386/ibcs +DRIVERS := $(DRIVERS) arch/i386/ibcs/ibcs.o +endif -zlilo: $(CONFIGURE) zImage - if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi - if [ -f $(INSTALL_PATH)/zSystem.map ]; then mv $(INSTALL_PATH)/zSystem.map $(INSTALL_PATH)/zSystem.old; fi - cat zImage > $(INSTALL_PATH)/vmlinuz - cp zSystem.map $(INSTALL_PATH)/ - if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi +ifdef CONFIG_MATH_EMULATION +SUBDIRS := $(SUBDIRS) arch/i386/math-emu +DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a +endif -# -# Set these to indicate how to link it.. -# -# -zmagic: -# -#LOWLDFLAGS = -Ttext 0x1000 -#HIGHLDFLAGS = -Ttext 0x100000 -# -# -qmagic (we need to remove the 32 byte header for bootup purposes) -# -LOWLDFLAGS =-qmagic -Ttext 0xfe0 -HIGHLDFLAGS =-qmagic -Ttext 0xfffe0 - -tools/system: boot/head.o init/main.o init/init.o tools/version.o linuxsubdirs - $(LD) $(LOWLDFLAGS) boot/head.o init/main.o tools/version.o \ - $(ARCHIVES) \ - $(FILESYSTEMS) \ - $(DRIVERS) \ - $(LIBS) \ - -o tools/system - nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ - sort > System.map - -boot/setup: boot/setup.o - $(LD86) -s -o $@ $< - -boot/setup.o: boot/setup.s - $(AS86) -o $@ $< - -boot/setup.s: boot/setup.S $(CONFIGURE) include/linux/config.h Makefile - $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ - -boot/bootsect: boot/bootsect.o - $(LD86) -s -o $@ $< - -boot/bootsect.o: boot/bootsect.s - $(AS86) -o $@ $< - -boot/bootsect.s: boot/bootsect.S $(CONFIGURE) include/linux/config.h Makefile - $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ - -tools/zSystem: boot/head.o init/main.o tools/version.o linuxsubdirs - $(LD) $(HIGHLDFLAGS) boot/head.o init/main.o tools/version.o \ - $(ARCHIVES) \ - $(FILESYSTEMS) \ - $(DRIVERS) \ - $(LIBS) \ - -o tools/zSystem - nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ - sort > zSystem.map +arch/i386/kernel: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/i386/kernel -# -# Leave these dummy entries for now to tell people that they are going away.. -# -lilo: - @echo - @echo Uncompressed kernel images no longer supported. Use - @echo \"make zlilo\" instead. - @echo - @exit 1 +arch/i386/mm: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/i386/mm + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +zImage: vmlinux + @$(MAKEBOOT) zImage + +compressed: zImage + +zlilo: vmlinux + @$(MAKEBOOT) zlilo + +zdisk: vmlinux + @$(MAKEBOOT) zdisk + +install: vmlinux + @$(MAKEBOOT) install archclean: - rm -f boot/bootsect boot/setup + @$(MAKEBOOT) clean + +archdep: + @$(MAKEBOOT) dep diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile new file mode 100644 index 000000000..b6a5f1c15 --- /dev/null +++ b/arch/i386/boot/Makefile @@ -0,0 +1,60 @@ +# +# arch/i386/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# + +AS86 =as86 -0 -a +LD86 =ld86 -0 + +zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build + tools/build bootsect setup compressed/vmlinux $(ROOT_DEV) > zImage + sync + +compressed/vmlinux: $(TOPDIR)/vmlinux + @$(MAKE) -C compressed vmlinux + +zdisk: zImage + dd bs=8192 if=zImage of=/dev/fd0 + +zlilo: $(CONFIGURE) zImage + 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 + cp $(TOPDIR)/System.map $(INSTALL_PATH)/ + if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi + +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 + +setup: setup.o + $(LD86) -s -o $@ $< + +setup.o: setup.s + $(AS86) -o $@ $< + +setup.s: setup.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile + $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + +bootsect: bootsect.o + $(LD86) -s -o $@ $< + +bootsect.o: bootsect.s + $(AS86) -o $@ $< + +bootsect.s: bootsect.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile + $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + +dep: + +clean: + rm -f bootsect setup + rm -f zImage tools/build + @$(MAKE) -C compressed clean diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile new file mode 100644 index 000000000..b4e3f8482 --- /dev/null +++ b/arch/i386/boot/compressed/Makefile @@ -0,0 +1,41 @@ +# +# linux/arch/i386/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +HEAD = head.o +SYSTEM = $(TOPDIR)/vmlinux + +OBJECTS = $(HEAD) inflate.o unzip.o misc.o + +CFLAGS = -O2 -DSTDC_HEADERS + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< + +all: vmlinux + +vmlinux: piggy.o $(OBJECTS) + $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/linux/tasks.h + $(CPP) -traditional head.S -o head.s + +piggy.o: $(SYSTEM) xtract piggyback + ./xtract $(SYSTEM) | gzip -9 | ./piggyback > piggy.o + +xtract: xtract.c + $(HOSTCC) $(CFLAGS) -o xtract xtract.c + +piggyback: piggyback.c + $(HOSTCC) $(CFLAGS) -o piggyback piggyback.c + +clean: + rm -f xtract piggyback vmlinux diff --git a/arch/i386/boot/compressed/crypt.h b/arch/i386/boot/compressed/crypt.h new file mode 100644 index 000000000..2a4c203ca --- /dev/null +++ b/arch/i386/boot/compressed/crypt.h @@ -0,0 +1,12 @@ +/* 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 new file mode 100644 index 000000000..2f738b945 --- /dev/null +++ b/arch/i386/boot/compressed/gzip.h @@ -0,0 +1,284 @@ +/* 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 new file mode 100644 index 000000000..6035cea85 --- /dev/null +++ b/arch/i386/boot/compressed/head.S @@ -0,0 +1,59 @@ +/* + * linux/boot/head.S + * + * Copyright (C) 1991, 1992, 1993 Linus Torvalds + */ + +/* + * head.S contains the 32-bit startup code. + * + * 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. + * + * 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. + */ +.text + +#define __ASSEMBLY__ +#include <asm/segment.h> + +startup_32: + cld + cli + movl $(KERNEL_DS),%eax + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + mov %ax,%gs + lss _stack_start,%esp + xorl %eax,%eax +1: incl %eax # check that A20 really IS enabled + movl %eax,0x000000 # loop forever if it isn't + cmpl %eax,0x100000 + je 1b +/* + * Initialize eflags. Some BIOS's leave bits like NT set. This would + * confuse the debugger if this code is traced. + * XXX - best to initialize before switching to protected mode. + */ + pushl $0 + popfl +/* + * Clear BSS + */ + xorl %eax,%eax + movl $__edata,%edi + movl $__end,%ecx + subl %edi,%ecx + cld + rep + stosb +/* + * Do the decompression, and jump to the new kernel.. + */ + call _decompress_kernel + ljmp $(KERNEL_CS), $0x100000 diff --git a/arch/i386/boot/compressed/inflate.c b/arch/i386/boot/compressed/inflate.c new file mode 100644 index 000000000..848fef6ae --- /dev/null +++ b/arch/i386/boot/compressed/inflate.c @@ -0,0 +1,810 @@ +#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 new file mode 100644 index 000000000..4e640f5a2 --- /dev/null +++ b/arch/i386/boot/compressed/lzw.h @@ -0,0 +1,42 @@ +/* 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 new file mode 100644 index 000000000..2623925c7 --- /dev/null +++ b/arch/i386/boot/compressed/misc.c @@ -0,0 +1,418 @@ +/* + * misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * puts by Nick Holloway 1993 + */ + +#include "gzip.h" +#include "lzw.h" + +#include <asm/segment.h> + +/* + * These are set up by the setup-routine at boot-time: + */ + +struct screen_info { + unsigned char orig_x; + unsigned char orig_y; + unsigned char unused1[2]; + unsigned short orig_video_page; + unsigned char orig_video_mode; + unsigned char orig_video_cols; + unsigned short orig_video_ega_ax; + unsigned short orig_video_ega_bx; + unsigned short orig_video_ega_cx; + unsigned char orig_video_lines; +}; + +/* + * This is set up by the setup-routine at boot-time + */ +#define EXT_MEM_K (*(unsigned short *)0x90002) +#define DRIVE_INFO (*(struct drive_info *)0x90080) +#define SCREEN_INFO (*(struct screen_info *)0x90000) +#define RAMDISK_SIZE (*(unsigned short *)0x901F8) +#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 void puts(const char *); + +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) + return p; + puts("large kernel, low 1M tight..."); + free_mem_ptr = (long)input_data; + } +} + +void free(void *where) +{ /* Don't care */ +} + +static void scroll() +{ + int i; + + memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 ); + for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 ) + vidmem[i] = ' '; +} + +static void puts(const char *s) +{ + int x,y; + char c; + + x = SCREEN_INFO.orig_x; + y = SCREEN_INFO.orig_y; + + while ( ( c = *s++ ) != '\0' ) { + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } + } + + SCREEN_INFO.orig_x = x; + SCREEN_INFO.orig_y = y; +} + +__ptr_t memset(__ptr_t s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i<n;i++) ss[i] = c; +} + +__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src, + size_t __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__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; +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty + * and at least one byte is really needed. + */ +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]; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +void flush_window() +{ + if (outcnt == 0) return; + updcrc(window, outcnt); + + memcpy(&output_data[output_ptr], (char *)window, outcnt); + + 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) +{ +/* 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; + } + crc_32_tab[i] = c; + } +} + +void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +#define STACK_SIZE (4096) + +long user_stack [STACK_SIZE]; + +struct { + long * a; + 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; + + lines = SCREEN_INFO.orig_video_lines; + cols = SCREEN_INFO.orig_video_cols; + + if (EXT_MEM_K < 1024) error("<2M of mem\n"); + + output_data = (char *)0x100000; /* Points to 1M */ + output_ptr = 0; + + exit_code = 0; + test = 0; + input_ptr = 0; + part_nb = 0; + + clear_bufs(); + makecrc(); + + puts("Uncompressing Linux..."); + + method = get_method(0); + + work(0, 0); + + puts("done.\n"); + + puts("Now booting the kernel\n"); +} + +/* ======================================================================== + * 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 */ +{ + 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(); + } + + /* 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 */ ; + } + } else + error("unknown compression method"); + return method; +} diff --git a/arch/i386/boot/compressed/piggyback.c b/arch/i386/boot/compressed/piggyback.c new file mode 100644 index 000000000..40284118b --- /dev/null +++ b/arch/i386/boot/compressed/piggyback.c @@ -0,0 +1,81 @@ +/* + * linux/zBoot/piggyback.c + * + * (C) 1993 Hannu Savolainen + */ + +/* + * This program reads the compressed system image from stdin and + * encapsulates it into an object file written to the stdout. + */ + +#include <stdio.h> +#include <unistd.h> +#include <a.out.h> + +int main(int argc, char *argv[]) +{ + int c, n=0, len=0; + char tmp_buf[512*1024]; + + struct exec obj = {0x00640107}; /* object header */ + char string_names[] = {"_input_data\0_input_len\0"}; + + struct nlist var_names[2] = /* Symbol table */ + { + { /* _input_data */ + (char *)4, 7, 0, 0, 0 + }, + { /* _input_len */ + (char *)16, 7, 0, 0, 0 + } + }; + + + len = 0; + while ((n = read(0, &tmp_buf[len], sizeof(tmp_buf)-len+1)) > 0) + len += n; + + if (n==-1) + { + perror("stdin"); + exit(-1); + } + + if (len >= sizeof(tmp_buf)) + { + fprintf(stderr, "%s: Input too large\n", argv[0]); + exit(-1); + } + + fprintf(stderr, "Compressed size %d.\n", len); + +/* + * Output object header + */ + obj.a_data = len + sizeof(long); + obj.a_syms = sizeof(var_names); + write(1, (char *)&obj, sizeof(obj)); + +/* + * Output data segment (compressed system & len) + */ + write(1, tmp_buf, len); + write(1, (char *)&len, sizeof(len)); + +/* + * Output symbol table + */ + var_names[1].n_value = len; + write(1, (char *)&var_names, sizeof(var_names)); + +/* + * Output string table + */ + len = sizeof(string_names) + sizeof(len); + write(1, (char *)&len, sizeof(len)); + write(1, string_names, sizeof(string_names)); + + exit(0); + +} diff --git a/arch/i386/boot/compressed/unzip.c b/arch/i386/boot/compressed/unzip.c new file mode 100644 index 000000000..d4a6617cd --- /dev/null +++ b/arch/i386/boot/compressed/unzip.c @@ -0,0 +1,180 @@ +/* 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/compressed/xtract.c b/arch/i386/boot/compressed/xtract.c new file mode 100644 index 000000000..91de49ca7 --- /dev/null +++ b/arch/i386/boot/compressed/xtract.c @@ -0,0 +1,86 @@ +/* + * linux/zBoot/xtract.c + * + * Copyright (C) 1993 Hannu Savolainen + * + * Extracts the system image and writes it to the stdout. + * based on tools/build.c by Linus Torvalds + */ + +#include <stdio.h> /* fprintf */ +#include <string.h> +#include <stdlib.h> /* contains exit */ +#include <sys/types.h> /* unistd.h needs this */ +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> /* contains read/write */ +#include <fcntl.h> +#include <a.out.h> + +#define N_MAGIC_OFFSET 1024 + +static int GCC_HEADER = sizeof(struct exec); + +#define STRINGIFY(x) #x + +void die(char * str) +{ + fprintf(stderr,"%s\n",str); + exit(1); +} + +void usage(void) +{ + die("Usage: xtract system [ | gzip | piggyback > piggy.s]"); +} + +int main(int argc, char ** argv) +{ + int i,c,id, sz; + char buf[1024]; + char major_root, minor_root; + struct stat sb; + + struct exec *ex = (struct exec *)buf; + + if (argc != 2) + usage(); + + if ((id=open(argv[1],O_RDONLY,0))<0) + die("Unable to open 'system'"); + if (read(id,buf,GCC_HEADER) != GCC_HEADER) + die("Unable to read header of 'system'"); + if (N_MAGIC(*ex) == ZMAGIC) { + GCC_HEADER = N_MAGIC_OFFSET; + lseek(id, GCC_HEADER, SEEK_SET); + } else if (N_MAGIC(*ex) != QMAGIC) + die("Non-GCC header of 'system'"); + + sz = N_SYMOFF(*ex) - GCC_HEADER + 4; /* +4 to get the same result than tools/build */ + + fprintf(stderr, "System size is %d\n", sz); + + while (sz) + { + int l, n; + + l = sz; + if (l > sizeof(buf)) l = sizeof(buf); + + if ((n=read(id, buf, l)) !=l) + { + if (n == -1) + perror(argv[1]); + else + fprintf(stderr, "Unexpected EOF\n"); + + die("Can't read system"); + } + + write(1, buf, l); + sz -= l; + } + + close(id); + return(0); +} diff --git a/arch/i386/boot/install.sh b/arch/i386/boot/install.sh new file mode 100644 index 000000000..346ea8ff7 --- /dev/null +++ b/arch/i386/boot/install.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# +# arch/i386/boot/install.sh +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# +# "make install" script for i386 architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install path (blank if root directory) +# + +# User may have a custom install script + +if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi + +# Default install - same as make zlilo + +if [ -f $4/vmlinuz ]; then + mv $4/vmlinuz $4/vmlinuz.old +fi + +if [ -f $4/System.map ]; then + mv $4/System.map $4/System.old +fi + +cat $2 > $4/vmlinuz +cp $3 $4/System.map + +if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi diff --git a/arch/i386/boot/tools/build.c b/arch/i386/boot/tools/build.c new file mode 100644 index 000000000..31277a019 --- /dev/null +++ b/arch/i386/boot/tools/build.c @@ -0,0 +1,238 @@ +/* + * linux/tools/build.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * This file builds a disk-image from three different files: + * + * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest + * - setup: 8086 machine code, sets up system parm + * - system: 80386 code for actual system + * + * It does some checking that all files are of the correct type, and + * just writes the result to stdout, removing headers and padding to + * the right amount. It also writes some system data to stderr. + */ + +/* + * Changes by tytso to allow root device specification + */ + +#include <stdio.h> /* fprintf */ +#include <string.h> +#include <stdlib.h> /* contains exit */ +#include <sys/types.h> /* unistd.h needs this */ +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> /* contains read/write */ +#include <fcntl.h> +#include <linux/a.out.h> +#include <linux/config.h> + +#define MINIX_HEADER 32 + +#define N_MAGIC_OFFSET 1024 +static int GCC_HEADER = sizeof(struct exec); + +#define SYS_SIZE DEF_SYSSIZE + +#define DEFAULT_MAJOR_ROOT 0 +#define DEFAULT_MINOR_ROOT 0 + +/* max nr of sectors of setup: don't change unless you also change + * bootsect etc */ +#define SETUP_SECTS 4 + +#define STRINGIFY(x) #x + +typedef union { + long l; + short s[2]; + char b[4]; +} conv; + +long intel_long(long l) +{ + conv t; + + t.b[0] = l & 0xff; l >>= 8; + t.b[1] = l & 0xff; l >>= 8; + t.b[2] = l & 0xff; l >>= 8; + t.b[3] = l & 0xff; l >>= 8; + return t.l; +} + +short intel_short(short l) +{ + conv t; + + t.b[0] = l & 0xff; l >>= 8; + t.b[1] = l & 0xff; l >>= 8; + return t.s[0]; +} + +void die(char * str) +{ + fprintf(stderr,"%s\n",str); + exit(1); +} + +void usage(void) +{ + die("Usage: build bootsect setup system [rootdev] [> image]"); +} + +int main(int argc, char ** argv) +{ + int i,c,id, sz; + unsigned long sys_size; + char buf[1024]; + struct exec *ex = (struct exec *)buf; + char major_root, minor_root; + struct stat sb; + unsigned char setup_sectors; + + if ((argc < 4) || (argc > 5)) + usage(); + if (argc > 4) { + if (!strcmp(argv[4], "CURRENT")) { + if (stat("/", &sb)) { + perror("/"); + die("Couldn't stat /"); + } + major_root = major(sb.st_dev); + minor_root = minor(sb.st_dev); + } else if (strcmp(argv[4], "FLOPPY")) { + if (stat(argv[4], &sb)) { + perror(argv[4]); + die("Couldn't stat root device."); + } + major_root = major(sb.st_rdev); + minor_root = minor(sb.st_rdev); + } else { + major_root = 0; + minor_root = 0; + } + } else { + major_root = DEFAULT_MAJOR_ROOT; + minor_root = DEFAULT_MINOR_ROOT; + } + fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); + for (i=0;i<sizeof buf; i++) buf[i]=0; + if ((id=open(argv[1],O_RDONLY,0))<0) + die("Unable to open 'boot'"); + if (read(id,buf,MINIX_HEADER) != MINIX_HEADER) + die("Unable to read header of 'boot'"); + if (((long *) buf)[0]!=intel_long(0x04100301)) + die("Non-Minix header of 'boot'"); + if (((long *) buf)[1]!=intel_long(MINIX_HEADER)) + die("Non-Minix header of 'boot'"); + if (((long *) buf)[3] != 0) + die("Illegal data segment in 'boot'"); + if (((long *) buf)[4] != 0) + die("Illegal bss in 'boot'"); + if (((long *) buf)[5] != 0) + die("Non-Minix header of 'boot'"); + if (((long *) buf)[7] != 0) + die("Illegal symbol table in 'boot'"); + i=read(id,buf,sizeof buf); + fprintf(stderr,"Boot sector %d bytes.\n",i); + if (i != 512) + die("Boot block must be exactly 512 bytes"); + if ((*(unsigned short *)(buf+510)) != (unsigned short)intel_short(0xAA55)) + die("Boot block hasn't got boot flag (0xAA55)"); + buf[508] = (char) minor_root; + buf[509] = (char) major_root; + i=write(1,buf,512); + if (i!=512) + die("Write call failed"); + close (id); + + if ((id=open(argv[2],O_RDONLY,0))<0) + die("Unable to open 'setup'"); + if (read(id,buf,MINIX_HEADER) != MINIX_HEADER) + die("Unable to read header of 'setup'"); + if (((long *) buf)[0]!=intel_long(0x04100301)) + die("Non-Minix header of 'setup'"); + if (((long *) buf)[1]!=intel_long(MINIX_HEADER)) + die("Non-Minix header of 'setup'"); + if (((long *) buf)[3] != 0) + die("Illegal data segment in 'setup'"); + if (((long *) buf)[4] != 0) + die("Illegal bss in 'setup'"); + if (((long *) buf)[5] != 0) + die("Non-Minix header of 'setup'"); + if (((long *) buf)[7] != 0) + die("Illegal symbol table in 'setup'"); + for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c ) + if (write(1,buf,c)!=c) + die("Write call failed"); + if (c != 0) + die("read-error on 'setup'"); + close (id); + setup_sectors = (unsigned char)((i + 511) / 512); + /* for compatibility with LILO */ + if (setup_sectors < SETUP_SECTS) + setup_sectors = SETUP_SECTS; + fprintf(stderr,"Setup is %d bytes.\n",i); + for (c=0 ; c<sizeof(buf) ; c++) + buf[c] = '\0'; + while (i < setup_sectors * 512) { + c = setup_sectors * 512 - i; + if (c > sizeof(buf)) + c = sizeof(buf); + if (write(1,buf,c) != c) + die("Write call failed"); + i += c; + } + + if ((id=open(argv[3],O_RDONLY,0))<0) + die("Unable to open 'system'"); + if (read(id,buf,GCC_HEADER) != GCC_HEADER) + die("Unable to read header of 'system'"); + if (N_MAGIC(*ex) == ZMAGIC) { + GCC_HEADER = N_MAGIC_OFFSET; + lseek(id, GCC_HEADER, SEEK_SET); + } else if (N_MAGIC(*ex) != QMAGIC) + die("Non-GCC header of 'system'"); + fprintf(stderr,"System is %d kB (%d kB code, %d kB data and %d kB bss)\n", + (ex->a_text+ex->a_data+ex->a_bss)/1024, + ex->a_text /1024, + ex->a_data /1024, + ex->a_bss /1024); + sz = N_SYMOFF(*ex) - GCC_HEADER + 4; + sys_size = (sz + 15) / 16; + if (sys_size > SYS_SIZE) + die("System is too big"); + while (sz > 0) { + int l, n; + + l = sz; + if (l > sizeof(buf)) + l = sizeof(buf); + if ((n=read(id, buf, l)) != l) { + if (n == -1) + perror(argv[1]); + else + fprintf(stderr, "Unexpected EOF\n"); + die("Can't read 'system'"); + } + if (write(1, buf, l) != l) + die("Write failed"); + sz -= l; + } + close(id); + if (lseek(1, 497, 0) == 497) { + if (write(1, &setup_sectors, 1) != 1) + die("Write of setup sectors failed"); + } + if (lseek(1,500,0) == 500) { + buf[0] = (sys_size & 0xff); + buf[1] = ((sys_size >> 8) & 0xff); + if (write(1, buf, 2) != 2) + die("Write failed"); + } + return(0); +} diff --git a/arch/i386/bootsect.S b/arch/i386/bootsect.S deleted file mode 100644 index f6a0d3158..000000000 --- a/arch/i386/bootsect.S +++ /dev/null @@ -1,460 +0,0 @@ -! -! SYS_SIZE is the number of clicks (16 bytes) to be loaded. -! 0x7F00 is 0x7F000 bytes = 508kB, more than enough for current -! versions of linux which compress the kernel -! -#include <linux/config.h> -SYSSIZE = DEF_SYSSIZE -! -! bootsect.s Copyright (C) 1991, 1992 Linus Torvalds -! modified by Drew Eckhardt -! modified by Bruce Evans (bde) -! -! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves -! itself out of the way to address 0x90000, and jumps there. -! -! bde - should not jump blindly, there may be systems with only 512K low -! memory. Use int 0x12 to get the top of memory, etc. -! -! It then loads 'setup' directly after itself (0x90200), and the system -! at 0x10000, using BIOS interrupts. -! -! NOTE! currently system is at most (8*65536-4096) bytes long. This should -! be no problem, even in the future. I want to keep it simple. This 508 kB -! kernel size should be enough, especially as this doesn't contain the -! buffer cache as in minix (and especially now that the kernel is -! compressed :-) -! -! The loader has been made as simple as possible, and continuous -! read errors will result in a unbreakable loop. Reboot by hand. It -! loads pretty fast by getting whole tracks at a time whenever possible. - -.text - -SETUPSECS = 4 ! nr of setup-sectors -BOOTSEG = 0x07C0 ! original address of boot-sector -INITSEG = DEF_INITSEG ! we move boot here - out of the way -SETUPSEG = DEF_SETUPSEG ! setup starts here -SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536). - -! ROOT_DEV & SWAP_DEV are now written by "build". -ROOT_DEV = 0 -SWAP_DEV = 0 -#ifndef SVGA_MODE -#define SVGA_MODE ASK_VGA -#endif -#ifndef RAMDISK -#define RAMDISK 0 -#endif -#ifndef CONFIG_ROOT_RDONLY -#define CONFIG_ROOT_RDONLY 0 -#endif - -! ld86 requires an entry symbol. This may as well be the usual one. -.globl _main -_main: -#if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */ - int 3 -#endif - mov ax,#BOOTSEG - mov ds,ax - mov ax,#INITSEG - mov es,ax - mov cx,#256 - sub si,si - sub di,di - cld - rep - movsw - jmpi go,INITSEG - -! ax and es already contain INITSEG - -go: mov di,#0x4000-12 ! 0x4000 is arbitrary value >= length of - ! bootsect + length of setup + room for stack - ! 12 is disk parm size - -! bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We -! wouldn't have to worry about this if we checked the top of memory. Also -! my BIOS can be configured to put the wini drive tables in high memory -! instead of in the vector table. The old stack might have clobbered the -! drive table. - - mov ds,ax - mov ss,ax ! put stack at INITSEG:0x4000-12. - mov sp,di -/* - * Many BIOS's default disk parameter tables will not - * recognize multi-sector reads beyond the maximum sector number - * specified in the default diskette parameter tables - this may - * mean 7 sectors in some cases. - * - * Since single sector reads are slow and out of the question, - * we must take care of this by creating new parameter tables - * (for the first disk) in RAM. We will set the maximum sector - * count to 36 - the most we will encounter on an ED 2.88. - * - * High doesn't hurt. Low does. - * - * Segments are as follows: ds=es=ss=cs - INITSEG, - * fs = 0, gs is unused. - */ - -! cx contains 0 from rep movsw above - - mov fs,cx - mov bx,#0x78 ! fs:bx is parameter table address - push ds - seg fs - lds si,(bx) ! ds:si is source - - mov cl,#6 ! copy 12 bytes - cld - push di - - rep - movsw - - pop di - pop ds - - movb 4(di),*36 ! patch sector count - - seg fs - mov (bx),di - seg fs - mov 2(bx),es - -! load the setup-sectors directly after the bootblock. -! Note that 'es' is already set up. -! Also cx is 0 from rep movsw above. - -load_setup: - xor ah,ah ! reset FDC - xor dl,dl - int 0x13 - - xor dx, dx ! drive 0, head 0 - mov cl,#0x02 ! sector 2, track 0 - mov bx,#0x0200 ! address = 512, in INITSEG - mov ah,#0x02 ! service 2, nr of sectors - mov al,setup_sects ! (assume all on head 0, track 0) - int 0x13 ! read it - jnc ok_load_setup ! ok - continue - - push ax ! dump error code - call print_nl - mov bp, sp - call print_hex - pop ax - - jmp load_setup - -ok_load_setup: - -! Get disk drive parameters, specifically nr of sectors/track - -#if 0 - -! bde - the Phoenix BIOS manual says function 0x08 only works for fixed -! disks. It doesn't work for one of my BIOS's (1987 Award). It was -! fatal not to check the error code. - - xor dl,dl - mov ah,#0x08 ! AH=8 is get drive parameters - int 0x13 - xor ch,ch -#else - -! It seems that there is no BIOS call to get the number of sectors. Guess -! 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read, -! 15 if sector 15 can be read. Otherwise guess 9. - - mov si,#disksizes ! table of sizes to try - -probe_loop: - lodsb - cbw ! extend to word - mov sectors, ax - cmp si,#disksizes+4 - jae got_sectors ! if all else fails, try 9 - xchg ax, cx ! cx = track and sector - xor dx, dx ! drive 0, head 0 - xor bl, bl - mov bh,setup_sects - inc bh - shl bh,#1 ! address after setup (es = cs) - mov ax,#0x0201 ! service 2, 1 sector - int 0x13 - jc probe_loop ! try next value - -#endif - -got_sectors: - -! Restore es - - mov ax,#INITSEG - mov es,ax - -! Print some inane message - - mov ah,#0x03 ! read cursor pos - xor bh,bh - int 0x10 - - mov cx,#9 - mov bx,#0x0007 ! page 0, attribute 7 (normal) - mov bp,#msg1 - mov ax,#0x1301 ! write string, move cursor - int 0x10 - -! ok, we've written the message, now -! we want to load the system (at 0x10000) - - mov ax,#SYSSEG - mov es,ax ! segment of 0x010000 - call read_it - call kill_motor - call print_nl - -! After that we check which root-device to use. If the device is -! defined (!= 0), nothing is done and the given device is used. -! Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8), -! depending on the number of sectors we pretend to know we have. - - seg cs - mov ax,root_dev - or ax,ax - jne root_defined - seg cs - mov bx,sectors - mov ax,#0x0208 ! /dev/ps0 - 1.2Mb - cmp bx,#15 - je root_defined - mov al,#0x1c ! /dev/PS0 - 1.44Mb - cmp bx,#18 - je root_defined - mov al,#0x20 ! /dev/fd0H2880 - 2.88Mb - cmp bx,#36 - je root_defined - mov al,#0 ! /dev/fd0 - autodetect -root_defined: - seg cs - mov root_dev,ax - -! after that (everything loaded), we jump to -! the setup-routine loaded directly after -! the bootblock: - - jmpi 0,SETUPSEG - -! This routine loads the system at address 0x10000, making sure -! no 64kB boundaries are crossed. We try to load it as fast as -! possible, loading whole tracks whenever we can. -! -! in: es - starting address segment (normally 0x1000) -! -sread: .word 0 ! sectors read of current track -head: .word 0 ! current head -track: .word 0 ! current track - -read_it: - mov al,setup_sects - inc al - mov sread,al - mov ax,es - test ax,#0x0fff -die: jne die ! es must be at 64kB boundary - xor bx,bx ! bx is starting address within segment -rp_read: - mov ax,es - sub ax,#SYSSEG - cmp ax,syssize ! have we loaded all yet? - jbe ok1_read - ret -ok1_read: - mov ax,sectors - sub ax,sread - mov cx,ax - shl cx,#9 - add cx,bx - jnc ok2_read - je ok2_read - xor ax,ax - sub ax,bx - shr ax,#9 -ok2_read: - call read_track - mov cx,ax - add ax,sread - cmp ax,sectors - jne ok3_read - mov ax,#1 - sub ax,head - jne ok4_read - inc track -ok4_read: - mov head,ax - xor ax,ax -ok3_read: - mov sread,ax - shl cx,#9 - add bx,cx - jnc rp_read - mov ax,es - add ah,#0x10 - mov es,ax - xor bx,bx - jmp rp_read - -read_track: - pusha - pusha - mov ax, #0xe2e ! loading... message 2e = . - mov bx, #7 - int 0x10 - popa - - mov dx,track - mov cx,sread - inc cx - mov ch,dl - mov dx,head - mov dh,dl - and dx,#0x0100 - mov ah,#2 - - push dx ! save for error dump - push cx - push bx - push ax - - int 0x13 - jc bad_rt - add sp, #8 - popa - ret - -bad_rt: push ax ! save error code - call print_all ! ah = error, al = read - - - xor ah,ah - xor dl,dl - int 0x13 - - - add sp, #10 - popa - jmp read_track - -/* - * print_all is for debugging purposes. - * It will print out all of the registers. The assumption is that this is - * called from a routine, with a stack frame like - * dx - * cx - * bx - * ax - * error - * ret <- sp - * -*/ - -print_all: - mov cx, #5 ! error code + 4 registers - mov bp, sp - -print_loop: - push cx ! save count left - call print_nl ! nl for readability - - cmp cl, #5 - jae no_reg ! see if register name is needed - - mov ax, #0xe05 + 'A - 1 - sub al, cl - int 0x10 - - mov al, #'X - int 0x10 - - mov al, #': - int 0x10 - -no_reg: - add bp, #2 ! next register - call print_hex ! print it - pop cx - loop print_loop - ret - -print_nl: - mov ax, #0xe0d ! CR - int 0x10 - mov al, #0xa ! LF - int 0x10 - ret - -/* - * print_hex is for debugging purposes, and prints the word - * pointed to by ss:bp in hexadecimal. -*/ - -print_hex: - mov cx, #4 ! 4 hex digits - mov dx, (bp) ! load word into dx -print_digit: - rol dx, #4 ! rotate so that lowest 4 bits are used - mov ax, #0xe0f ! ah = request, al = mask for nybble - and al, dl - add al, #0x90 ! convert al to ascii hex (four instructions) - daa - adc al, #0x40 - daa - int 0x10 - loop print_digit - ret - - -/* - * This procedure turns off the floppy drive motor, so - * that we enter the kernel in a known state, and - * don't have to worry about it later. - */ -kill_motor: - push dx - mov dx,#0x3f2 - xor al, al - outb - pop dx - ret - -sectors: - .word 0 - -disksizes: - .byte 36,18,15,9 - -msg1: - .byte 13,10 - .ascii "Loading" - -.org 497 -setup_sects: - .byte SETUPSECS -root_flags: - .word CONFIG_ROOT_RDONLY -syssize: - .word SYSSIZE -swap_dev: - .word SWAP_DEV -ram_size: - .word RAMDISK -vid_mode: - .word SVGA_MODE -root_dev: - .word ROOT_DEV -boot_flag: - .word 0xAA55 diff --git a/arch/i386/config.in b/arch/i386/config.in index 2c4bc4ba5..bfbf42acb 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -7,32 +7,68 @@ comment 'General setup' bool 'Kernel math emulation' CONFIG_MATH_EMULATION n bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y -bool 'Normal harddisk support' CONFIG_BLK_DEV_HD 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 + +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 +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 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n -bool 'Reverse ARP' CONFIG_INET_RARP n -bool 'Assume subnets are local' CONFIG_INET_SNARL y -bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n +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 'Amateur Radio AX.25 Level 2' CONFIG_AX25 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 fi comment 'SCSI support' -bool 'SCSI support?' CONFIG_SCSI n +bool 'SCSI support?' CONFIG_SCSI y if [ "$CONFIG_SCSI" = "n" ]; then @@ -42,22 +78,29 @@ 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 n -bool 'Scsi generic support' CONFIG_CHR_DEV_SG n +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' -bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n -bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y -bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n +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 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +if [ "$CONFIG_PCI" = "y" ]; then + bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +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 @@ -65,7 +108,7 @@ bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE 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 'EISA EATA support' CONFIG_SCSI_EATA 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 fi @@ -80,21 +123,28 @@ if [ "$CONFIG_NETDEVICES" = "n" ]; then comment 'Skipping network driver configuration options...' else -bool 'Dummy net driver support' CONFIG_DUMMY n +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' SL_COMPRESSED y -# bool ' SLIP debugging on' SL_DUMP y + 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 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING 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 @@ -107,40 +157,58 @@ if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then fi bool 'Other ISA cards' CONFIG_NET_ISA n if [ "$CONFIG_NET_ISA" = "y" ]; then - bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n - bool 'Cabletron E21xx support (not recommended)' CONFIG_E2100 n + 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 'EtherExpress support' CONFIG_EEXPRESS n 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 support' CONFIG_HPLAN n - bool 'HP PCLAN PLUS support' CONFIG_HPLAN_PLUS n + 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 and on board controllers' CONFIG_NET_EISA n +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 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n - bool 'Zenith Z-Note support' CONFIG_ZNET 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 fi -comment 'CD-ROM drivers' +comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n -bool 'Mitsumi CDROM driver support' CONFIG_MCD 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 @@ -151,6 +219,8 @@ if [ "$CONFIG_SBPCD" = "y" ]; then fi fi fi +bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n +bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n comment 'Filesystems' @@ -160,13 +230,14 @@ 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 +#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' 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" ]; then +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 @@ -176,6 +247,7 @@ bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n comment 'character devices' +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 @@ -184,7 +256,7 @@ bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y fi bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n -bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION n + bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n if [ "$CONFIG_QIC02_TAPE" = "y" ]; then @@ -213,7 +285,10 @@ bool 'Sound card support' CONFIG_SOUND n comment 'Kernel hacking' #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n -bool 'Kernel profiling support' CONFIG_PROFILE n +bool 'Kernel profiling support' CONFIG_PROFILE y +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 +bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y fi diff --git a/arch/i386/dummy.c b/arch/i386/dummy.c deleted file mode 100644 index dd2410a7a..000000000 --- a/arch/i386/dummy.c +++ /dev/null @@ -1,11 +0,0 @@ -/* - * This file handles Systemcalls not available for all CPUs. - * - * Written by Ralf Baechle, - * Copyright (C) 1994 by Waldorf GMBH - */ - -/* - * Nothing yet for i386... - */ - diff --git a/arch/i386/head.S b/arch/i386/head.S deleted file mode 100644 index e720c14d0..000000000 --- a/arch/i386/head.S +++ /dev/null @@ -1,349 +0,0 @@ -/* - * linux/boot/head.S - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * head.S contains the 32-bit startup code. - */ - -.text -.globl _idt,_gdt, -.globl _swapper_pg_dir,_pg0 -.globl _empty_bad_page -.globl _empty_bad_page_table -.globl _empty_zero_page -.globl _floppy_track_buffer - -#include <linux/tasks.h> -#include <linux/segment.h> -#define ASSEMBLER -#include <linux/fd.h> - -#define CL_MAGIC_ADDR 0x90020 -#define CL_MAGIC 0xA33F -#define CL_BASE_ADDR 0x90000 -#define CL_OFFSET 0x90022 - -/* - * swapper_pg_dir is the main page directory, address 0x00001000 (or at - * address 0x00101000 for a compressed boot). - */ -startup_32: - cld - movl $(KERNEL_DS),%eax - mov %ax,%ds - mov %ax,%es - mov %ax,%fs - mov %ax,%gs - lss _stack_start,%esp -/* - * Clear BSS first so that there are no surprises... - */ - xorl %eax,%eax - movl $__edata,%edi - movl $__end,%ecx - subl %edi,%ecx - cld - rep - stosb -/* - * start system 32-bit setup. We need to re-do some of the things done - * in 16-bit mode for the "real" operations. - */ - call setup_idt - xorl %eax,%eax -1: incl %eax # check that A20 really IS enabled - movl %eax,0x000000 # loop forever if it isn't - cmpl %eax,0x100000 - je 1b -/* - * Initialize eflags. Some BIOS's leave bits like NT set. This would - * confuse the debugger if this code is traced. - * XXX - best to initialize before switching to protected mode. - */ - pushl $0 - popfl -/* - * Copy bootup parameters out of the way. First 2kB of - * _empty_zero_page is for boot parameters, second 2kB - * is for the command line. - */ - movl $0x90000,%esi - movl $_empty_zero_page,%edi - movl $512,%ecx - cld - rep - movsl - xorl %eax,%eax - movl $512,%ecx - rep - stosl - cmpw $(CL_MAGIC),CL_MAGIC_ADDR - jne 1f - movl $_empty_zero_page+2048,%edi - movzwl CL_OFFSET,%esi - addl $(CL_BASE_ADDR),%esi - movl $2048,%ecx - rep - movsb -1: -/* 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 %esp,%edi # save stack pointer - andl $0xfffffffc,%esp # align stack to avoid AC fault - movl $3,_x86 - pushfl # push EFLAGS - popl %eax # get EFLAGS - movl %eax,%ecx # save original EFLAGS - xorl $0x40000,%eax # flip AC bit in EFLAGS - pushl %eax # copy to EFLAGS - popfl # set EFLAGS - pushfl # get new EFLAGS - popl %eax # put it in eax - xorl %ecx,%eax # change in flags - andl $0x40000,%eax # check if AC bit changed - je is386 - movl $4,_x86 - movl %ecx,%eax - xorl $0x200000,%eax # check ID flag - pushl %eax - popfl # if we are on a straight 486DX, SX, or - pushfl # 487SX we can't change it - popl %eax - xorl %ecx,%eax - andl $0x200000,%eax - je is486 -isnew: pushl %ecx # restore original EFLAGS - popfl - movl $1, %eax # Use the CPUID instruction to - .byte 0x0f, 0xa2 # check the processor type - andl $0xf00, %eax # Set _x86 with the family - shrl $8, %eax # returned. - movl %eax, _x86 - movl %edi,%esp # restore esp - movl %cr0,%eax # 486+ - andl $0x80000011,%eax # Save PG,PE,ET - orl $0x50022,%eax # set AM, WP, NE and MP - jmp 2f -is486: pushl %ecx # restore original EFLAGS - popfl - movl %edi,%esp # restore esp - movl %cr0,%eax # 486 - andl $0x80000011,%eax # Save PG,PE,ET - orl $0x50022,%eax # set AM, WP, NE and MP - jmp 2f -is386: pushl %ecx # restore original EFLAGS - popfl - movl %edi,%esp # restore esp - movl %cr0,%eax # 386 - andl $0x80000011,%eax # Save PG,PE,ET - orl $2,%eax # set MP -2: movl %eax,%cr0 - call check_x87 - call setup_paging - lgdt gdt_descr - lidt idt_descr - ljmp $(KERNEL_CS),$1f -1: movl $(KERNEL_DS),%eax # reload all the segment registers - mov %ax,%ds # after changing gdt. - mov %ax,%es - mov %ax,%fs - mov %ax,%gs - lss _stack_start,%esp - 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 -L6: - jmp L6 # main should never return here, but - # just in case, we know what happens. - -/* - * We depend on ET to be correct. This checks for 287/387. - */ -check_x87: - movl $0,_hard_math - clts - fninit - fstsw %ax - cmpb $0,%al - je 1f - movl %cr0,%eax /* no coprocessor: have to set bits */ - xorl $4,%eax /* set EM */ - movl %eax,%cr0 - ret -.align 2 -1: movl $1,_hard_math - .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ - ret - -/* - * setup_idt - * - * sets up a idt with 256 entries pointing to - * ignore_int, interrupt gates. It doesn't actually load - * idt - that can be done only after paging has been enabled - * and the kernel moved to 0xC0000000. Interrupts - * are enabled elsewhere, when we can be relatively - * sure everything is ok. - */ -setup_idt: - lea ignore_int,%edx - movl $(KERNEL_CS << 16),%eax - movw %dx,%ax /* selector = 0x0010 = cs */ - movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ - - lea _idt,%edi - mov $256,%ecx -rp_sidt: - movl %eax,(%edi) - movl %edx,4(%edi) - addl $8,%edi - dec %ecx - jne rp_sidt - ret - - -/* - * 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, - * 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..... - */ -.org 0x1000 -_swapper_pg_dir: -/* - * The page tables are initialized to only 4MB here - the final page - * tables are set up later depending on memory size. - */ -.org 0x2000 -_pg0: - -.org 0x3000 -_empty_bad_page: - -.org 0x4000 -_empty_bad_page_table: - -.org 0x5000 -_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 - -/* This is the default interrupt "handler" :-) */ -int_msg: - .asciz "Unknown interrupt\n" -.align 2 -ignore_int: - cld - pushl %eax - pushl %ecx - pushl %edx - push %ds - push %es - push %fs - movl $(KERNEL_DS),%eax - mov %ax,%ds - mov %ax,%es - mov %ax,%fs - pushl $int_msg - call _printk - popl %eax - pop %fs - pop %es - pop %ds - popl %edx - popl %ecx - popl %eax - iret - -/* - * The interrupt descriptor table has room for 256 idt's - */ -.align 4 -.word 0 -idt_descr: - .word 256*8-1 # idt contains 256 entries - .long 0xc0000000+_idt - -.align 4 -_idt: - .fill 256,8,0 # idt is uninitialized - -.align 4 -.word 0 -gdt_descr: - .word (8+2*NR_TASKS)*8-1 - .long 0xc0000000+_gdt - -/* - * This gdt setup gives the kernel a 1GB address space at virtual - * address 0xC0000000 - space enough for expansion, I hope. - */ -.align 4 -_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 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 */ diff --git a/arch/i386/ibcs/Makefile b/arch/i386/ibcs/Makefile new file mode 100644 index 000000000..7b81ff2bc --- /dev/null +++ b/arch/i386/ibcs/Makefile @@ -0,0 +1,37 @@ +# +# 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 new file mode 100644 index 000000000..1a7c760cd --- /dev/null +++ b/arch/i386/ibcs/binfmt_coff.c @@ -0,0 +1,784 @@ +/* + * 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 new file mode 100644 index 000000000..ab6d36915 --- /dev/null +++ b/arch/i386/ibcs/binfmt_elf.c @@ -0,0 +1,655 @@ +/* + * 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 new file mode 100644 index 000000000..dc3b3d576 --- /dev/null +++ b/arch/i386/ibcs/emulate.c @@ -0,0 +1,10 @@ +/* + * 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/ioport.c b/arch/i386/ioport.c deleted file mode 100644 index c61690e3c..000000000 --- a/arch/i386/ioport.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * linux/kernel/ioport.c - * - * This contains the io-permission bitmap code - written by obz, with changes - * by Linus. - */ - -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/ioport.h> - -static unsigned long ioport_registrar[IO_BITMAP_SIZE] = {0, /* ... */}; - -#define _IODEBUG - -#ifdef IODEBUG -static char * ios(unsigned long l) -{ - static char str[33] = { '\0' }; - int i; - unsigned long mask; - - for (i = 0, mask = 0x80000000; i < 32; ++i, mask >>= 1) - str[i] = (l & mask) ? '1' : '0'; - return str; -} - -static void dump_io_bitmap(void) -{ - int i, j; - int numl = sizeof(current->tss.io_bitmap) >> 2; - - for (i = j = 0; j < numl; ++i) - { - printk("%4d [%3x]: ", 64*i, 64*i); - printk("%s ", ios(current->tss.io_bitmap[j++])); - if (j < numl) - printk("%s", ios(current->tss.io_bitmap[j++])); - printk("\n"); - } -} -#endif - -/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ -asmlinkage void set_bitmap(unsigned long *bitmap, - short base, short extent, int new_value) -{ - int mask; - unsigned long *bitmap_base = bitmap + (base >> 5); - unsigned short low_index = base & 0x1f; - int length = low_index + extent; - - if (low_index != 0) { - mask = (~0 << low_index); - if (length < 32) - mask &= ~(~0 << length); - if (new_value) - *bitmap_base++ |= mask; - else - *bitmap_base++ &= ~mask; - length -= 32; - } - - mask = (new_value ? ~0 : 0); - while (length >= 32) { - *bitmap_base++ = mask; - length -= 32; - } - - if (length > 0) { - mask = ~(~0 << length); - if (new_value) - *bitmap_base++ |= mask; - else - *bitmap_base++ &= ~mask; - } -} - -/* Check for set bits in BITMAP starting at BASE, going to EXTENT. */ -asmlinkage int check_bitmap(unsigned long *bitmap, short base, short extent) -{ - int mask; - unsigned long *bitmap_base = bitmap + (base >> 5); - unsigned short low_index = base & 0x1f; - int length = low_index + extent; - - if (low_index != 0) { - mask = (~0 << low_index); - if (length < 32) - mask &= ~(~0 << length); - if (*bitmap_base++ & mask) - return 1; - length -= 32; - } - while (length >= 32) { - if (*bitmap_base++ != 0) - return 1; - length -= 32; - } - - if (length > 0) { - mask = ~(~0 << length); - if (*bitmap_base++ & mask) - return 1; - } - return 0; -} - -/* - * this changes the io permissions bitmap in the current task. - */ -asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) -{ - if (from + num <= from) - return -EINVAL; - if (from + num > IO_BITMAP_SIZE*32) - return -EINVAL; - if (!suser()) - return -EPERM; - -#ifdef IODEBUG - printk("io: from=%d num=%d %s\n", from, num, (turn_on ? "on" : "off")); -#endif - set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on); - return 0; -} - -unsigned int *stack; - -/* - * sys_iopl has to be used when you want to access the IO ports - * beyond the 0x3ff range: to get the full 65536 ports bitmapped - * you'd need 8kB of bitmaps/process, which is a bit excessive. - * - * Here we just change the eflags value on the stack: we allow - * only the super-user to do it. This depends on the stack-layout - * on system-call entry - see also fork() and the signal handling - * code. - */ -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) -{ - unsigned int level = ebx; - - if (level > 3) - return -EINVAL; - if (!suser()) - return -EPERM; - *(&eflags) = (eflags & 0xffffcfff) | (level << 12); - return 0; -} - - -void snarf_region(unsigned int from, unsigned int num) -{ - if (from > IO_BITMAP_SIZE*32) - return; - if (from + num > IO_BITMAP_SIZE*32) - num = IO_BITMAP_SIZE*32 - from; - set_bitmap(ioport_registrar, from, num, 1); - return; -} - -void release_region(unsigned int from, unsigned int num) -{ - if (from > IO_BITMAP_SIZE*32) - return; - if (from + num > IO_BITMAP_SIZE*32) - num = IO_BITMAP_SIZE*32 - from; - set_bitmap(ioport_registrar, from, num, 0); - return; -} - -int check_region(unsigned int from, unsigned int num) -{ - if (from > IO_BITMAP_SIZE*32) - return 0; - if (from + num > IO_BITMAP_SIZE*32) - num = IO_BITMAP_SIZE*32 - from; - return check_bitmap(ioport_registrar, from, num); -} - -/* Called from init/main.c to reserve IO ports. */ -void reserve_setup(char *str, int *ints) -{ - int i; - - for (i = 1; i < ints[0]; i += 2) - snarf_region(ints[i], ints[i+1]); -} diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile new file mode 100644 index 000000000..701926d26 --- /dev/null +++ b/arch/i386/kernel/Makefile @@ -0,0 +1,48 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# 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.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 + +all: kernel.o head.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/linux/tasks.h + $(CPP) -traditional -o $*.s $< + +kernel.o: $(OBJS) + $(LD) -r -o kernel.o $(OBJS) + sync + +dep: + $(CPP) -M *.c > .depend + +modules: + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + + diff --git a/arch/i386/bios32.c b/arch/i386/kernel/bios32.c index 311dd111e..8cc3d76fa 100644 --- a/arch/i386/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -32,25 +32,31 @@ * CHANGELOG : * Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION * Revision 2.0 present on <thys@dennis.ee.up.ac.za>'s ASUS mainboard. + * + * Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic + * Potter, potter@cao-vlsi.ibp.fr + * + * Jan 10, 1995 : Modified to store the information about configured pci + * devices into a list, which can be accessed via /proc/pci by + * Curtis Varner, cvarner@cs.ucr.edu + * + * Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter. + * Alpha version. Intel & UMC chipset support only. + * + * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code + * moved to drivers/pci/pci.c. + * + * */ #include <linux/config.h> +#include <linux/types.h> #include <linux/kernel.h> #include <linux/bios32.h> #include <linux/pci.h> #include <asm/segment.h> -/* - * It would seem some PCI bioses are buggy, so we don't actually use these - * routines unless we need to.. - */ -#ifdef CONFIG_SCSI_NCR53C7xx - #define CONFIG_PCI -#else - #undef CONFIG_PCI -#endif - #define PCIBIOS_PCI_FUNCTION_ID 0xb1XX #define PCIBIOS_PCI_BIOS_PRESENT 0xb101 #define PCIBIOS_FIND_PCI_DEVICE 0xb102 @@ -63,6 +69,7 @@ #define PCIBIOS_WRITE_CONFIG_WORD 0xb10c #define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d + /* BIOS32 signature: "_32_" */ #define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) @@ -97,7 +104,7 @@ union bios32 { /* * Physical address of the service directory. I don't know if we're * allowed to have more than one of these or not, so just in case - * we'll make bios32_init() take a memory start parameter and store + * we'll make pcibios_present() take a memory start parameter and store * the array there. */ @@ -147,9 +154,8 @@ static struct { unsigned short segment; } pci_indirect = { 0, KERNEL_CS }; -void NCR53c810_test(void); -static unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) +extern unsigned long check_pcibios(unsigned long memory_start, unsigned long memory_end) { unsigned long signature; unsigned char present_status; @@ -191,10 +197,6 @@ static unsigned long pcibios_init(unsigned long memory_start, unsigned long memo major_revision, minor_revision, pcibios_entry); } } - -#if 0 - NCR53c810_test(); -#endif return memory_start; } @@ -203,7 +205,7 @@ int pcibios_present(void) return pcibios_entry ? 1 : 0; } -int pcibios_find_class_code (unsigned long class_code, unsigned short index, +int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *device_fn) { unsigned long bx; @@ -286,7 +288,7 @@ int pcibios_read_config_word (unsigned char bus, } int pcibios_read_config_dword (unsigned char bus, - unsigned char device_fn, unsigned char where, unsigned long *value) + unsigned char device_fn, unsigned char where, unsigned int *value) { unsigned long ret; unsigned long bx = (bus << 8) | device_fn; @@ -343,7 +345,7 @@ int pcibios_write_config_word (unsigned char bus, } int pcibios_write_config_dword (unsigned char bus, - unsigned char device_fn, unsigned char where, unsigned long value) + unsigned char device_fn, unsigned char where, unsigned int value) { unsigned long ret; unsigned long bx = (bus << 8) | device_fn; @@ -361,36 +363,6 @@ int pcibios_write_config_dword (unsigned char bus, return (int) (ret & 0xff00) >> 8; } -void NCR53c810_test(void) -{ - unsigned char bus, device_fn; - unsigned short index; - int ret; - unsigned char row, col; - unsigned long val; - - for (index = 0; index < 4; ++index) { - ret = pcibios_find_device ( - (unsigned short) PCI_VENDOR_ID_NCR, - (unsigned short) PCI_DEVICE_ID_NCR_53C810, - index, &bus, &device_fn); - if (ret) - break; - printk ("ncr53c810 : at PCI bus %d, device %d, function %d.", - bus, ((device_fn & 0xf8) >> 3), (device_fn & 7)); - for (row = 0; row < 0x3c; row += 0x10) { - printk ("\n reg 0x%02x ", row); - for (col = 0; col < 0x10; col += 4) { - if (!(ret = pcibios_read_config_dword (bus, device_fn, row+col, &val))) - printk ("0x%08lx ", val); - else - printk ("error 0x%02x ", ret); - } - } - printk ("\n"); - } -} - char *pcibios_strerror (int error) { static char buf[80]; @@ -417,9 +389,16 @@ char *pcibios_strerror (int error) } } + +unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end) +{ +return mem_start; +} + + #endif -unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) +unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) { union bios32 *check; unsigned char sum; @@ -430,8 +409,6 @@ unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) * directory by scanning the permissible address range from * 0xe0000 through 0xfffff for a valid BIOS32 structure. * - * The PCI BIOS doesn't seem to work too well on many machines, - * so we disable this unless it's really needed (NCR SCSI driver) */ for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) { @@ -446,31 +423,35 @@ unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) if (sum != 0) continue; if (check->fields.revision != 0) { - printk("bios32_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n", + printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n", check->fields.revision, check); continue; } - printk ("bios32_init : BIOS32 Service Directory structure at 0x%p\n", check); + printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check); if (!bios32_entry) { - bios32_indirect.address = bios32_entry = check->fields.entry; - printk ("bios32_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); + 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; + printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); + } } else { - printk ("bios32_init : multiple entries, mail drew@colorado.edu\n"); + 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.. */ -#if 0 - return memory_start; -#endif } } #ifdef CONFIG_PCI if (bios32_entry) { - memory_start = pcibios_init (memory_start, memory_end); + memory_start = check_pcibios (memory_start, memory_end); } #endif return memory_start; } + + + diff --git a/arch/i386/entry.S b/arch/i386/kernel/entry.S index d7008a74b..1bdf062c8 100644 --- a/arch/i386/entry.S +++ b/arch/i386/kernel/entry.S @@ -40,7 +40,6 @@ * 40(%esp) - %oldss */ -#define __ASSEMBLY__ #include <linux/sys.h> #include <asm/segment.h> @@ -483,14 +482,14 @@ _sys_call_table: .long _sys_settimeofday .long _sys_getgroups /* 80 */ .long _sys_setgroups - .long _sys_select + .long _old_select .long _sys_symlink .long _sys_lstat .long _sys_readlink /* 85 */ .long _sys_uselib .long _sys_swapon .long _sys_reboot - .long _sys_readdir + .long _old_readdir .long _sys_mmap /* 90 */ .long _sys_munmap .long _sys_truncate @@ -542,4 +541,7 @@ _sys_call_table: .long _sys_setfsuid .long _sys_setfsgid .long _sys_llseek /* 140 */ + .long _sys_getdents + .long _sys_select + .long _sys_flock .space (NR_syscalls-140)*4 diff --git a/arch/i386/boot/head.S b/arch/i386/kernel/head.S index 896c62f18..a3e0df213 100644 --- a/arch/i386/boot/head.S +++ b/arch/i386/kernel/head.S @@ -1,5 +1,5 @@ /* - * linux/boot/head.S + * linux/arch/i386/head.S * * Copyright (C) 1991, 1992 Linus Torvalds */ @@ -9,7 +9,7 @@ */ .text -.globl _idt,_gdt, +.globl _idt,_gdt,_stext,__stext .globl _swapper_pg_dir,_pg0 .globl _empty_bad_page .globl _empty_bad_page_table @@ -30,6 +30,8 @@ * swapper_pg_dir is the main page directory, address 0x00001000 (or at * address 0x00101000 for a compressed boot). */ +_stext: +__stext: startup_32: cld movl $(KERNEL_DS),%eax @@ -37,7 +39,7 @@ startup_32: mov %ax,%es mov %ax,%fs mov %ax,%gs - lss _stack_start,%esp + lss stack_start,%esp /* * Clear BSS first so that there are no surprises... */ @@ -95,8 +97,6 @@ startup_32: * apply at our cpl of 0 and the stack ought to be aligned already, and * we don't need to preserve eflags. */ - movl %esp,%edi # save stack pointer - andl $0xfffffffc,%esp # align stack to avoid AC fault movl $3,_x86 pushfl # push EFLAGS popl %eax # get EFLAGS @@ -121,26 +121,37 @@ startup_32: je is486 isnew: pushl %ecx # restore original EFLAGS popfl + /* get processor type */ movl $1, %eax # Use the CPUID instruction to .byte 0x0f, 0xa2 # check the processor type - andl $0xf00, %eax # Set _x86 with the family - shrl $8, %eax # returned. - movl %eax, _x86 - movl %edi,%esp # restore esp + movb %al, %cl # save reg for future use + andb $0x0f,%ah # mask processor family + movb %ah, _x86 + andb $0xf0, %eax # mask model + shrb $4, %al + movb %al, _x86_model + andb $0x0f, %cl # mask mask revision + movb %cl, _x86_mask + movl %edx, _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 %cr0,%eax # 486+ andl $0x80000011,%eax # Save PG,PE,ET orl $0x50022,%eax # set AM, WP, NE and MP jmp 2f is486: pushl %ecx # restore original EFLAGS popfl - movl %edi,%esp # restore esp movl %cr0,%eax # 486 andl $0x80000011,%eax # Save PG,PE,ET orl $0x50022,%eax # set AM, WP, NE and MP jmp 2f is386: pushl %ecx # restore original EFLAGS popfl - movl %edi,%esp # restore esp movl %cr0,%eax # 386 andl $0x80000011,%eax # Save PG,PE,ET orl $2,%eax # set MP @@ -155,7 +166,7 @@ is386: pushl %ecx # restore original EFLAGS mov %ax,%es mov %ax,%fs mov %ax,%gs - lss _stack_start,%esp + lss stack_start,%esp xorl %eax,%eax lldt %ax pushl %eax # These are the parameters to main :-) @@ -171,7 +182,7 @@ L6: * We depend on ET to be correct. This checks for 287/387. */ check_x87: - movl $0,_hard_math + movb $0,_hard_math clts fninit fstsw %ax @@ -182,7 +193,7 @@ check_x87: movl %eax,%cr0 ret .align 2 -1: movl $1,_hard_math +1: movb $1,_hard_math .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ ret @@ -285,6 +296,10 @@ _empty_zero_page: */ _floppy_track_buffer: .fill 512*2*MAX_BUFFER_SECTORS,1,0 + +stack_start: + .long _init_user_stack+4096 + .long KERNEL_DS /* This is the default interrupt "handler" :-) */ int_msg: diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c new file mode 100644 index 000000000..c949f70cd --- /dev/null +++ b/arch/i386/kernel/ioport.c @@ -0,0 +1,89 @@ +/* + * linux/arch/i386/kernel/ioport.c + * + * This contains the io-permission bitmap code - written by obz, with changes + * by Linus. + */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/ioport.h> + +/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ +static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value) +{ + int mask; + unsigned long *bitmap_base = bitmap + (base >> 5); + unsigned short low_index = base & 0x1f; + int length = low_index + extent; + + if (low_index != 0) { + mask = (~0 << low_index); + if (length < 32) + mask &= ~(~0 << length); + if (new_value) + *bitmap_base++ |= mask; + else + *bitmap_base++ &= ~mask; + length -= 32; + } + + mask = (new_value ? ~0 : 0); + while (length >= 32) { + *bitmap_base++ = mask; + length -= 32; + } + + if (length > 0) { + mask = ~(~0 << length); + if (new_value) + *bitmap_base++ |= mask; + else + *bitmap_base++ &= ~mask; + } +} + +/* + * this changes the io permissions bitmap in the current task. + */ +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) +{ + if (from + num <= from) + return -EINVAL; + if (from + num > IO_BITMAP_SIZE*32) + return -EINVAL; + if (!suser()) + return -EPERM; + + set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on); + return 0; +} + +unsigned int *stack; + +/* + * sys_iopl has to be used when you want to access the IO ports + * beyond the 0x3ff range: to get the full 65536 ports bitmapped + * you'd need 8kB of bitmaps/process, which is a bit excessive. + * + * Here we just change the eflags value on the stack: we allow + * only the super-user to do it. This depends on the stack-layout + * on system-call entry - see also fork() and the signal handling + * code. + */ +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) +{ + unsigned int level = ebx; + + if (level > 3) + return -EINVAL; + if (!suser()) + return -EPERM; + *(&eflags) = (eflags & 0xffffcfff) | (level << 12); + return 0; +} diff --git a/arch/i386/irq.c b/arch/i386/kernel/irq.c index 2de16db53..23085dc6c 100644 --- a/arch/i386/irq.c +++ b/arch/i386/kernel/irq.c @@ -1,5 +1,5 @@ /* - * linux/kernel/irq.c + * linux/arch/i386/kernel/irq.c * * Copyright (C) 1992 Linus Torvalds * @@ -12,14 +12,7 @@ /* * IRQ's are in fact implemented a bit like signal handlers for the kernel. - * The same sigaction struct is used, and with similar semantics (ie there - * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there - * are similarities. - * - * sa_handler(int irq_NR) is the default function called (0 if no). - * sa_mask is horribly ugly (I won't even mention it) - * sa_flags contains various info: SA_INTERRUPT etc - * sa_restorer is the unused + * Naturally it's not a 1:1 relation, but there are similarities. */ #include <linux/ptrace.h> @@ -27,22 +20,20 @@ #include <linux/kernel_stat.h> #include <linux/signal.h> #include <linux/sched.h> +#include <linux/ioport.h> #include <linux/interrupt.h> +#include <linux/timex.h> #include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> +#include <asm/bitops.h> #define CR0_NE 32 static unsigned char cache_21 = 0xff; static unsigned char cache_A1 = 0xff; -unsigned long intr_count = 0; -unsigned long bh_active = 0; -unsigned long bh_mask = 0xFFFFFFFF; -struct bh_struct bh_base[32]; - void disable_irq(unsigned int irq_nr) { unsigned long flags; @@ -84,34 +75,6 @@ void enable_irq(unsigned int irq_nr) } /* - * do_bottom_half() runs at normal kernel priority: all interrupts - * enabled. do_bottom_half() is atomic with respect to itself: a - * bottom_half handler need not be re-entrant. - */ -asmlinkage void do_bottom_half(void) -{ - unsigned long active; - unsigned long mask, left; - struct bh_struct *bh; - - bh = bh_base; - active = bh_active & bh_mask; - for (mask = 1, left = ~0 ; left & active ; bh++,mask += mask,left += left) { - if (mask & active) { - void (*fn)(void *); - bh_active &= ~mask; - fn = bh->routine; - if (!fn) - goto bad_bh; - fn(bh->data); - } - } - return; -bad_bh: - printk ("irq.c:bad bottom half entry\n"); -} - -/* * This builds up the IRQ handler stubs using some ugly macros in irq.h * * These macros create the low-level assembly IRQ routines that do all @@ -179,7 +142,14 @@ static void (*bad_interrupt[16])(void) = { /* * Initial irq handlers. */ -static struct sigaction irq_sigaction[16] = { +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 }, @@ -193,15 +163,15 @@ static struct sigaction irq_sigaction[16] = { int get_irq_list(char *buf) { int i, len = 0; - struct sigaction * sa = irq_sigaction; + struct irqaction * action = irq_action; - for (i = 0 ; i < 16 ; i++, sa++) { - if (!sa->sa_handler) + for (i = 0 ; i < 16 ; i++, action++) { + if (!action->handler) continue; len += sprintf(buf+len, "%2d: %8d %c %s\n", i, kstat.interrupts[i], - (sa->sa_flags & SA_INTERRUPT) ? '+' : ' ', - (char *) sa->sa_mask); + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); } return len; } @@ -215,10 +185,10 @@ int get_irq_list(char *buf) */ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { - struct sigaction * sa = irq + irq_sigaction; + struct irqaction * action = irq + irq_action; kstat.interrupts[irq]++; - sa->sa_handler((int) regs); + action->handler(irq, regs); } /* @@ -228,35 +198,39 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) */ asmlinkage void do_fast_IRQ(int irq) { - struct sigaction * sa = irq + irq_sigaction; + struct irqaction * action = irq + irq_action; kstat.interrupts[irq]++; - sa->sa_handler(irq); + action->handler(irq, NULL); } -/* - * Using "struct sigaction" is slightly silly, but there - * are historical reasons and it works well, so.. - */ -static int irqaction(unsigned int irq, struct sigaction * new_sa) +#define SA_PROBE SA_ONESHOT + +int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), + unsigned long irqflags, const char * devname) { - struct sigaction * sa; + struct irqaction * action; unsigned long flags; if (irq > 15) return -EINVAL; - sa = irq + irq_sigaction; - if (sa->sa_handler) + action = irq + irq_action; + if (action->handler) return -EBUSY; - if (!new_sa->sa_handler) + if (!handler) return -EINVAL; save_flags(flags); cli(); - *sa = *new_sa; - if (sa->sa_flags & SA_INTERRUPT) - set_intr_gate(0x20+irq,fast_interrupt[irq]); - else - set_intr_gate(0x20+irq,interrupt[irq]); + 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); @@ -270,28 +244,16 @@ static int irqaction(unsigned int irq, struct sigaction * new_sa) return 0; } -int request_irq(unsigned int irq, void (*handler)(int), - unsigned long flags, const char * devname) -{ - struct sigaction sa; - - sa.sa_handler = handler; - sa.sa_flags = flags; - sa.sa_mask = (unsigned long) devname; - sa.sa_restorer = NULL; - return irqaction(irq,&sa); -} - void free_irq(unsigned int irq) { - struct sigaction * sa = irq + irq_sigaction; + struct irqaction * action = irq + irq_action; unsigned long flags; if (irq > 15) { printk("Trying to free IRQ%d\n",irq); return; } - if (!sa->sa_handler) { + if (!action->handler) { printk("Trying to free free IRQ%d\n",irq); return; } @@ -305,10 +267,10 @@ void free_irq(unsigned int irq) outb(cache_A1,0xA1); } set_intr_gate(0x20+irq,bad_interrupt[irq]); - sa->sa_handler = NULL; - sa->sa_flags = 0; - sa->sa_mask = 0; - sa->sa_restorer = NULL; + action->handler = NULL; + action->flags = 0; + action->mask = 0; + action->name = NULL; restore_flags(flags); } @@ -323,7 +285,7 @@ void free_irq(unsigned int irq) * leads to races. IBM designers who came up with it should * be shot. */ -static void math_error_irq(int cpl) +static void math_error_irq(int cpl, struct pt_regs *regs) { outb(0,0xF0); if (ignore_irq13 || !hard_math) @@ -331,24 +293,74 @@ static void math_error_irq(int cpl) math_error(); } -static void no_action(int cpl) { } +static void no_action(int cpl, struct pt_regs * regs) { } + +unsigned int probe_irq_on (void) +{ + unsigned int i, irqs = 0, irqmask; + unsigned long delay; + + /* first, snaffle up any unassigned irqs */ + for (i = 15; i > 0; i--) { + if (!request_irq(i, no_action, SA_PROBE, "probe")) { + enable_irq(i); + irqs |= (1 << i); + } + } + + /* wait for spurious interrupts to mask themselves out again */ + for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms 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; +} + +int probe_irq_off (unsigned int 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); +#endif + irqs &= irqmask; + if (!irqs) + return 0; + i = ffz(~irqs); + if (irqs != (irqs & (1 << i))) + i = -i; + return i; +} void init_IRQ(void) { int i; + /* set the clock to 100 Hz */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + 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"); - - /* initialize the bottom half routines. */ - for (i = 0; i < 32; i++) { - bh_base[i].routine = NULL; - bh_base[i].data = NULL; - } - bh_active = 0; - intr_count = 0; -} + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); +} diff --git a/arch/i386/ldt.c b/arch/i386/kernel/ldt.c index dd0e477d4..bace95f4e 100644 --- a/arch/i386/ldt.c +++ b/arch/i386/kernel/ldt.c @@ -4,10 +4,10 @@ * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/string.h> +#include <linux/mm.h> #include <asm/segment.h> #include <asm/system.h> #include <linux/ldt.h> @@ -66,6 +66,7 @@ static int write_ldt(void * ptr, unsigned long bytecount) if (task[i] == current) { if (!(current->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE))) return -ENOMEM; + memset(current->ldt, 0, LDT_ENTRIES*LDT_ENTRY_SIZE); set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, current->ldt, LDT_ENTRIES); load_ldt(i); } diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c new file mode 100644 index 000000000..a5e8777bf --- /dev/null +++ b/arch/i386/kernel/process.c @@ -0,0 +1,288 @@ +/* + * linux/arch/i386/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#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 <asm/segment.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/io.h> + +asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); + +static int hlt_counter=0; + +void disable_hlt(void) +{ + hlt_counter++; +} + +void enable_hlt(void) +{ + hlt_counter--; +} + +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_fs_long(fd[0],0+fildes); + put_fs_long(fd[1],1+fildes); + return 0; +} + +/* + * The idle loop on a i386.. + */ +asmlinkage int sys_idle(void) +{ + int i; + pmd_t * pmd; + + 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"); + schedule(); + } +} + +/* + * 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 inline void kb_wait(void) +{ + int i; + + for (i=0; i<0x10000; i++) + if ((inb_p(0x64) & 0x02) == 0) + break; +} + +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; + for (;;) { + for (i=0; i<100; i++) { + kb_wait(); + for(j = 0; j < 100000 ; j++) + /* nothing */; + outb(0xfe,0x64); /* pulse reset low */ + } + __asm__ __volatile__("\tlidt %0": "=m" (no_idt)); + } +} + +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(" 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); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + /* forget local segments */ + __asm__ __volatile__("mov %w0,%%fs ; mov %w0,%%gs ; lldt %w0" + : /* no outputs */ + : "r" (0)); + current->tss.ldt = 0; + if (current->ldt) { + void * ldt = current->ldt; + current->ldt = NULL; + vfree(ldt); + } +} + +void flush_thread(void) +{ + int i; + + if (current->ldt) { + free_page((unsigned long) current->ldt); + current->ldt = NULL; + for (i=1 ; i<NR_TASKS ; i++) { + if (task[i] == current) { + set_ldt_desc(gdt+(i<<1)+ + FIRST_LDT_ENTRY,&default_ldt, 1); + load_ldt(i); + } + } + } + + for (i=0 ; i<8 ; i++) + current->debugreg[i] = 0; +} + +void copy_thread(int nr, unsigned long clone_flags, unsigned long esp, + struct task_struct * p, struct pt_regs * regs) +{ + int i; + struct pt_regs * childregs; + + p->tss.es = KERNEL_DS; + p->tss.cs = KERNEL_CS; + p->tss.ss = KERNEL_DS; + p->tss.ds = KERNEL_DS; + p->tss.fs = USER_DS; + p->tss.gs = KERNEL_DS; + p->tss.ss0 = KERNEL_DS; + p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE; + p->tss.tr = _TSS(nr); + 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; + *childregs = *regs; + childregs->eax = 0; + childregs->esp = esp; + p->tss.back_link = 0; + p->tss.eflags = regs->eflags & 0xffffcfff; /* iopl is always 0 for a new process */ + p->tss.ldt = _LDT(nr); + if (p->ldt) { + p->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); + if (p->ldt != NULL) + memcpy(p->ldt, current->ldt, LDT_ENTRIES*LDT_ENTRY_SIZE); + } + set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); + if (p->ldt) + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512); + else + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1); + p->tss.bitmap = offsetof(struct thread_struct,io_bitmap); + for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */ + p->tss.io_bitmap[i] = ~0; + if (last_task_used_math == current) + __asm__("clts ; fnsave %0 ; frstor %0":"=m" (p->tss.i387)); +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + int i; + +/* changed the size calculations - should hopefully work better. lbt */ + 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_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; + } +} + +asmlinkage int sys_fork(struct pt_regs regs) +{ + return do_fork(COPYVM | 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; + if (!newsp) + newsp = regs.esp; + if (newsp == regs.esp) + clone_flags |= COPYVM; + return do_fork(clone_flags, newsp, ®s); +#else + return -ENOSYS; +#endif +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(struct pt_regs regs) +{ + int error; + char * filename; + + error = getname((char *) regs.ebx, &filename); + if (error) + return error; + error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s); + putname(filename); + return error; +} diff --git a/arch/i386/ptrace.c b/arch/i386/kernel/ptrace.c index cade04750..f32035edc 100644 --- a/arch/i386/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -9,10 +9,11 @@ #include <linux/errno.h> #include <linux/ptrace.h> #include <linux/user.h> +#include <linux/debugreg.h> #include <asm/segment.h> +#include <asm/pgtable.h> #include <asm/system.h> -#include <linux/debugreg.h> /* * does not yet catch signals sent when the child dies. @@ -84,23 +85,41 @@ static inline int put_stack_long(struct task_struct *task, int offset, */ static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr) { + pgd_t * pgdir; + pmd_t * pgmiddle; + pte_t * pgtable; unsigned long page; repeat: - page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr); - if (page & PAGE_PRESENT) { - page &= PAGE_MASK; - page += PAGE_PTR(addr); - page = *((unsigned long *) page); + pgdir = pgd_offset(vma->vm_task, addr); + if (pgd_none(*pgdir)) { + do_no_page(vma, addr, 0); + goto repeat; } - if (!(page & PAGE_PRESENT)) { + if (pgd_bad(*pgdir)) { + printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); + pgd_clear(pgdir); + return 0; + } + pgmiddle = pmd_offset(pgdir, addr); + if (pmd_none(*pgmiddle)) { do_no_page(vma, addr, 0); goto repeat; } + if (pmd_bad(*pgmiddle)) { + printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); + pmd_clear(pgmiddle); + return 0; + } + pgtable = pte_offset(pgmiddle, addr); + if (!pte_present(*pgtable)) { + do_no_page(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) return 0; - page &= PAGE_MASK; page += addr & ~PAGE_MASK; return *(unsigned long *) page; } @@ -117,52 +136,61 @@ repeat: static void put_long(struct vm_area_struct * vma, unsigned long addr, unsigned long data) { - unsigned long page, pte = 0; - int readonly = 0; + pgd_t *pgdir; + pmd_t *pgmiddle; + pte_t *pgtable; + unsigned long page; repeat: - page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr); - if (page & PAGE_PRESENT) { - page &= PAGE_MASK; - page += PAGE_PTR(addr); - pte = page; - page = *((unsigned long *) page); + pgdir = pgd_offset(vma->vm_task, addr); + if (!pgd_present(*pgdir)) { + do_no_page(vma, addr, 1); + goto repeat; } - if (!(page & PAGE_PRESENT)) { - do_no_page(vma, addr, 0 /* PAGE_RW */); + if (pgd_bad(*pgdir)) { + printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); + pgd_clear(pgdir); + return; + } + pgmiddle = pmd_offset(pgdir, addr); + if (pmd_none(*pgmiddle)) { + do_no_page(vma, addr, 1); goto repeat; } - if (!(page & PAGE_RW)) { - if (!(page & PAGE_COW)) - readonly = 1; - do_wp_page(vma, addr, PAGE_RW | PAGE_PRESENT); + if (pmd_bad(*pgmiddle)) { + printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); + pmd_clear(pgmiddle); + return; + } + pgtable = pte_offset(pgmiddle, addr); + if (!pte_present(*pgtable)) { + do_no_page(vma, addr, 1); + goto repeat; + } + page = pte_page(*pgtable); + if (!pte_write(*pgtable)) { + do_wp_page(vma, addr, 1); goto repeat; } /* this is a hack for non-kernel-mapped video buffers and similar */ - if (page >= high_memory) - return; + if (page < high_memory) { + page += addr & ~PAGE_MASK; + *(unsigned long *) page = data; + } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ - *(unsigned long *) pte |= (PAGE_DIRTY|PAGE_COW); - page &= PAGE_MASK; - page += addr & ~PAGE_MASK; - *(unsigned long *) page = data; - if (readonly) { - *(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW); - invalidate(); - } +/* this should also re-instate whatever read-only mode there was before */ + *pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot)); + invalidate(); } -static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr) +static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr) { struct vm_area_struct * vma; addr &= PAGE_MASK; - for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return NULL; - if (vma->vm_end > addr) - break; - } + vma = find_vma(tsk,addr); + if (!vma) + return NULL; if (vma->vm_start <= addr) return vma; if (!(vma->vm_flags & VM_GROWSDOWN)) @@ -181,7 +209,7 @@ static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long static int read_long(struct task_struct * tsk, unsigned long addr, unsigned long * result) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; @@ -223,7 +251,7 @@ static int read_long(struct task_struct * tsk, unsigned long addr, static int write_long(struct task_struct * tsk, unsigned long addr, unsigned long data) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c new file mode 100644 index 000000000..5cc4c5d7d --- /dev/null +++ b/arch/i386/kernel/setup.c @@ -0,0 +1,181 @@ +/* + * linux/arch/i386/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#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 <asm/system.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 */ +int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */ + +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 hlt_works_ok = 1; /* set if the "hlt" instruction works */ + +/* + * Bus types .. + */ +int EISA_bus = 0; + +/* + * Setup options + */ +struct drive_info_struct { char dummy[32]; } drive_info; +struct screen_info screen_info; + +unsigned char aux_device_present; +extern int ramdisk_size; +extern int root_mountflags; +extern int etext, edata, end; + +extern char empty_zero_page[PAGE_SIZE]; + +/* + * This is set up by the setup-routine at boot-time + */ +#define PARAM empty_zero_page +#define EXT_MEM_K (*(unsigned short *) (PARAM+2)) +#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 ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) +#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) +#define COMMAND_LINE ((char *) (PARAM+2048)) +#define COMMAND_LINE_SIZE 256 + +static char command_line[COMMAND_LINE_SIZE] = { 0, }; + +void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + unsigned long memory_start, memory_end; + char c = ' ', *to = command_line, *from = COMMAND_LINE; + int len = 0; + + ROOT_DEV = ORIG_ROOT_DEV; + drive_info = DRIVE_INFO; + screen_info = SCREEN_INFO; + aux_device_present = AUX_DEVICE_INFO; + memory_end = (1<<20) + (EXT_MEM_K<<10); + memory_end &= PAGE_MASK; + ramdisk_size = RAMDISK_SIZE; +#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; + + 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++; + } + } + c = *(from++); + if (!c) + break; + if (COMMAND_LINE_SIZE <= ++len) + break; + *(to++) = c; + } + *to = '\0'; + *cmdline_p = command_line; + *memory_start_p = memory_start; + *memory_end_p = memory_end; + /* 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"); +} + +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 + ); +} diff --git a/arch/i386/signal.c b/arch/i386/kernel/signal.c index df7324294..3db1a6985 100644 --- a/arch/i386/signal.c +++ b/arch/i386/kernel/signal.c @@ -1,10 +1,11 @@ /* - * linux/kernel/signal.c + * linux/arch/i386/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds */ #include <linux/sched.h> +#include <linux/mm.h> #include <linux/kernel.h> #include <linux/signal.h> #include <linux/errno.h> @@ -18,63 +19,7 @@ #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs); - -asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset) -{ - sigset_t new_set, old_set = current->blocked; - int error; - - if (set) { - error = verify_area(VERIFY_READ, set, sizeof(sigset_t)); - if (error) - return error; - new_set = get_fs_long((unsigned long *) set) & _BLOCKABLE; - switch (how) { - case SIG_BLOCK: - current->blocked |= new_set; - break; - case SIG_UNBLOCK: - current->blocked &= ~new_set; - break; - case SIG_SETMASK: - current->blocked = new_set; - break; - default: - return -EINVAL; - } - } - if (oset) { - error = verify_area(VERIFY_WRITE, oset, sizeof(sigset_t)); - if (error) - return error; - put_fs_long(old_set, (unsigned long *) oset); - } - return 0; -} - -asmlinkage int sys_sgetmask(void) -{ - return current->blocked; -} - -asmlinkage int sys_ssetmask(int newmask) -{ - int old=current->blocked; - - current->blocked = newmask & _BLOCKABLE; - return old; -} - -asmlinkage int sys_sigpending(sigset_t *set) -{ - int error; - /* fill in "set" with signals pending but blocked. */ - error = verify_area(VERIFY_WRITE, set, 4); - if (!error) - put_fs_long(current->blocked & current->signal, (unsigned long *)set); - return error; -} +asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); /* * atomically swap in the new signal mask, and wait for a signal. @@ -96,99 +41,6 @@ asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long } /* - * POSIX 3.3.1.3: - * "Setting a signal action to SIG_IGN for a signal that is pending - * shall cause the pending signal to be discarded, whether or not - * it is blocked" (but SIGCHLD is unspecified: linux leaves it alone). - * - * "Setting a signal action to SIG_DFL for a signal that is pending - * and whose default action is to ignore the signal (for example, - * SIGCHLD), shall cause the pending signal to be discarded, whether - * or not it is blocked" - * - * Note the silly behaviour of SIGCHLD: SIG_IGN means that the signal - * isn't actually ignored, but does automatic child reaping, while - * SIG_DFL is explicitly said by POSIX to force the signal to be ignored.. - */ -static void check_pending(int signum) -{ - struct sigaction *p; - - p = signum - 1 + current->sigaction; - if (p->sa_handler == SIG_IGN) { - if (signum == SIGCHLD) - return; - current->signal &= ~_S(signum); - return; - } - if (p->sa_handler == SIG_DFL) { - if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH) - return; - current->signal &= ~_S(signum); - return; - } -} - -asmlinkage int sys_signal(int signum, unsigned long handler) -{ - struct sigaction tmp; - - if (signum<1 || signum>32) - return -EINVAL; - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - if (handler >= TASK_SIZE) - return -EFAULT; - tmp.sa_handler = (void (*)(int)) handler; - tmp.sa_mask = 0; - tmp.sa_flags = SA_ONESHOT | SA_NOMASK; - tmp.sa_restorer = NULL; - handler = (long) current->sigaction[signum-1].sa_handler; - current->sigaction[signum-1] = tmp; - check_pending(signum); - return handler; -} - -asmlinkage int sys_sigaction(int signum, const struct sigaction * action, - struct sigaction * oldaction) -{ - struct sigaction new_sa, *p; - - if (signum<1 || signum>32) - return -EINVAL; - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - p = signum - 1 + current->sigaction; - if (action) { - int err = verify_area(VERIFY_READ, action, sizeof(*action)); - if (err) - return err; - memcpy_fromfs(&new_sa, action, sizeof(struct sigaction)); - if (new_sa.sa_flags & SA_NOMASK) - new_sa.sa_mask = 0; - else { - new_sa.sa_mask |= _S(signum); - new_sa.sa_mask &= _BLOCKABLE; - } - if (TASK_SIZE <= (unsigned long) new_sa.sa_handler) - return -EFAULT; - } - if (oldaction) { - int err = verify_area(VERIFY_WRITE, oldaction, sizeof(*oldaction)); - if (err) - return err; - memcpy_tofs(oldaction, p, sizeof(struct sigaction)); - } - if (action) { - *p = new_sa; - check_pending(signum); - } - return 0; -} - -asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); - -/* * This sets regs->esp even though we don't actually use sigstacks yet.. */ asmlinkage int sys_sigreturn(unsigned long __unused) @@ -229,7 +81,7 @@ badframe: * Set up a signal frame... Make the stack look the way iBCS2 expects * it to look. */ -static void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip, +void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip, struct pt_regs * regs, int signr, unsigned long oldmask) { unsigned long * frame; @@ -299,10 +151,10 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) struct sigaction * sa; while ((signr = current->signal & mask)) { - __asm__("bsf %2,%1\n\t" + __asm__("bsf %3,%1\n\t" "btrl %1,%0" :"=m" (current->signal),"=r" (signr) - :"1" (signr)); + :"0" (current->signal), "1" (signr)); sa = current->sigaction + signr; signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { @@ -348,7 +200,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) continue; case SIGQUIT: case SIGILL: case SIGTRAP: - case SIGIOT: case SIGFPE: case SIGSEGV: + case SIGABRT: case SIGFPE: case SIGSEGV: if (current->binfmt && current->binfmt->core_dump) { if (current->binfmt->core_dump(signr, regs)) signr |= 0x80; @@ -402,6 +254,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) } 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; } diff --git a/arch/i386/traps.c b/arch/i386/kernel/traps.c index 150b702b3..6dd7fc65b 100644 --- a/arch/i386/traps.c +++ b/arch/i386/kernel/traps.c @@ -1,5 +1,5 @@ /* - * linux/kernel/traps.c + * linux/arch/i386/traps.c * * Copyright (C) 1991, 1992 Linus Torvalds */ @@ -16,11 +16,18 @@ #include <linux/string.h> #include <linux/errno.h> #include <linux/ptrace.h> +#include <linux/config.h> +#include <linux/timer.h> +#include <linux/mm.h> #include <asm/system.h> #include <asm/segment.h> #include <asm/io.h> +asmlinkage int system_call(void); +asmlinkage void lcall7(void); +struct desc_struct default_ldt; + static inline void console_verbose(void) { extern int console_loglevel; @@ -76,11 +83,23 @@ asmlinkage void coprocessor_error(void); asmlinkage void reserved(void); asmlinkage void alignment_check(void); +int kstack_depth_to_print = 24; + +/* + * These constants are for searching for possible module text + * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is + * a guess of how much space is likely to be vmalloced. + */ +#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) { int i; unsigned long esp; unsigned short ss; + unsigned long *stack, addr, module_start, module_end; + extern char start_kernel, etext; esp = (unsigned long) ®s->esp; ss = KERNEL_DS; @@ -104,8 +123,38 @@ asmlinkage void alignment_check(void); printk("Corrupted stack page\n"); printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)\nStack: ", current->comm, current->pid, 0xffff & i, current->kernel_stack_page); - for(i=0;i<5;i++) - printk("%08lx ", get_seg_long(ss,(i+(unsigned long *)esp))); + stack = (unsigned long *) esp; + for(i=0; i < kstack_depth_to_print; i++) { + if (((long) stack & 4095) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", get_seg_long(ss,stack++)); + } + printk("\nCall Trace: "); + stack = (unsigned long *) esp; + i = 1; + module_start = ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); + module_end = module_start + MODULE_RANGE; + while (((long) stack & 4095) != 0) { + addr = get_seg_long(ss, stack++); + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (unsigned long) &start_kernel) && + (addr <= (unsigned long) &etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", addr); + i++; + } + } printk("\nCode: "); for(i=0;i<20;i++) printk("%02x ",0xff & get_seg_byte(regs->cs,(i+(char *)regs->eip))); @@ -129,29 +178,23 @@ DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) { - int signr = SIGSEGV; - if (regs->eflags & VM_MASK) { handle_vm86_fault((struct vm86_regs *) regs, error_code); return; } die_if_kernel("general protection",regs,error_code); - switch (get_seg_byte(regs->cs, (char *)regs->eip)) { - case 0xCD: /* INT */ - case 0xF4: /* HLT */ - case 0xFA: /* CLI */ - case 0xFB: /* STI */ - signr = SIGILL; - } current->tss.error_code = error_code; current->tss.trap_no = 13; - send_sig(signr, current, 1); + send_sig(SIGSEGV, current, 1); } asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { +#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\n"); + printk("You probably have a hardware problem with your RAM chips or a\n"); + printk("power saving mode enabled.\n"); +#endif } asmlinkage void do_debug(struct pt_regs * regs, long error_code) @@ -218,10 +261,54 @@ asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) math_error(); } +/* + * 'math_state_restore()' saves the current math information in the + * old math state array, and gets the new ones from the current task + * + * Careful.. There are problems with IBM-designed IRQ13 behaviour. + * Don't touch unless you *really* know how it works. + */ +asmlinkage void math_state_restore(void) +{ + __asm__ __volatile__("clts"); + 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) { + __asm__("frstor %0": :"m" (current->tss.i387)); + } else { + __asm__("fninit"); + current->used_math=1; + } + timer_active &= ~(1<<COPRO_TIMER); +} + +#ifndef CONFIG_MATH_EMULATION + +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); + schedule(); +} + +#endif /* CONFIG_MATH_EMULATION */ + void trap_init(void) { int i; + struct desc_struct * p; + if (strncmp((char*)0x0FFFD9, "EISA", 4) == 0) + EISA_bus = 1; + set_call_gate(&default_ldt,lcall7); set_trap_gate(0,÷_error); set_trap_gate(1,&debug); set_trap_gate(2,&nmi); @@ -242,4 +329,21 @@ void trap_init(void) set_trap_gate(17,&alignment_check); for (i=18;i<48;i++) set_trap_gate(i,&reserved); + set_system_gate(0x80,&system_call); +/* set up GDT task & ldt entries */ + p = gdt+FIRST_TSS_ENTRY; + set_tss_desc(p, &init_task.tss); + p++; + set_ldt_desc(p, &default_ldt, 1); + p++; + for(i=1 ; i<NR_TASKS ; i++) { + p->a=p->b=0; + p++; + p->a=p->b=0; + p++; + } +/* Clear NT, so that we won't have troubles with that later on */ + __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); + load_TR(0); + load_ldt(0); } diff --git a/arch/i386/vm86.c b/arch/i386/kernel/vm86.c index 144d93a02..d55f8248f 100644 --- a/arch/i386/vm86.c +++ b/arch/i386/kernel/vm86.c @@ -9,8 +9,10 @@ #include <linux/signal.h> #include <linux/string.h> #include <linux/ptrace.h> +#include <linux/mm.h> #include <asm/segment.h> +#include <asm/pgtable.h> #include <asm/io.h> /* @@ -39,8 +41,8 @@ /* * virtual flags (16 and 32-bit versions) */ -#define VFLAGS (*(unsigned short *)&(current->v86flags)) -#define VEFLAGS (current->v86flags) +#define VFLAGS (*(unsigned short *)&(current->tss.v86flags)) +#define VEFLAGS (current->tss.v86flags) #define set_flags(X,new,mask) \ ((X) = ((X) & ~(mask)) | ((new) & (mask))) @@ -52,13 +54,13 @@ asmlinkage struct pt_regs * save_v86_state(struct vm86_regs * regs) { unsigned long tmp; - if (!current->vm86_info) { + if (!current->tss.vm86_info) { printk("no vm86_info: BAD\n"); do_exit(SIGSEGV); } - set_flags(regs->eflags, VEFLAGS, VIF_MASK | current->v86mask); - memcpy_tofs(¤t->vm86_info->regs,regs,sizeof(*regs)); - put_fs_long(current->screen_bitmap,¤t->vm86_info->screen_bitmap); + 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 = current->tss.esp0; current->tss.esp0 = current->saved_kernel_stack; current->saved_kernel_stack = 0; @@ -67,22 +69,34 @@ asmlinkage struct pt_regs * save_v86_state(struct vm86_regs * regs) static void mark_screen_rdonly(struct task_struct * tsk) { - unsigned long tmp; - unsigned long *pg_table; - - if ((tmp = tsk->tss.cr3) != 0) { - tmp = *(unsigned long *) tmp; - if (tmp & PAGE_PRESENT) { - tmp &= PAGE_MASK; - pg_table = (0xA0000 >> PAGE_SHIFT) + (unsigned long *) tmp; - tmp = 32; - while (tmp--) { - if (PAGE_PRESENT & *pg_table) - *pg_table &= ~PAGE_RW; - pg_table++; - } - } + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + int i; + + pgd = pgd_offset(tsk, 0xA0000); + if (pgd_none(*pgd)) + return; + if (pgd_bad(*pgd)) { + printk("vm86: bad pgd entry [%p]:%08lx\n", pgd, pgd_val(*pgd)); + pgd_clear(pgd); + return; + } + pmd = pmd_offset(pgd, 0xA0000); + if (pmd_none(*pmd)) + return; + if (pmd_bad(*pmd)) { + printk("vm86: bad pmd entry [%p]:%08lx\n", pmd, pmd_val(*pmd)); + pmd_clear(pmd); + return; } + pte = pte_offset(pmd, 0xA0000); + for (i = 0; i < 32; i++) { + if (pte_present(*pte)) + *pte = pte_wrprotect(*pte); + pte++; + } + invalidate(); } asmlinkage int sys_vm86(struct vm86_struct * v86) @@ -117,16 +131,16 @@ asmlinkage int sys_vm86(struct vm86_struct * v86) switch (info.cpu_type) { case CPU_286: - current->v86mask = 0; + current->tss.v86mask = 0; break; case CPU_386: - current->v86mask = NT_MASK | IOPL_MASK; + current->tss.v86mask = NT_MASK | IOPL_MASK; break; case CPU_486: - current->v86mask = AC_MASK | NT_MASK | IOPL_MASK; + current->tss.v86mask = AC_MASK | NT_MASK | IOPL_MASK; break; default: - current->v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK; + current->tss.v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK; break; } @@ -136,9 +150,9 @@ asmlinkage int sys_vm86(struct vm86_struct * v86) pt_regs->eax = 0; current->saved_kernel_stack = current->tss.esp0; current->tss.esp0 = (unsigned long) pt_regs; - current->vm86_info = v86; + current->tss.vm86_info = v86; - current->screen_bitmap = info.screen_bitmap; + current->tss.screen_bitmap = info.screen_bitmap; if (info.flags & VM86_SCREEN_BITMAP) mark_screen_rdonly(current); __asm__ __volatile__("movl %0,%%esp\n\t" @@ -178,7 +192,7 @@ static inline void clear_TF(struct vm86_regs * regs) static inline void set_vflags_long(unsigned long eflags, struct vm86_regs * regs) { - set_flags(VEFLAGS, eflags, current->v86mask); + set_flags(VEFLAGS, eflags, current->tss.v86mask); set_flags(regs->eflags, eflags, SAFE_MASK); if (eflags & IF_MASK) set_IF(regs); @@ -186,7 +200,7 @@ static inline void set_vflags_long(unsigned long eflags, struct vm86_regs * regs static inline void set_vflags_short(unsigned short flags, struct vm86_regs * regs) { - set_flags(VFLAGS, flags, current->v86mask); + set_flags(VFLAGS, flags, current->tss.v86mask); set_flags(regs->eflags, flags, SAFE_MASK); if (flags & IF_MASK) set_IF(regs); @@ -198,7 +212,7 @@ static inline unsigned long get_vflags(struct vm86_regs * regs) if (VEFLAGS & VIF_MASK) flags |= IF_MASK; - return flags | (VEFLAGS & current->v86mask); + return flags | (VEFLAGS & current->tss.v86mask); } static inline int is_revectored(int nr, struct revectored_struct * bitmap) @@ -287,9 +301,9 @@ static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned unsigned short seg = get_fs_word((void *) ((i<<2)+2)); if (seg == BIOSSEG || regs->cs == BIOSSEG || - is_revectored(i, ¤t->vm86_info->int_revectored)) + is_revectored(i, ¤t->tss.vm86_info->int_revectored)) return_to_32bit(regs, VM86_INTx + (i << 8)); - if (i==0x21 && is_revectored(AH(regs),¤t->vm86_info->int21_revectored)) + if (i==0x21 && is_revectored(AH(regs),¤t->tss.vm86_info->int21_revectored)) return_to_32bit(regs, VM86_INTx + (i << 8)); pushw(ssp, sp, get_vflags(regs)); pushw(ssp, sp, regs->cs); @@ -344,6 +358,14 @@ void handle_vm86_fault(struct vm86_regs * regs, long error_code) IP(regs) += 2; set_vflags_long(popl(ssp, sp), regs); return; + + /* iretd */ + case 0xcf: + SP(regs) += 12; + IP(regs) = (unsigned short)popl(ssp, sp); + regs->cs = (unsigned short)popl(ssp, sp); + set_vflags_long(popl(ssp, sp), regs); + return; } /* pushf */ @@ -360,12 +382,6 @@ void handle_vm86_fault(struct vm86_regs * regs, long error_code) set_vflags_short(popw(ssp, sp), regs); return; - /* int 3 */ - case 0xcc: - IP(regs)++; - do_int(regs, 3, ssp, sp); - return; - /* int xx */ case 0xcd: IP(regs) += 2; diff --git a/arch/i386/main.c b/arch/i386/main.c deleted file mode 100644 index 49606d4de..000000000 --- a/arch/i386/main.c +++ /dev/null @@ -1,481 +0,0 @@ -/* - * linux/init/main.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -#include <stdarg.h> - -#include <asm/system.h> -#include <asm/io.h> - -#include <linux/types.h> -#include <linux/fcntl.h> -#include <linux/config.h> -#include <linux/sched.h> -#include <linux/tty.h> -#include <linux/head.h> -#include <linux/unistd.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/ctype.h> -#include <linux/delay.h> -#include <linux/utsname.h> -#include <linux/ioport.h> - -extern unsigned long * prof_buffer; -extern unsigned long prof_len; -extern char edata, end; -extern char *linux_banner; -asmlinkage void lcall7(void); -struct desc_struct default_ldt; - -/* - * we need this inline - forking from kernel space will result - * in NO COPY ON WRITE (!!!), until an execve is executed. This - * is no problem, but for the stack. This is handled by not letting - * main() use the stack at all after fork(). Thus, no function - * calls - which means inline code for fork too, as otherwise we - * would use the stack upon exit from 'fork()'. - * - * Actually only pause and fork are needed inline, so that there - * won't be any messing with the stack from main(), but we define - * some others too. - */ -#define __NR__exit __NR_exit -static inline _syscall0(int,idle) -static inline _syscall0(int,fork) -static inline _syscall0(int,pause) -static inline _syscall0(int,setup) -static inline _syscall0(int,sync) -static inline _syscall0(pid_t,setsid) -static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) -static inline _syscall1(int,dup,int,fd) -static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) -static inline _syscall3(int,open,const char *,file,int,flag,int,mode) -static inline _syscall1(int,close,int,fd) -static inline _syscall1(int,_exit,int,exitcode) -static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) - -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} - -static char printbuf[1024]; - -extern int console_loglevel; - -extern char empty_zero_page[PAGE_SIZE]; -extern void init(void); -extern void init_IRQ(void); -extern void init_modules(void); -extern long console_init(long, long); -extern long kmalloc_init(long,long); -extern long blk_dev_init(long,long); -extern long chr_dev_init(long,long); -extern void sock_init(void); -extern long rd_init(long mem_start, int length); -unsigned long net_dev_init(unsigned long, unsigned long); -extern long bios32_init(long, long); - -extern void hd_setup(char *str, int *ints); -extern void bmouse_setup(char *str, int *ints); -extern void eth_setup(char *str, int *ints); -extern void xd_setup(char *str, int *ints); -extern void mcd_setup(char *str, int *ints); -extern void st_setup(char *str, int *ints); -extern void st0x_setup(char *str, int *ints); -extern void tmc8xx_setup(char *str, int *ints); -extern void t128_setup(char *str, int *ints); -extern void pas16_setup(char *str, int *ints); -extern void generic_NCR5380_setup(char *str, int *intr); -extern void aha152x_setup(char *str, int *ints); -extern void aha1542_setup(char *str, int *ints); -extern void aha274x_setup(char *str, int *ints); -extern void scsi_luns_setup(char *str, int *ints); -extern void sound_setup(char *str, int *ints); -#ifdef CONFIG_SBPCD -extern void sbpcd_setup(char *str, int *ints); -#endif CONFIG_SBPCD -#ifdef CONFIG_CDU31A -extern void cdu31a_setup(char *str, int *ints); -#endif CONFIG_CDU31A -void ramdisk_setup(char *str, int *ints); - -#ifdef CONFIG_SYSVIPC -extern void ipc_init(void); -#endif -#ifdef CONFIG_SCSI -extern unsigned long scsi_dev_init(unsigned long, unsigned long); -#endif - -/* - * This is set up by the setup-routine at boot-time - */ -#define PARAM empty_zero_page -#define EXT_MEM_K (*(unsigned short *) (PARAM+2)) -#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 ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) -#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) - -/* - * Boot command-line arguments - */ -void copy_options(char * to, char * from); -void parse_options(char *line); -#define MAX_INIT_ARGS 8 -#define MAX_INIT_ENVS 8 -#define COMMAND_LINE ((char *) (PARAM+2048)) -#define COMMAND_LINE_SIZE 256 - -extern void time_init(void); - -static unsigned long memory_start = 0; /* After mem_init, stores the */ - /* amount of free user memory */ -static unsigned long memory_end = 0; -static unsigned long low_memory_start = 0; - -static char term[21]; -int rows, cols; - -static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; -static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", term, NULL, }; - -static char * argv_rc[] = { "/bin/sh", NULL }; -static char * envp_rc[] = { "HOME=/", term, NULL }; - -static char * argv[] = { "-/bin/sh",NULL }; -static char * envp[] = { "HOME=/usr/root", term, NULL }; - -struct drive_info_struct { char dummy[32]; } drive_info; -struct screen_info screen_info; - -unsigned char aux_device_present; -int ramdisk_size; -int root_mountflags = 0; - -static char fpu_error = 0; - -static char command_line[COMMAND_LINE_SIZE] = { 0, }; - -char *get_options(char *str, int *ints) -{ - char *cur = str; - int i=1; - - while (cur && isdigit(*cur) && i <= 10) { - ints[i++] = simple_strtoul(cur,NULL,0); - if ((cur = strchr(cur,',')) != NULL) - cur++; - } - ints[0] = i-1; - return(cur); -} - -struct { - char *str; - void (*setup_func)(char *, int *); -} bootsetups[] = { - { "reserve=", reserve_setup }, - { "ramdisk=", ramdisk_setup }, -#ifdef CONFIG_INET - { "ether=", eth_setup }, -#endif -#ifdef CONFIG_SCSI - { "max_scsi_luns=", scsi_luns_setup }, -#endif -#ifdef CONFIG_BLK_DEV_HD - { "hd=", hd_setup }, -#endif -#ifdef CONFIG_CHR_DEV_ST - { "st=", st_setup }, -#endif -#ifdef CONFIG_BUSMOUSE - { "bmouse=", bmouse_setup }, -#endif -#ifdef CONFIG_SCSI_SEAGATE - { "st0x=", st0x_setup }, - { "tmc8xx=", tmc8xx_setup }, -#endif -#ifdef CONFIG_SCSI_T128 - { "t128=", t128_setup }, -#endif -#ifdef CONFIG_SCSI_PAS16 - { "pas16=", pas16_setup }, -#endif -#ifdef CONFIG_SCSI_GENERIC_NCR5380 - { "ncr5380=", generic_NCR5380_setup }, -#endif -#ifdef CONFIG_SCSI_AHA152X - { "aha152x=", aha152x_setup}, -#endif -#ifdef CONFIG_SCSI_AHA1542 - { "aha1542=", aha1542_setup}, -#endif -#ifdef CONFIG_SCSI_AHA274X - { "aha274x=", aha274x_setup}, -#endif -#ifdef CONFIG_BLK_DEV_XD - { "xd=", xd_setup }, -#endif -#ifdef CONFIG_MCD - { "mcd=", mcd_setup }, -#endif -#ifdef CONFIG_SOUND - { "sound=", sound_setup }, -#endif -#ifdef CONFIG_SBPCD - { "sbpcd=", sbpcd_setup }, -#endif CONFIG_SBPCD -#ifdef CONFIG_CDU31A - { "cdu31a=", cdu31a_setup }, -#endif CONFIG_CDU31A - { 0, 0 } -}; - -void ramdisk_setup(char *str, int *ints) -{ - if (ints[0] > 0 && ints[1] >= 0) - ramdisk_size = ints[1]; -} - -static int checksetup(char *line) -{ - int i = 0; - int ints[11]; - - while (bootsetups[i].str) { - int n = strlen(bootsetups[i].str); - if (!strncmp(line,bootsetups[i].str,n)) { - bootsetups[i].setup_func(get_options(line+n,ints), ints); - return 1; - } - i++; - } - return 0; -} - -unsigned long loops_per_sec = 1; - -static void calibrate_delay(void) -{ - int ticks; - - printk("Calibrating delay loop.. "); - while (loops_per_sec <<= 1) { - ticks = jiffies; - __delay(loops_per_sec); - ticks = jiffies - ticks; - if (ticks >= HZ) { - __asm__("mull %1 ; divl %2" - :"=a" (loops_per_sec) - :"d" (HZ), - "r" (ticks), - "0" (loops_per_sec) - :"dx"); - printk("ok - %lu.%02lu BogoMips\n", - loops_per_sec/500000, - (loops_per_sec/5000) % 100); - return; - } - } - printk("failed\n"); -} - -/* - * parse machine depended options - */ -int parse_machine_options(char *line) -{ - if (!strcmp(line,"no-hlt")) { - hlt_works_ok = 0; - return 1; - } - if (!strcmp(line,"no387")) { - hard_math = 0; - __asm__("movl %%cr0,%%eax\n\t" - "orl $0xE,%%eax\n\t" - "movl %%eax,%%cr0\n\t" : : : "ax"); - return 1; - } -} - -static void copro_timeout(void) -{ - fpu_error = 1; - timer_table[COPRO_TIMER].expires = jiffies+100; - timer_active |= 1<<COPRO_TIMER; - printk("387 failed: trying to reset\n"); - send_sig(SIGFPE, last_task_used_math, 1); - outb_p(0,0xf1); - outb_p(0,0xf0); -} - -static void check_fpu(void) -{ - static double x = 4195835.0; - static double y = 3145727.0; - unsigned short control_word; - int i; - - if (!hard_math) { -#ifndef CONFIG_MATH_EMULATION - printk("No coprocessor found and no math emulation present.\n"); - printk("Giving up.\n"); - for (;;) ; -#endif - return; - } - /* - * check if exception 16 works correctly.. This is truly evil - * code: it disables the high 8 interrupts to make sure that - * the irq13 doesn't happen. But as this will lead to a lockup - * if no exception16 arrives, it depends on the fact that the - * high 8 interrupts will be re-enabled by the next timer tick. - * So the irq13 will happen eventually, but the exception 16 - * should get there first.. - */ - printk("Checking 386/387 coupling... "); - timer_table[COPRO_TIMER].expires = jiffies+50; - timer_table[COPRO_TIMER].fn = copro_timeout; - timer_active |= 1<<COPRO_TIMER; - __asm__("clts ; fninit ; fnstcw %0 ; fwait":"=m" (*&control_word)); - control_word &= 0xffc0; - __asm__("fldcw %0 ; fwait": :"m" (*&control_word)); - outb_p(inb_p(0x21) | (1 << 2), 0x21); - __asm__("fldz ; fld1 ; fdiv %st,%st(1) ; fwait"); - timer_active &= ~(1<<COPRO_TIMER); - if (fpu_error) - return; - if (!ignore_irq13) { - printk("Ok, fpu using old IRQ13 error reporting\n"); - return; - } - __asm__("fninit\n\t" - "fldl %1\n\t" - "fdivl %2\n\t" - "fmull %2\n\t" - "fldl %1\n\t" - "fsubp %%st,%%st(1)\n\t" - "fistpl %0\n\t" - "fwait\n\t" - "fninit" - : "=m" (*&i) - : "m" (*&x), "m" (*&y)); - if (!i) { - printk("Ok, fpu using exception 16 error reporting.\n"); - return; - - } - printk("Ok, FDIV bug i%c86 system\n", '0'+x86); -} - -static void check_hlt(void) -{ - printk("Checking 'hlt' instruction... "); - if (!hlt_works_ok) { - printk("disabled\n"); - return; - } - __asm__ __volatile__("hlt ; hlt ; hlt ; hlt"); - printk("Ok.\n"); -} - -static void check_bugs(void) -{ - check_fpu(); - check_hlt(); -} - -asmlinkage void start_kernel(void) -{ -/* - * Interrupts are still disabled. Do necessary setups, then - * enable them - */ - set_call_gate(&default_ldt,lcall7); - ROOT_DEV = ORIG_ROOT_DEV; - drive_info = DRIVE_INFO; - screen_info = SCREEN_INFO; - aux_device_present = AUX_DEVICE_INFO; - memory_end = (1<<20) + (EXT_MEM_K<<10); - memory_end &= PAGE_MASK; - ramdisk_size = RAMDISK_SIZE; - copy_options(command_line,COMMAND_LINE); -#ifdef CONFIG_MAX_16M - if (memory_end > 16*1024*1024) - memory_end = 16*1024*1024; -#endif - if (MOUNT_ROOT_RDONLY) - root_mountflags |= MS_RDONLY; - if ((unsigned long)&end >= (1024*1024)) { - memory_start = (unsigned long) &end; - low_memory_start = PAGE_SIZE; - } else { - memory_start = 1024*1024; - low_memory_start = (unsigned long) &end; - } - low_memory_start = PAGE_ALIGN(low_memory_start); - memory_start = paging_init(memory_start,memory_end); - if (strncmp((char*)0x0FFFD9, "EISA", 4) == 0) - EISA_bus = 1; - trap_init(); - init_IRQ(); - sched_init(); - parse_options(command_line); - init_modules(); -#ifdef CONFIG_PROFILE - prof_buffer = (unsigned long *) memory_start; - prof_len = (unsigned long) &end; - prof_len >>= 2; - memory_start += prof_len * sizeof(unsigned long); -#endif - memory_start = console_init(memory_start,memory_end); - memory_start = bios32_init(memory_start,memory_end); - memory_start = kmalloc_init(memory_start,memory_end); - memory_start = chr_dev_init(memory_start,memory_end); - memory_start = blk_dev_init(memory_start,memory_end); - sti(); - calibrate_delay(); -#ifdef CONFIG_SCSI - memory_start = scsi_dev_init(memory_start,memory_end); -#endif -#ifdef CONFIG_INET - memory_start = net_dev_init(memory_start,memory_end); -#endif - memory_start = inode_init(memory_start,memory_end); - memory_start = file_table_init(memory_start,memory_end); - memory_start = name_cache_init(memory_start,memory_end); - mem_init(low_memory_start,memory_start,memory_end); - buffer_init(); - time_init(); - sock_init(); -#ifdef CONFIG_SYSVIPC - ipc_init(); -#endif - sti(); - check_bugs(); - - system_utsname.machine[1] = '0' + x86; - printk(linux_banner); - - move_to_user_mode(); - if (!fork()) /* we count on this going ok */ - init(); -/* - * task[0] is meant to be used as an "idle" task: it may not sleep, but - * it might do some general things like count free pages or it could be - * used to implement a reasonable LRU algorithm for the paging routines: - * anything that can be useful, but shouldn't take time from the real - * processes. - * - * Right now task[0] just does a infinite idle loop. - */ - for(;;) - idle(); -} diff --git a/arch/i386/math-emu/Makefile b/arch/i386/math-emu/Makefile new file mode 100644 index 000000000..2d391a9e6 --- /dev/null +++ b/arch/i386/math-emu/Makefile @@ -0,0 +1,52 @@ +# +# Makefile for wm-FPU-emu +# + +#DEBUG = -DDEBUGGING +DEBUG = +PARANOID = -DPARANOID +CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin + +.c.o: + $(CC) $(CFLAGS) $(MATH_EMULATION) -c $< + +.S.o: + $(CC) -D__ASSEMBLER__ $(PARANOID) -c $< + +.s.o: + $(CC) -c $< + +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 \ + reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \ + reg_div.o reg_mul.o reg_norm.o \ + reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \ + reg_round.o \ + wm_shrx.o wm_sqrt.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 + +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 new file mode 100644 index 000000000..2c0acb423 --- /dev/null +++ b/arch/i386/math-emu/README @@ -0,0 +1,436 @@ + +---------------------------------------------------------------------------+ + | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | 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 | + | published by the Free Software Foundation. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + | | + | You should have received a copy of the GNU General Public License | + | along with this program; if not, write to the Free Software | + | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | + | | + +---------------------------------------------------------------------------+ + + + +wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387 +which was my 80387 emulator for early versions of djgpp (gcc under +msdos); wm-emu387 was in turn based upon emu387 which was written by +DJ Delorie for djgpp. The interface to the Linux kernel is based upon +the original Linux math emulator by Linus Torvalds. + +My target FPU for wm-FPU-emu is that described in the Intel486 +Programmer's Reference Manual (1992 edition). Unfortunately, numerous +facets of the functioning of the FPU are not well covered in the +Reference Manual. The information in the manual has been supplemented +with measurements on real 80486's. Unfortunately, it is simply not +possible to be sure that all of the peculiarities of the 80486 have +been discovered, so there is always likely to be obscure differences +in the detailed behaviour of the emulator and a real 80486. + +wm-FPU-emu does not implement all of the behaviour of the 80486 FPU, +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 + + +--Bill Metzenthen + August 1994 + + +----------------------- Internals of wm-FPU-emu ----------------------- + +Numeric algorithms: +(1) Add, subtract, and multiply. Nothing remarkable in these. +(2) Divide has been tuned to get reasonable performance. The algorithm + is not the obvious one which most people seem to use, but is designed + to take advantage of the characteristics of the 80386. I expect that + it has been invented many times before I discovered it, but I have not + seen it. It is based upon one of those ideas which one carries around + for years without ever bothering to check it out. +(3) The sqrt function has been tuned to get good performance. It is based + upon Newton's classic method. Performance was improved by capitalizing + upon the properties of Newton's method, and the code is once again + structured taking account of the 80386 characteristics. +(4) The trig, log, and exp functions are based in each case upon quasi- + "optimal" polynomial approximations. My definition of "optimal" was + based upon getting good accuracy with reasonable speed. +(5) The argument reducing code for the trig function effectively uses + a value of pi which is accurate to more than 128 bits. As a consequence, + the reduced argument is accurate to more than 64 bits for arguments up + to a few pi, and accurate to more than 64 bits for most arguments, + even for arguments approaching 2^63. This is far superior to an + 80486, which uses a value of pi which is accurate to 66 bits. + +The code of the emulator is complicated slightly by the need to +account for a limited form of re-entrancy. Normally, the emulator will +emulate each FPU instruction to completion without interruption. +However, it may happen that when the emulator is accessing the user +memory space, swapping may be needed. In this case the emulator may be +temporarily suspended while disk i/o takes place. During this time +another process may use the emulator, thereby perhaps changing static +variables. The code which accesses user memory is confined to five +files: + fpu_entry.c + reg_ld_str.c + load_store.c + get_address.c + errors.c +As from version 1.12 of the emulator, no static variables are used +(apart from those in the kernel's per-process tables). The emulator is +therefore now fully re-entrant, rather than having just the restricted +form of re-entrancy which is required by the Linux kernel. + +----------------------- Limitations of wm-FPU-emu ----------------------- + +There are a number of differences between the current wm-FPU-emu +(version 1.20) and the 80486 FPU (apart from bugs). Some of the more +important differences are listed below: + +The Roundup flag does not have much meaning for the transcendental +functions and its 80486 value with these functions is likely to differ +from its emulator value. + +In a few rare cases the Underflow flag obtained with the emulator will +be different from that obtained with an 80486. This occurs when the +following conditions apply simultaneously: +(a) the operands have a higher precision than the current setting of the + precision control (PC) flags. +(b) the underflow exception is masked. +(c) the magnitude of the exact result (before rounding) is less than 2^-16382. +(d) the magnitude of the final result (after rounding) is exactly 2^-16382. +(e) the magnitude of the exact result would be exactly 2^-16382 if the + operands were rounded to the current precision before the arithmetic + operation was performed. +If all of these apply, the emulator will set the Underflow flag but a real +80486 will not. + +NOTE: Certain formats of Extended Real are UNSUPPORTED. They are +unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities, +and Unnormals. None of these will be generated by an 80486 or by the +emulator. Do not use them. The emulator treats them differently in +detail from the way an 80486 does. + +The emulator treats PseudoDenormals differently from an 80486. These +numbers are in fact properly normalised numbers with the exponent +offset by 1, and the emulator treats them as such. Unlike the 80486, +the emulator does not generate a Denormal Operand exception for these +numbers. The arithmetical results produced when using such a number as +an operand are the same for the emulator and a real 80486 (apart from +any slight precision difference for the transcendental functions). +Neither the emulator nor an 80486 produces one of these numbers as the +result of any arithmetic operation. An 80486 can keep one of these +numbers in an FPU register with its identity as a PseudoDenormal, but +the emulator will not; they are always converted to a valid number. + +Self modifying code can cause the emulator to fail. An example of such +code is: + movl %esp,[%ebx] + fld1 +The FPU instruction may be (usually will be) loaded into the pre-fetch +queue of the cpu before the mov instruction is executed. If the +destination of the 'movl' overlaps the FPU instruction then the bytes +in the prefetch queue and memory will be inconsistent when the FPU +instruction is executed. The emulator will be invoked but will not be +able to find the instruction which caused the device-not-present +exception. For this case, the emulator cannot emulate the behaviour of +an 80486DX. + +Handling of the address size override prefix byte (0x67) has not been +extensively tested yet. A major problem exists because using it in +vm86 mode can cause a general protection fault. Address offsets +greater than 0xffff appear to be illegal in vm86 mode but are quite +acceptable (and work) in real mode. A small test program developed to +check the addressing, and which runs successfully in real mode, +crashes dosemu under Linux and also brings Windows down with a general +protection fault message when run under the MS-DOS prompt of Windows +3.1. (The program simply reads data from a valid address). + +The emulator supports 16-bit protected mode, with one difference from +an 80486DX. A 80486DX will allow some floating point instructions to +write a few bytes below the lowest address of the stack. The emulator +will not allow this in 16-bit protected mode: no instructions are +allowed to write outside the bounds set by the protection. + +----------------------- Performance of wm-FPU-emu ----------------------- + +Speed. +----- + +The speed of floating point computation with the emulator will depend +upon instruction mix. Relative performance is best for the instructions +which require most computation. The simple instructions are adversely +affected by the fpu instruction trap overhead. + + +Timing: Some simple timing tests have been made on the emulator functions. +The times include load/store instructions. All times are in microseconds +measured on a 33MHz 386 with 64k cache. The Turbo C tests were under +ms-dos, the next two columns are for emulators running with the djgpp +ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97, +using libm4.0 (hard). + +function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu + + + 60.5 154.8 76.5 139.4 + - 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7 + * 71.0 190.8 79.6 146.6 + / 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1 + + sin() 310.8 4692.0 319.0 398.5 + cos() 284.4 4855.2 308.0 388.7 + tan() 495.0 8807.1 394.9 504.7 + atan() 328.9 4866.4 601.1 419.5-491.9 + + sqrt() 128.7 crashed 145.2 227.0 + log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1 + exp() 479.1 6619.2 469.1 850.8 + + +The performance under Linux is improved by the use of look-ahead code. +The following results show the improvement which is obtained under +Linux due to the look-ahead code. Also given are the times for the +original Linux emulator with the 4.1 'soft' lib. + + [ Linus' note: I changed look-ahead to be the default under linux, as + there was no reason not to use it after I had edited it to be + disabled during tracing ] + + wm-FPU-emu w original w + look-ahead 'soft' lib + + 106.4 190.2 + - 108.6-111.6 192.4-216.2 + * 113.4 193.1 + / 108.8-124.4 700.1-706.2 + + sin() 390.5 2642.0 + cos() 381.5 2767.4 + tan() 496.5 3153.3 + atan() 367.2-435.5 2439.4-3396.8 + + sqrt() 195.1 4732.5 + log() 358.0-387.5 3359.2-3390.3 + exp() 619.3 4046.4 + + +These figures are now somewhat out-of-date. The emulator has become +progressively slower for most functions as more of the 80486 features +have been implemented. + + +----------------------- Accuracy of wm-FPU-emu ----------------------- + + +The accuracy of the emulator is in almost all cases equal to or better +than that of an Intel 80486 FPU. + +The results of the basic arithmetic functions (+,-,*,/), and fsqrt +match those of an 80486 FPU. They are the best possible; the error for +these never exceeds 1/2 an lsb. The fprem and fprem1 instructions +return exact results; they have no error. + + +The following table compares the emulator accuracy for the sqrt(), +trig and log functions against the Turbo C "emulator". For this table, +each function was tested at about 400 points. Ideal worst-case results +would be 64 bits. The reduced Turbo C accuracy of cos() and tan() for +arguments greater than pi/4 can be thought of as being related to the +precision of the argument x; e.g. an argument of pi/2-(1e-10) which is +accurate to 64 bits can result in a relative accuracy in cos() of +about 64 + log2(cos(x)) = 31 bits. + + +Function Tested x range Worst result Turbo C + (relative bits) + +sqrt(x) 1 .. 2 64.1 63.2 +atan(x) 1e-10 .. 200 64.2 62.8 +cos(x) 0 .. pi/2-(1e-10) 64.4 (x <= pi/4) 62.4 + 64.1 (x = pi/2-(1e-10)) 31.9 +sin(x) 1e-10 .. pi/2 64.0 62.8 +tan(x) 1e-10 .. pi/2-(1e-10) 64.0 (x <= pi/4) 62.1 + 64.1 (x = pi/2-(1e-10)) 31.9 +exp(x) 0 .. 1 63.1 ** 62.9 +log(x) 1+1e-6 .. 2 63.8 ** 62.1 + +** The accuracy for exp() and log() is low because the FPU (emulator) +does not compute them directly; two operations are required. + + +The emulator passes the "paranoia" tests (compiled with gcc 2.3.3 or +later) for 'float' variables (24 bit precision numbers) when precision +control is set to 24, 53 or 64 bits, and for 'double' variables (53 +bit precision numbers) when precision control is set to 53 bits (a +properly performing FPU cannot pass the 'paranoia' tests for 'double' +variables when precision control is set to 64 bits). + +The code for reducing the argument for the trig functions (fsin, fcos, +fptan and fsincos) has been improved and now effectively uses a value +for pi which is accurate to more than 128 bits precision. As a +consequence, the accuracy of these functions for large arguments has +been dramatically improved (and is now very much better than an 80486 +FPU). There is also now no degradation of accuracy for fcos and fptan +for operands close to pi/2. Measured results are (note that the +definition of accuracy has changed slightly from that used for the +above table): + +Function Tested x range Worst result + (absolute bits) + +cos(x) 0 .. 9.22e+18 62.0 +sin(x) 1e-16 .. 9.22e+18 62.1 +tan(x) 1e-16 .. 9.22e+18 61.8 + +It is possible with some effort to find very large arguments which +give much degraded precision. For example, the integer number + 8227740058411162616.0 +is within about 10e-7 of a multiple of pi. To find the tan (for +example) of this number to 64 bits precision it would be necessary to +have a value of pi which had about 150 bits precision. The FPU +emulator computes the result to about 42.6 bits precision (the correct +result is about -9.739715e-8). On the other hand, an 80486 FPU returns +0.01059, which in relative terms is hopelessly inaccurate. + +For arguments close to critical angles (which occur at multiples of +pi/2) the emulator is more accurate than an 80486 FPU. For very large +arguments, the emulator is far more accurate. + + +Prior to version 1.20 of the emulator, the accuracy of the results for +the transcendental functions (in their principal range) was not as +good as the results from an 80486 FPU. From version 1.20, the accuracy +has been considerably improved and these functions now give measured +worst-case results which are better than the worst-case results given +by an 80486 FPU. + +The following table gives the measured results for the emulator. The +number of randomly selected arguments in each case is about half a +million. The group of three columns gives the frequency of the given +accuracy in number of times per million, thus the second of these +columns shows that an accuracy of between 63.80 and 63.89 bits was +found at a rate of 133 times per one million measurements for fsin. +The results show that the fsin, fcos and fptan instructions return +results which are in error (i.e. less accurate than the best possible +result (which is 64 bits)) for about one per cent of all arguments +between -pi/2 and +pi/2. The other instructions have a lower +frequency of results which are in error. The last two columns give +the worst accuracy which was found (in bits) and the approximate value +of the argument which produced it. + + frequency (per M) + ------------------- --------------- +instr arg range # tests 63.7 63.8 63.9 worst at arg + bits bits bits bits +----- ------------ ------- ---- ---- ----- ----- -------- +fsin (0,pi/2) 547756 0 133 10673 63.89 0.451317 +fcos (0,pi/2) 547563 0 126 10532 63.85 0.700801 +fptan (0,pi/2) 536274 11 267 10059 63.74 0.784876 +fpatan 4 quadrants 517087 0 8 1855 63.88 0.435121 (4q) +fyl2x (0,20) 541861 0 0 1323 63.94 1.40923 (x) +fyl2xp1 (-.293,.414) 520256 0 0 5678 63.93 0.408542 (x) +f2xm1 (-1,1) 538847 4 481 6488 63.79 0.167709 + + +Tests performed on an 80486 FPU showed results of lower accuracy. The +following table gives the results which were obtained with an AMD +486DX2/66 (other tests indicate that an Intel 486DX produces +identical results). The tests were basically the same as those used +to measure the emulator (the values, being random, were in general not +the same). The total number of tests for each instruction are given +at the end of the table, in case each about 100k tests were performed. +Another line of figures at the end of the table shows that most of the +instructions return results which are in error for more than 10 +percent of the arguments tested. + +The numbers in the body of the table give the approx number of times a +result of the given accuracy in bits (given in the left-most column) +was obtained per one million arguments. For three of the instructions, +two columns of results are given: * The second column for f2xm1 gives +the number cases where the results of the first column were for a +positive argument, this shows that this instruction gives better +results for positive arguments than it does for negative. * In the +cases of fcos and fptan, the first column gives the results when all +cases where arguments greater than 1.5 were removed from the results +given in the second column. Unlike the emulator, an 80486 FPU returns +results of relatively poor accuracy for these instructions when the +argument approaches pi/2. The table does not show those cases when the +accuracy of the results were less than 62 bits, which occurs quite +often for fsin and fptan when the argument approaches pi/2. This poor +accuracy is discussed above in relation to the Turbo C "emulator", and +the accuracy of the value of pi. + + +bits f2xm1 f2xm1 fpatan fcos fcos fyl2x fyl2xp1 fsin fptan fptan +62.0 0 0 0 0 437 0 0 0 0 925 +62.1 0 0 10 0 894 0 0 0 0 1023 +62.2 14 0 0 0 1033 0 0 0 0 945 +62.3 57 0 0 0 1202 0 0 0 0 1023 +62.4 385 0 0 10 1292 0 23 0 0 1178 +62.5 1140 0 0 119 1649 0 39 0 0 1149 +62.6 2037 0 0 189 1620 0 16 0 0 1169 +62.7 5086 14 0 646 2315 10 101 35 39 1402 +62.8 8818 86 0 984 3050 59 287 131 224 2036 +62.9 11340 1355 0 2126 4153 79 605 357 321 1948 +63.0 15557 4750 0 3319 5376 246 1281 862 808 2688 +63.1 20016 8288 0 4620 6628 511 2569 1723 1510 3302 +63.2 24945 11127 10 6588 8098 1120 4470 2968 2990 4724 +63.3 25686 12382 69 8774 10682 1906 6775 4482 5474 7236 +63.4 29219 14722 79 11109 12311 3094 9414 7259 8912 10587 +63.5 30458 14936 393 13802 15014 5874 12666 9609 13762 15262 +63.6 32439 16448 1277 17945 19028 10226 15537 14657 19158 20346 +63.7 35031 16805 4067 23003 23947 18910 20116 21333 25001 26209 +63.8 33251 15820 7673 24781 25675 24617 25354 24440 29433 30329 +63.9 33293 16833 18529 28318 29233 31267 31470 27748 29676 30601 + +Per cent with error: + 30.9 3.2 18.5 9.8 13.1 11.6 17.4 +Total arguments tested: + 70194 70099 101784 100641 100641 101799 128853 114893 102675 102675 + + +------------------------- Contributors ------------------------------- + +A number of people have contributed to the development of the +emulator, often by just reporting bugs, sometimes with suggested +fixes, and a few kind people have provided me with access in one way +or another to an 80486 machine. Contributors include (to those people +who I may have forgotten, please forgive me): + +Linus Torvalds +Tommy.Thorn@daimi.aau.dk +Andrew.Tridgell@anu.edu.au +Nick Holloway, alfie@dcs.warwick.ac.uk +Hermano Moura, moura@dcs.gla.ac.uk +Jon Jagger, J.Jagger@scp.ac.uk +Lennart Benschop +Brian Gallew, geek+@CMU.EDU +Thomas Staniszewski, ts3v+@andrew.cmu.edu +Martin Howell, mph@plasma.apana.org.au +M Saggaf, alsaggaf@athena.mit.edu +Peter Barker, PETER@socpsy.sci.fau.edu +tom@vlsivie.tuwien.ac.at +Dan Russel, russed@rpi.edu +Daniel Carosone, danielce@ee.mu.oz.au +cae@jpmorgan.com +Hamish Coleman, t933093@minyos.xx.rmit.oz.au +Bruce Evans, bde@kralizec.zeta.org.au +Timo Korvola, Timo.Korvola@hut.fi +Rick Lyons, rick@razorback.brisnet.org.au +Rick, jrs@world.std.com + +...and numerous others who responded to my request for help with +a real 80486. + diff --git a/arch/i386/math-emu/control_w.h b/arch/i386/math-emu/control_w.h new file mode 100644 index 000000000..ef5fced39 --- /dev/null +++ b/arch/i386/math-emu/control_w.h @@ -0,0 +1,45 @@ +/*---------------------------------------------------------------------------+ + | control_w.h | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _CONTROLW_H_ +#define _CONTROLW_H_ + +#ifdef __ASSEMBLER__ +#define _Const_(x) $##x +#else +#define _Const_(x) x +#endif + +#define CW_RC _Const_(0x0C00) /* rounding control */ +#define CW_PC _Const_(0x0300) /* precision control */ + +#define CW_Precision Const_(0x0020) /* loss of precision mask */ +#define CW_Underflow Const_(0x0010) /* underflow mask */ +#define CW_Overflow Const_(0x0008) /* overflow mask */ +#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */ +#define CW_Denormal Const_(0x0002) /* denormalized operand mask */ +#define CW_Invalid Const_(0x0001) /* invalid operation mask */ + +#define CW_Exceptions _Const_(0x003f) /* all masks */ + +#define RC_RND _Const_(0x0000) +#define RC_DOWN _Const_(0x0400) +#define RC_UP _Const_(0x0800) +#define RC_CHOP _Const_(0x0C00) + +/* p 15-5: Precision control bits affect only the following: + ADD, SUB(R), MUL, DIV(R), and SQRT */ +#define PR_24_BITS _Const_(0x000) +#define PR_53_BITS _Const_(0x200) +#define PR_64_BITS _Const_(0x300) +#define PR_RESERVED_BITS _Const_(0x100) +/* FULL_PRECISION simulates all exceptions masked */ +#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f) + +#endif _CONTROLW_H_ diff --git a/arch/i386/math-emu/div_Xsig.S b/arch/i386/math-emu/div_Xsig.S new file mode 100644 index 000000000..67d8be964 --- /dev/null +++ b/arch/i386/math-emu/div_Xsig.S @@ -0,0 +1,369 @@ + .file "div_Xsig.S" +/*---------------------------------------------------------------------------+ + | div_Xsig.S | + | | + | Division subroutine for 96 bit quantities | + | | + | Copyright (C) 1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Divide the 96 bit quantity pointed to by a, by that pointed to by b, and | + | put the 96 bit result at the location d. | + | | + | The result may not be accurate to 96 bits. It is intended for use where | + | a result better than 64 bits is required. The result should usually be | + | good to at least 94 bits. | + | The returned result is actually divided by one half. This is done to | + | prevent overflow. | + | | + | .aaaaaaaaaaaaaa / .bbbbbbbbbbbbb -> .dddddddddddd | + | | + | void div_Xsig(Xsig *a, Xsig *b, Xsig *dest) | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" + + +#define XsigLL(x) (x) +#define XsigL(x) 4(x) +#define XsigH(x) 8(x) + + +#ifndef NON_REENTRANT_FPU +/* + Local storage on the stack: + Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 + */ +#define FPU_accum_3 -4(%ebp) +#define FPU_accum_2 -8(%ebp) +#define FPU_accum_1 -12(%ebp) +#define FPU_accum_0 -16(%ebp) +#define FPU_result_3 -20(%ebp) +#define FPU_result_2 -24(%ebp) +#define FPU_result_1 -28(%ebp) + +#else +.data +/* + Local storage in a static area: + Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 + */ + .align 2,0 +FPU_accum_3: + .long 0 +FPU_accum_2: + .long 0 +FPU_accum_1: + .long 0 +FPU_accum_0: + .long 0 +FPU_result_3: + .long 0 +FPU_result_2: + .long 0 +FPU_result_1: + .long 0 +#endif NON_REENTRANT_FPU + + +.text + .align 2,144 + +.globl _div_Xsig + +_div_Xsig: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $28,%esp +#endif NON_REENTRANT_FPU + + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi /* pointer to num */ + movl PARAM2,%ebx /* pointer to denom */ + +#ifdef PARANOID + testl $0x80000000, XsigH(%ebx) /* Divisor */ + je L_bugged +#endif PARANOID + + +/*---------------------------------------------------------------------------+ + | Divide: Return arg1/arg2 to arg3. | + | | + | The maximum returned value is (ignoring exponents) | + | .ffffffff ffffffff | + | ------------------ = 1.ffffffff fffffffe | + | .80000000 00000000 | + | and the minimum is | + | .80000000 00000000 | + | ------------------ = .80000000 00000001 (rounded) | + | .ffffffff ffffffff | + | | + +---------------------------------------------------------------------------*/ + + /* Save extended dividend in local register */ + + /* Divide by 2 to prevent overflow */ + clc + movl XsigH(%esi),%eax + rcrl %eax + movl %eax,FPU_accum_3 + movl XsigL(%esi),%eax + rcrl %eax + movl %eax,FPU_accum_2 + movl XsigLL(%esi),%eax + rcrl %eax + movl %eax,FPU_accum_1 + movl $0,%eax + rcrl %eax + movl %eax,FPU_accum_0 + + movl FPU_accum_2,%eax /* Get the current num */ + movl FPU_accum_3,%edx + +/*----------------------------------------------------------------------*/ +/* Initialization done. + Do the first 32 bits. */ + + /* We will divide by a number which is too large */ + movl XsigH(%ebx),%ecx + addl $1,%ecx + jnc LFirst_div_not_1 + + /* here we need to divide by 100000000h, + i.e., no division at all.. */ + mov %edx,%eax + jmp LFirst_div_done + +LFirst_div_not_1: + divl %ecx /* Divide the numerator by the augmented + denom ms dw */ + +LFirst_div_done: + movl %eax,FPU_result_3 /* Put the result in the answer */ + + mull XsigH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,FPU_accum_2 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_3 + + movl FPU_result_3,%eax /* Get the result back */ + mull XsigL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + sbbl $0,FPU_accum_3 + je LDo_2nd_32_bits /* Must check for non-zero result here */ + +#ifdef PARANOID + jb L_bugged_1 +#endif PARANOID + + /* need to subtract another once of the denom */ + incl FPU_result_3 /* Correct the answer */ + + movl XsigL(%ebx),%eax + movl XsigH(%ebx),%edx + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + +#ifdef PARANOID + sbbl $0,FPU_accum_3 + jne L_bugged_1 /* Must check for non-zero result here */ +#endif PARANOID + +/*----------------------------------------------------------------------*/ +/* Half of the main problem is done, there is just a reduced numerator + to handle now. + Work with the second 32 bits, FPU_accum_0 not used from now on */ +LDo_2nd_32_bits: + movl FPU_accum_2,%edx /* get the reduced num */ + movl FPU_accum_1,%eax + + /* need to check for possible subsequent overflow */ + cmpl XsigH(%ebx),%edx + jb LDo_2nd_div + ja LPrevent_2nd_overflow + + cmpl XsigL(%ebx),%eax + jb LDo_2nd_div + +LPrevent_2nd_overflow: +/* The numerator is greater or equal, would cause overflow */ + /* prevent overflow */ + subl XsigL(%ebx),%eax + sbbl XsigH(%ebx),%edx + movl %edx,FPU_accum_2 + movl %eax,FPU_accum_1 + + incl FPU_result_3 /* Reflect the subtraction in the answer */ + +#ifdef PARANOID + je L_bugged_2 /* Can't bump the result to 1.0 */ +#endif PARANOID + +LDo_2nd_div: + cmpl $0,%ecx /* augmented denom msw */ + jnz LSecond_div_not_1 + + /* %ecx == 0, we are dividing by 1.0 */ + mov %edx,%eax + jmp LSecond_div_done + +LSecond_div_not_1: + divl %ecx /* Divide the numerator by the denom ms dw */ + +LSecond_div_done: + movl %eax,FPU_result_2 /* Put the result in the answer */ + + mull XsigH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + movl FPU_result_2,%eax /* Get the result back */ + mull XsigL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,FPU_accum_0 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */ + sbbl $0,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + jz LDo_3rd_32_bits + +#ifdef PARANOID + cmpl $1,FPU_accum_2 + jne L_bugged_2 +#endif PARANOID + + /* need to subtract another once of the denom */ + movl XsigL(%ebx),%eax + movl XsigH(%ebx),%edx + subl %eax,FPU_accum_0 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_1 + sbbl $0,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 + jne L_bugged_2 +#endif PARANOID + + addl $1,FPU_result_2 /* Correct the answer */ + adcl $0,FPU_result_3 + +#ifdef PARANOID + jc L_bugged_2 /* Must check for non-zero result here */ +#endif PARANOID + +/*----------------------------------------------------------------------*/ +/* The division is essentially finished here, we just need to perform + tidying operations. + Deal with the 3rd 32 bits */ +LDo_3rd_32_bits: + /* We use an approximation for the third 32 bits. + To take account of the 3rd 32 bits of the divisor + (call them del), we subtract del * (a/b) */ + + movl FPU_result_3,%eax /* a/b */ + mull XsigLL(%ebx) /* del */ + + subl %edx,FPU_accum_1 + + /* A borrow indicates that the result is negative */ + jnb LTest_over + + movl XsigH(%ebx),%edx + addl %edx,FPU_accum_1 + + subl $1,FPU_result_2 /* Adjust the answer */ + sbbl $0,FPU_result_3 + + /* The above addition might not have been enough, check again. */ + movl FPU_accum_1,%edx /* get the reduced num */ + cmpl XsigH(%ebx),%edx /* denom */ + jb LDo_3rd_div + + movl XsigH(%ebx),%edx + addl %edx,FPU_accum_1 + + subl $1,FPU_result_2 /* Adjust the answer */ + sbbl $0,FPU_result_3 + jmp LDo_3rd_div + +LTest_over: + movl FPU_accum_1,%edx /* get the reduced num */ + + /* need to check for possible subsequent overflow */ + cmpl XsigH(%ebx),%edx /* denom */ + jb LDo_3rd_div + + /* prevent overflow */ + subl XsigH(%ebx),%edx + movl %edx,FPU_accum_1 + + addl $1,FPU_result_2 /* Reflect the subtraction in the answer */ + adcl $0,FPU_result_3 + +LDo_3rd_div: + movl FPU_accum_0,%eax + movl FPU_accum_1,%edx + divl XsigH(%ebx) + + movl %eax,FPU_result_1 /* Rough estimate of third word */ + + movl PARAM3,%esi /* pointer to answer */ + + movl FPU_result_1,%eax + movl %eax,XsigLL(%esi) + movl FPU_result_2,%eax + movl %eax,XsigL(%esi) + movl FPU_result_3,%eax + movl %eax,XsigH(%esi) + +L_exit: + popl %ebx + popl %edi + popl %esi + + leave + ret + + +#ifdef PARANOID +/* The logic is wrong if we got here */ +L_bugged: + pushl EX_INTERNAL|0x240 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_1: + pushl EX_INTERNAL|0x241 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_2: + pushl EX_INTERNAL|0x242 + call EXCEPTION + pop %ebx + jmp L_exit +#endif PARANOID diff --git a/arch/i386/math-emu/div_small.S b/arch/i386/math-emu/div_small.S new file mode 100644 index 000000000..0225a96d4 --- /dev/null +++ b/arch/i386/math-emu/div_small.S @@ -0,0 +1,50 @@ + .file "div_small.S" +/*---------------------------------------------------------------------------+ + | div_small.S | + | | + | 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 | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | unsigned long div_small(unsigned long long *x, unsigned long y) | + +---------------------------------------------------------------------------*/ + +#include "fpu_asm.h" + +.text + .align 2,144 + +.globl _div_small + +_div_small: + pushl %ebp + movl %esp,%ebp + + pushl %esi + + movl PARAM1,%esi /* pointer to num */ + movl PARAM2,%ecx /* The denominator */ + + movl 4(%esi),%eax /* Get the current num msw */ + xorl %edx,%edx + divl %ecx + + movl %eax,4(%esi) + + movl (%esi),%eax /* Get the num lsw */ + divl %ecx + + movl %eax,(%esi) + + movl %edx,%eax /* Return the remainder in eax */ + + popl %esi + + leave + ret + diff --git a/arch/i386/math-emu/errors.c b/arch/i386/math-emu/errors.c new file mode 100644 index 000000000..e34eec942 --- /dev/null +++ b/arch/i386/math-emu/errors.c @@ -0,0 +1,671 @@ +/*---------------------------------------------------------------------------+ + | errors.c | + | | + | 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 | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ + +#include <linux/signal.h> + +#include <asm/segment.h> + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "control_w.h" +#include "reg_constant.h" +#include "version.h" + +/* */ +#undef PRINT_MESSAGES +/* */ + + +void Un_impl(void) +{ + unsigned char byte1, FPU_modrm; + unsigned long address = FPU_ORIG_EIP; + + RE_ENTRANT_CHECK_OFF; + /* No need to verify_area(), we have previously fetched these bytes. */ + printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address); + if ( FPU_CS == USER_CS ) + { + while ( 1 ) + { + byte1 = get_fs_byte((unsigned char *) address); + if ( (byte1 & 0xf8) == 0xd8 ) break; + printk("[%02x]", byte1); + address++; + } + printk("%02x ", byte1); + FPU_modrm = get_fs_byte(1 + (unsigned char *) address); + + if (FPU_modrm >= 0300) + printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); + else + printk("/%d\n", (FPU_modrm >> 3) & 7); + } + else + { + printk("cs selector = %04x\n", FPU_CS); + } + + RE_ENTRANT_CHECK_ON; + + EXCEPTION(EX_Invalid); + +} + + +/* + Called for opcodes which are illegal and which are known to result in a + SIGILL with a real 80486. + */ +void FPU_illegal(void) +{ + math_abort(FPU_info,SIGILL); +} + + + +void emu_printall() +{ + int i; + static char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR", + "DeNorm", "Inf", "NaN", "Empty" }; + unsigned char byte1, FPU_modrm; + unsigned long address = FPU_ORIG_EIP; + + RE_ENTRANT_CHECK_OFF; + /* No need to verify_area(), we have previously fetched these bytes. */ + printk("At %p:", (void *) address); + if ( FPU_CS == USER_CS ) + { +#define MAX_PRINTED_BYTES 20 + for ( i = 0; i < MAX_PRINTED_BYTES; i++ ) + { + byte1 = get_fs_byte((unsigned char *) address); + if ( (byte1 & 0xf8) == 0xd8 ) + { + printk(" %02x", byte1); + break; + } + printk(" [%02x]", byte1); + address++; + } + if ( i == MAX_PRINTED_BYTES ) + printk(" [more..]\n"); + else + { + FPU_modrm = get_fs_byte(1 + (unsigned char *) address); + + if (FPU_modrm >= 0300) + printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); + else + printk(" /%d, mod=%d rm=%d\n", + (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7); + } + } + else + { + printk("%04x\n", FPU_CS); + } + + partial_status = status_word(); + +#ifdef DEBUGGING +if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n"); +if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n"); +if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n"); +if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n"); +if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n"); +if ( partial_status & SW_Summary ) printk("SW: exception summary\n"); +if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n"); +if ( partial_status & SW_Precision ) printk("SW: loss of precision\n"); +if ( partial_status & SW_Underflow ) printk("SW: underflow\n"); +if ( partial_status & SW_Overflow ) printk("SW: overflow\n"); +if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n"); +if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n"); +if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n"); +#endif DEBUGGING + + printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n", + partial_status & 0x8000 ? 1 : 0, /* busy */ + (partial_status & 0x3800) >> 11, /* stack top pointer */ + partial_status & 0x80 ? 1 : 0, /* Error summary status */ + partial_status & 0x40 ? 1 : 0, /* Stack flag */ + partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */ + partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */ + partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0, + partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0, + partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0); + +printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n", + control_word & 0x1000 ? 1 : 0, + (control_word & 0x800) >> 11, (control_word & 0x400) >> 10, + (control_word & 0x200) >> 9, (control_word & 0x100) >> 8, + control_word & 0x80 ? 1 : 0, + control_word & SW_Precision?1:0, control_word & SW_Underflow?1:0, + control_word & SW_Overflow?1:0, control_word & SW_Zero_Div?1:0, + control_word & SW_Denorm_Op?1:0, control_word & SW_Invalid?1:0); + + for ( i = 0; i < 8; i++ ) + { + FPU_REG *r = &st(i); + switch (r->tag) + { + case TW_Empty: + continue; + break; + case TW_Zero: +#if 0 + printk("st(%d) %c .0000 0000 0000 0000 ", + i, r->sign ? '-' : '+'); + break; +#endif + case TW_Valid: + case TW_NaN: +/* case TW_Denormal: */ + case TW_Infinity: + printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6ld ", i, + r->sign ? '-' : '+', + (long)(r->sigh >> 16), + (long)(r->sigh & 0xFFFF), + (long)(r->sigl >> 16), + (long)(r->sigl & 0xFFFF), + r->exp - EXP_BIAS + 1); + break; + default: + printk("Whoops! Error in errors.c "); + break; + } + printk("%s\n", tag_desc[(int) (unsigned) r->tag]); + } + +#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; +} exception_names[] = { + { EX_StackOver, "stack overflow" }, + { EX_StackUnder, "stack underflow" }, + { EX_Precision, "loss of precision" }, + { EX_Underflow, "underflow" }, + { EX_Overflow, "overflow" }, + { EX_ZeroDiv, "divide by zero" }, + { EX_Denormal, "denormalized operand" }, + { EX_Invalid, "invalid operation" }, + { EX_INTERNAL, "INTERNAL BUG in "FPU_VERSION }, + { 0, NULL } +}; + +/* + EX_INTERNAL is always given with a code which indicates where the + error was detected. + + Internal error types: + 0x14 in fpu_etc.c + 0x1nn in a *.c file: + 0x101 in reg_add_sub.c + 0x102 in reg_mul.c + 0x104 in poly_atan.c + 0x105 in reg_mul.c + 0x107 in fpu_trig.c + 0x108 in reg_compare.c + 0x109 in reg_compare.c + 0x110 in reg_add_sub.c + 0x111 in fpe_entry.c + 0x112 in fpu_trig.c + 0x113 in errors.c + 0x115 in fpu_trig.c + 0x116 in fpu_trig.c + 0x117 in fpu_trig.c + 0x118 in fpu_trig.c + 0x119 in fpu_trig.c + 0x120 in poly_atan.c + 0x121 in reg_compare.c + 0x122 in reg_compare.c + 0x123 in reg_compare.c + 0x125 in fpu_trig.c + 0x126 in fpu_entry.c + 0x127 in poly_2xm1.c + 0x128 in fpu_entry.c + 0x129 in fpu_entry.c + 0x130 in get_address.c + 0x131 in get_address.c + 0x132 in get_address.c + 0x133 in get_address.c + 0x140 in load_store.c + 0x141 in load_store.c + 0x150 in poly_sin.c + 0x151 in poly_sin.c + 0x160 in reg_ld_str.c + 0x161 in reg_ld_str.c + 0x162 in reg_ld_str.c + 0x163 in reg_ld_str.c + 0x2nn in an *.S file: + 0x201 in reg_u_add.S + 0x202 in reg_u_div.S + 0x203 in reg_u_div.S + 0x204 in reg_u_div.S + 0x205 in reg_u_mul.S + 0x206 in reg_u_sub.S + 0x207 in wm_sqrt.S + 0x208 in reg_div.S + 0x209 in reg_u_sub.S + 0x210 in reg_u_sub.S + 0x211 in reg_u_sub.S + 0x212 in reg_u_sub.S + 0x213 in wm_sqrt.S + 0x214 in wm_sqrt.S + 0x215 in wm_sqrt.S + 0x220 in reg_norm.S + 0x221 in reg_norm.S + 0x230 in reg_round.S + 0x231 in reg_round.S + 0x232 in reg_round.S + 0x233 in reg_round.S + 0x234 in reg_round.S + 0x235 in reg_round.S + 0x236 in reg_round.S + 0x240 in div_Xsig.S + 0x241 in div_Xsig.S + 0x242 in div_Xsig.S + */ + +void exception(int n) +{ + int i, int_type; + + int_type = 0; /* Needed only to stop compiler warnings */ + if ( n & EX_INTERNAL ) + { + int_type = n - EX_INTERNAL; + n = EX_INTERNAL; + /* Set lots of exception bits! */ + partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward); + } + else + { + /* Extract only the bits which we use to set the status word */ + n &= (SW_Exc_Mask); + /* Set the corresponding exception bit */ + partial_status |= n; + /* Set summary bits iff exception isn't masked */ + if ( partial_status & ~control_word & CW_Exceptions ) + partial_status |= (SW_Summary | SW_Backward); + if ( n & (SW_Stack_Fault | EX_Precision) ) + { + if ( !(n & SW_C1) ) + /* This bit distinguishes over- from underflow for a stack fault, + and roundup from round-down for precision loss. */ + partial_status &= ~SW_C1; + } + } + + RE_ENTRANT_CHECK_OFF; + if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) ) + { +#ifdef PRINT_MESSAGES + /* My message from the sponsor */ + printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\n"); +#endif PRINT_MESSAGES + + /* Get a name string for error reporting */ + for (i=0; exception_names[i].type; i++) + if ( (exception_names[i].type & n) == exception_names[i].type ) + break; + + if (exception_names[i].type) + { +#ifdef PRINT_MESSAGES + printk("FP Exception: %s!\n", exception_names[i].name); +#endif PRINT_MESSAGES + } + else + printk("FPU emulator: Unknown Exception: 0x%04x!\n", n); + + if ( n == EX_INTERNAL ) + { + printk("FPU emulator: Internal error type 0x%04x\n", int_type); + emu_printall(); + } +#ifdef PRINT_MESSAGES + else + emu_printall(); +#endif PRINT_MESSAGES + + /* + * 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. + */ +/* regs[0].tag |= TW_FPU_Interrupt; */ + } + RE_ENTRANT_CHECK_ON; + +#ifdef __DEBUG__ + math_abort(FPU_info,SIGFPE); +#endif __DEBUG__ + +} + + +/* Real operation attempted on two operands, one a NaN. */ +/* Returns nz if the exception is unmasked */ +asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest) +{ + FPU_REG const *x; + int signalling; + + /* The default result for the case of two "equal" NaNs (signs may + differ) is chosen to reproduce 80486 behaviour */ + x = a; + if (a->tag == TW_NaN) + { + if (b->tag == TW_NaN) + { + signalling = !(a->sigh & b->sigh & 0x40000000); + /* find the "larger" */ + if ( significand(a) < significand(b) ) + x = b; + } + else + { + /* return the quiet version of the NaN in a */ + signalling = !(a->sigh & 0x40000000); + } + } + else +#ifdef PARANOID + if (b->tag == TW_NaN) +#endif PARANOID + { + signalling = !(b->sigh & 0x40000000); + x = b; + } +#ifdef PARANOID + else + { + signalling = 0; + EXCEPTION(EX_INTERNAL|0x113); + x = &CONST_QNaN; + } +#endif PARANOID + + if ( !signalling ) + { + if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */ + x = &CONST_QNaN; + reg_move(x, dest); + return 0; + } + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */ + x = &CONST_QNaN; + reg_move(x, dest); + /* ensure a Quiet NaN */ + dest->sigh |= 0x40000000; + } + + EXCEPTION(EX_Invalid); + + return !(control_word & CW_Invalid); +} + + +/* Invalid arith operation on Valid registers */ +/* Returns nz if the exception is unmasked */ +asmlinkage int arith_invalid(FPU_REG *dest) +{ + + EXCEPTION(EX_Invalid); + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + reg_move(&CONST_QNaN, dest); + } + + return !(control_word & CW_Invalid); + +} + + +/* Divide a finite number by zero */ +asmlinkage int divide_by_zero(int sign, FPU_REG *dest) +{ + + if ( control_word & CW_ZeroDiv ) + { + /* The masked response */ + reg_move(&CONST_INF, dest); + dest->sign = (unsigned char)sign; + } + + EXCEPTION(EX_ZeroDiv); + + return !(control_word & CW_ZeroDiv); + +} + + +/* This may be called often, so keep it lean */ +int set_precision_flag(int flags) +{ + if ( control_word & CW_Precision ) + { + partial_status &= ~(SW_C1 & flags); + partial_status |= flags; /* The masked response */ + return 0; + } + else + { + exception(flags); + return 1; + } +} + + +/* This may be called often, so keep it lean */ +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); + +} + + +/* This may be called often, so keep it lean */ +asmlinkage void set_precision_flag_down(void) +{ + if ( control_word & CW_Precision ) + { /* The masked response */ + partial_status &= ~SW_C1; + partial_status |= SW_Precision; + } + else + exception(EX_Precision); +} + + +asmlinkage int denormal_operand(void) +{ + if ( control_word & CW_Denormal ) + { /* The masked response */ + partial_status |= SW_Denorm_Op; + return 0; + } + else + { + exception(EX_Denormal); + return 1; + } +} + + +asmlinkage int arith_overflow(FPU_REG *dest) +{ + + if ( control_word & CW_Overflow ) + { + char sign; + /* The masked response */ +/* ###### The response here depends upon the rounding mode */ + sign = dest->sign; + reg_move(&CONST_INF, dest); + dest->sign = sign; + } + else + { + /* Subtract the magic number from the exponent */ + dest->exp -= (3 * (1 << 13)); + } + + EXCEPTION(EX_Overflow); + if ( control_word & CW_Overflow ) + { + /* The overflow exception is masked. */ + /* By definition, precision is lost. + The roundup bit (C1) is also set because we have + "rounded" upwards to Infinity. */ + EXCEPTION(EX_Precision | SW_C1); + return !(control_word & CW_Precision); + } + + return !(control_word & CW_Overflow); + +} + + +asmlinkage int arith_underflow(FPU_REG *dest) +{ + + if ( control_word & CW_Underflow ) + { + /* The masked response */ + if ( dest->exp <= EXP_UNDER - 63 ) + { + reg_move(&CONST_Z, dest); + partial_status &= ~SW_C1; /* Round down. */ + } + } + else + { + /* Add the magic number to the exponent. */ + dest->exp += (3 * (1 << 13)); + } + + EXCEPTION(EX_Underflow); + if ( control_word & CW_Underflow ) + { + /* The underflow exception is masked. */ + EXCEPTION(EX_Precision); + return !(control_word & CW_Precision); + } + + return !(control_word & CW_Underflow); + +} + + +void stack_overflow(void) +{ + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + top--; + reg_move(&CONST_QNaN, &st(0)); + } + + EXCEPTION(EX_StackOver); + + return; + +} + + +void stack_underflow(void) +{ + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + reg_move(&CONST_QNaN, &st(0)); + } + + EXCEPTION(EX_StackUnder); + + return; + +} + + +void stack_underflow_i(int i) +{ + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + reg_move(&CONST_QNaN, &(st(i))); + } + + EXCEPTION(EX_StackUnder); + + return; + +} + + +void stack_underflow_pop(int i) +{ + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + reg_move(&CONST_QNaN, &(st(i))); + pop(); + } + + EXCEPTION(EX_StackUnder); + + return; + +} + diff --git a/arch/i386/math-emu/exception.h b/arch/i386/math-emu/exception.h new file mode 100644 index 000000000..2e629a30c --- /dev/null +++ b/arch/i386/math-emu/exception.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------------------------+ + | exception.h | + | | + | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + + +#ifdef __ASSEMBLER__ +#define Const_(x) $##x +#else +#define Const_(x) x +#endif + +#ifndef SW_C1 +#include "fpu_emu.h" +#endif SW_C1 + +#define FPU_BUSY Const_(0x8000) /* FPU busy bit (8087 compatibility) */ +#define EX_ErrorSummary Const_(0x0080) /* Error summary status */ +/* Special exceptions: */ +#define EX_INTERNAL Const_(0x8000) /* Internal error in wm-FPU-emu */ +#define EX_StackOver Const_(0x0041|SW_C1) /* stack overflow */ +#define EX_StackUnder Const_(0x0041) /* stack underflow */ +/* Exception flags: */ +#define EX_Precision Const_(0x0020) /* loss of precision */ +#define EX_Underflow Const_(0x0010) /* underflow */ +#define EX_Overflow Const_(0x0008) /* overflow */ +#define EX_ZeroDiv Const_(0x0004) /* divide by zero */ +#define EX_Denormal Const_(0x0002) /* denormalized operand */ +#define EX_Invalid Const_(0x0001) /* invalid operation */ + + +#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1)) +#define PRECISION_LOST_DOWN Const_(EX_Precision) + + +#ifndef __ASSEMBLER__ + +#ifdef DEBUG +#define EXCEPTION(x) { printk("exception in %s at line %d\n", \ + __FILE__, __LINE__); exception(x); } +#else +#define EXCEPTION(x) exception(x) +#endif + +#endif __ASSEMBLER__ + +#endif _EXCEPTION_H_ diff --git a/arch/i386/math-emu/fpu_arith.c b/arch/i386/math-emu/fpu_arith.c new file mode 100644 index 000000000..96e6bd89b --- /dev/null +++ b/arch/i386/math-emu/fpu_arith.c @@ -0,0 +1,179 @@ +/*---------------------------------------------------------------------------+ + | fpu_arith.c | + | | + | Code to implement the FPU register/register arithmetic instructions | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "status_w.h" + + +void fadd__() +{ + /* fadd st,st(i) */ + clear_C1(); + reg_add(&st(0), &st(FPU_rm), &st(0), control_word); +} + + +void fmul__() +{ + /* fmul st,st(i) */ + clear_C1(); + reg_mul(&st(0), &st(FPU_rm), &st(0), control_word); +} + + + +void fsub__() +{ + /* fsub st,st(i) */ + clear_C1(); + reg_sub(&st(0), &st(FPU_rm), &st(0), control_word); +} + + +void fsubr_() +{ + /* fsubr st,st(i) */ + clear_C1(); + reg_sub(&st(FPU_rm), &st(0), &st(0), control_word); +} + + +void fdiv__() +{ + /* fdiv st,st(i) */ + clear_C1(); + reg_div(&st(0), &st(FPU_rm), &st(0), control_word); +} + + +void fdivr_() +{ + /* fdivr st,st(i) */ + clear_C1(); + reg_div(&st(FPU_rm), &st(0), &st(0), control_word); +} + + + +void fadd_i() +{ + /* fadd st(i),st */ + clear_C1(); + reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void fmul_i() +{ + /* fmul st(i),st */ + clear_C1(); + reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void fsubri() +{ + /* fsubr st(i),st */ + /* This is the sense of the 80486 manual + reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */ + clear_C1(); + reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void fsub_i() +{ + /* fsub st(i),st */ + /* This is the sense of the 80486 manual + reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ + clear_C1(); + reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); +} + + +void fdivri() +{ + /* fdivr st(i),st */ + clear_C1(); + reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void fdiv_i() +{ + /* fdiv st(i),st */ + clear_C1(); + reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); +} + + + +void faddp_() +{ + /* faddp st(i),st */ + clear_C1(); + if ( !reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); +} + + +void fmulp_() +{ + /* fmulp st(i),st */ + clear_C1(); + if ( !reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); +} + + + +void fsubrp() +{ + /* fsubrp st(i),st */ + /* This is the sense of the 80486 manual + reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */ + clear_C1(); + if ( !reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); +} + + +void fsubp_() +{ + /* fsubp st(i),st */ + /* This is the sense of the 80486 manual + reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ + clear_C1(); + if ( !reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) + pop(); +} + + +void fdivrp() +{ + /* fdivrp st(i),st */ + clear_C1(); + if ( !reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); +} + + +void fdivp_() +{ + /* fdivp st(i),st */ + clear_C1(); + if ( !reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) + pop(); +} + diff --git a/arch/i386/math-emu/fpu_asm.h b/arch/i386/math-emu/fpu_asm.h new file mode 100644 index 000000000..8eb60148d --- /dev/null +++ b/arch/i386/math-emu/fpu_asm.h @@ -0,0 +1,30 @@ +/*---------------------------------------------------------------------------+ + | fpu_asm.h | + | | + | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _FPU_ASM_H_ +#define _FPU_ASM_H_ + +#include "fpu_emu.h" + +#define EXCEPTION _exception + + +#define PARAM1 8(%ebp) +#define PARAM2 12(%ebp) +#define PARAM3 16(%ebp) +#define PARAM4 20(%ebp) + +#define SIGL_OFFSET 8 +#define SIGN(x) (x) +#define TAG(x) 1(x) +#define EXP(x) 4(x) +#define SIG(x) SIGL_OFFSET##(x) +#define SIGL(x) SIGL_OFFSET##(x) +#define SIGH(x) 12(x) + +#endif _FPU_ASM_H_ diff --git a/arch/i386/math-emu/fpu_aux.c b/arch/i386/math-emu/fpu_aux.c new file mode 100644 index 000000000..0d35fe19b --- /dev/null +++ b/arch/i386/math-emu/fpu_aux.c @@ -0,0 +1,184 @@ +/*---------------------------------------------------------------------------+ + | fpu_aux.c | + | | + | Code to implement some of the FPU auxiliary instructions. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "control_w.h" + + +static void fnop(void) +{ +} + +void fclex(void) +{ + partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision| + SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op| + SW_Invalid); + no_ip_update = 1; +} + +/* Needs to be externally visible */ +void finit() +{ + int r; + control_word = 0x037f; + partial_status = 0; + top = 0; /* We don't keep top in the status word internally. */ + for (r = 0; r < 8; r++) + { + regs[r].tag = TW_Empty; + } + /* The behaviour is different to that detailed in + Section 15.1.6 of the Intel manual */ + operand_address.offset = 0; + operand_address.selector = 0; + instruction_address.offset = 0; + instruction_address.selector = 0; + instruction_address.opcode = 0; + no_ip_update = 1; +} + +/* + * These are nops on the i387.. + */ +#define feni fnop +#define fdisi fnop +#define fsetpm fnop + +static FUNC const finit_table[] = { + feni, fdisi, fclex, finit, + fsetpm, FPU_illegal, FPU_illegal, FPU_illegal +}; + +void finit_() +{ + (finit_table[FPU_rm])(); +} + + +static void fstsw_ax(void) +{ + *(short *) &FPU_EAX = status_word(); + no_ip_update = 1; +} + +static FUNC const fstsw_table[] = { + fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal, + FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal +}; + +void fstsw_() +{ + (fstsw_table[FPU_rm])(); +} + + +static FUNC const fp_nop_table[] = { + fnop, FPU_illegal, FPU_illegal, FPU_illegal, + FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal +}; + +void fp_nop() +{ + (fp_nop_table[FPU_rm])(); +} + + +void fld_i_() +{ + FPU_REG *st_new_ptr; + + if ( STACK_OVERFLOW ) + { stack_overflow(); return; } + + /* fld st(i) */ + if ( NOT_EMPTY(FPU_rm) ) + { reg_move(&st(FPU_rm), st_new_ptr); push(); } + else + { + if ( control_word & CW_Invalid ) + { + /* The masked response */ + stack_underflow(); + } + else + EXCEPTION(EX_StackUnder); + } + +} + + +void fxch_i() +{ + /* fxch st(i) */ + FPU_REG t; + register FPU_REG *sti_ptr = &st(FPU_rm), *st0_ptr = &st(0); + + if ( st0_ptr->tag == TW_Empty ) + { + if ( sti_ptr->tag == TW_Empty ) + { + stack_underflow(); + stack_underflow_i(FPU_rm); + return; + } + if ( control_word & CW_Invalid ) + reg_move(sti_ptr, st0_ptr); /* Masked response */ + stack_underflow_i(FPU_rm); + return; + } + if ( sti_ptr->tag == TW_Empty ) + { + if ( control_word & CW_Invalid ) + reg_move(st0_ptr, sti_ptr); /* Masked response */ + stack_underflow(); + return; + } + clear_C1(); + reg_move(st0_ptr, &t); + reg_move(sti_ptr, st0_ptr); + reg_move(&t, sti_ptr); +} + + +void ffree_() +{ + /* ffree st(i) */ + st(FPU_rm).tag = TW_Empty; +} + + +void ffreep() +{ + /* ffree st(i) + pop - unofficial code */ + st(FPU_rm).tag = TW_Empty; + pop(); +} + + +void fst_i_() +{ + /* fst st(i) */ + reg_move(&st(0), &st(FPU_rm)); +} + + +void fstp_i() +{ + /* fstp st(i) */ + reg_move(&st(0), &st(FPU_rm)); + pop(); +} + diff --git a/arch/i386/math-emu/fpu_emu.h b/arch/i386/math-emu/fpu_emu.h new file mode 100644 index 000000000..9d2c5dd13 --- /dev/null +++ b/arch/i386/math-emu/fpu_emu.h @@ -0,0 +1,171 @@ +/*---------------------------------------------------------------------------+ + | fpu_emu.h | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + + +#ifndef _FPU_EMU_H_ +#define _FPU_EMU_H_ + +/* + * Define DENORM_OPERAND to make the emulator detect denormals + * and use the denormal flag of the status word. Note: this only + * affects the flag and corresponding interrupt, the emulator + * will always generate denormals and operate upon them as required. + */ +#define DENORM_OPERAND + +/* + * Define PECULIAR_486 to get a closer approximation to 80486 behaviour, + * rather than behaviour which appears to be cleaner. + * This is a matter of opinion: for all I know, the 80486 may simply + * be complying with the IEEE spec. Maybe one day I'll get to see the + * spec... + */ +#define PECULIAR_486 + +#ifdef __ASSEMBLER__ +#include "fpu_asm.h" +#define Const(x) $##x +#else +#define Const(x) x +#endif + +#define EXP_BIAS Const(0) +#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */ +#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */ +#define EXP_Infinity EXP_OVER +#define EXP_NaN EXP_OVER + +#define SIGN_POS Const(0) +#define SIGN_NEG Const(1) + +/* Keep the order TW_Valid, TW_Zero, TW_Denormal */ +#define TW_Valid Const(0) /* valid */ +#define TW_Zero Const(1) /* zero */ +/* The following fold to 2 (Special) in the Tag Word */ +/* #define TW_Denormal Const(4) */ /* De-normal */ +#define TW_Infinity Const(5) /* + or - infinity */ +#define TW_NaN Const(6) /* Not a Number */ + +#define TW_Empty Const(7) /* empty */ + + +#ifndef __ASSEMBLER__ + +#include <linux/math_emu.h> +#include <linux/linkage.h> + +/* +#define RE_ENTRANT_CHECKING + */ + +#ifdef RE_ENTRANT_CHECKING +extern char emulating; +# define RE_ENTRANT_CHECK_OFF emulating = 0 +# define RE_ENTRANT_CHECK_ON emulating = 1 +#else +# define RE_ENTRANT_CHECK_OFF +# define RE_ENTRANT_CHECK_ON +#endif RE_ENTRANT_CHECKING + +#define FWAIT_OPCODE 0x9b +#define OP_SIZE_PREFIX 0x66 +#define ADDR_SIZE_PREFIX 0x67 +#define PREFIX_CS 0x2e +#define PREFIX_DS 0x3e +#define PREFIX_ES 0x26 +#define PREFIX_SS 0x36 +#define PREFIX_FS 0x64 +#define PREFIX_GS 0x65 +#define PREFIX_REPE 0xf3 +#define PREFIX_REPNE 0xf2 +#define PREFIX_LOCK 0xf0 +#define PREFIX_CS_ 1 +#define PREFIX_DS_ 2 +#define PREFIX_ES_ 3 +#define PREFIX_FS_ 4 +#define PREFIX_GS_ 5 +#define PREFIX_SS_ 6 +#define PREFIX_DEFAULT 7 + +struct address { + unsigned int offset; + unsigned int selector:16; + unsigned int opcode:11; + unsigned int empty:5; +}; +typedef void (*FUNC)(void); +typedef struct fpu_reg FPU_REG; +typedef void (*FUNC_ST0)(FPU_REG *st0_ptr); +typedef struct { unsigned char address_size, operand_size, segment; } + overrides; +/* This structure is 32 bits: */ +typedef struct { overrides override; + unsigned char default_mode; } fpu_addr_modes; +/* PROTECTED has a restricted meaning in the emulator; it is used + to signal that the emulator needs to do special things to ensure + that protection is respected in a segmented model. */ +#define PROTECTED 4 +#define SIXTEEN 1 /* We rely upon this being 1 (true) */ +#define VM86 SIXTEEN +#define PM16 (SIXTEEN | PROTECTED) +#define SEG32 PROTECTED +extern unsigned char const data_sizes_16[32]; + +#define st(x) ( regs[((top+x) &7 )] ) + +#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty) +#define NOT_EMPTY(i) (st(i).tag != TW_Empty) +#define NOT_EMPTY_ST0 (st0_tag ^ TW_Empty) + +#define pop() { regs[(top++ & 7 )].tag = TW_Empty; } +#define poppop() { regs[((top + 1) & 7 )].tag \ + = regs[(top & 7 )].tag = TW_Empty; \ + top += 2; } + +/* push() does not affect the tags */ +#define push() { top--; } + + +#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); } + +#define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] ) + + +/*----- Prototypes for functions written in assembler -----*/ +/* extern void reg_move(FPU_REG *a, FPU_REG *b); */ + +asmlinkage void normalize(FPU_REG *x); +asmlinkage void normalize_nuo(FPU_REG *x); +asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w); +asmlinkage unsigned shrx(void *l, unsigned x); +asmlinkage unsigned shrxs(void *v, unsigned x); +asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y); +asmlinkage void round_reg(FPU_REG *arg, unsigned int extent, + unsigned int control_w); + +#ifndef MAKING_PROTO +#include "fpu_proto.h" +#endif + +#endif __ASSEMBLER__ + +#endif _FPU_EMU_H_ diff --git a/arch/i386/math-emu/fpu_entry.c b/arch/i386/math-emu/fpu_entry.c new file mode 100644 index 000000000..b2777a722 --- /dev/null +++ b/arch/i386/math-emu/fpu_entry.c @@ -0,0 +1,690 @@ +/*---------------------------------------------------------------------------+ + | fpu_entry.c | + | | + | The entry function 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 | + | | + | See the files "README" and "COPYING" for further copyright and warranty | + | information. | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | math_emulate() is the sole entry point for wm-FPU-emu | + +---------------------------------------------------------------------------*/ + +#include <linux/signal.h> + +#include <asm/segment.h> + +#include "fpu_system.h" +#include "fpu_emu.h" +#include "exception.h" +#include "control_w.h" +#include "status_w.h" + +#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */ + +#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by default. */ + +/* WARNING: These codes are not documented by Intel in their 80486 manual + and may not work on FPU clones or later Intel FPUs. */ + +/* Changes to support the un-doc codes provided by Linus Torvalds. */ + +#define _d9_d8_ fstp_i /* unofficial code (19) */ +#define _dc_d0_ fcom_st /* unofficial code (14) */ +#define _dc_d8_ fcompst /* unofficial code (1c) */ +#define _dd_c8_ fxch_i /* unofficial code (0d) */ +#define _de_d0_ fcompst /* unofficial code (16) */ +#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */ +#define _df_c8_ fxch_i /* unofficial code (0f) */ +#define _df_d0_ fstp_i /* unofficial code (17) */ +#define _df_d8_ fstp_i /* unofficial code (1f) */ + +static FUNC const st_instr_table[64] = { + fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_, + fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_, + fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_, + fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_, + fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, + fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, + fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, + fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, +}; + +#else /* Support only documented FPU op-codes */ + +static FUNC const st_instr_table[64] = { + fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__, + fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__, + fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__, + fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__, + fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, + fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, + fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, + fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, +}; + +#endif NO_UNDOC_CODE + + +#define _NONE_ 0 /* Take no special action */ +#define _REG0_ 1 /* Need to check for not empty st(0) */ +#define _REGI_ 2 /* Need to check for not empty st(0) and st(rm) */ +#define _REGi_ 0 /* Uses st(rm) */ +#define _PUSH_ 3 /* Need to check for space to push onto stack */ +#define _null_ 4 /* Function illegal or not implemented */ +#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */ +#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */ +#define _REGIc 0 /* Compare st(0) and st(rm) */ +#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */ + +#ifndef NO_UNDOC_CODE + +/* Un-documented FPU op-codes supported by default. (see above) */ + +static unsigned char const type_table[64] = { + _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, + _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, + _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, + _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, + _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, + _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_, + _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_, + _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_ +}; + +#else /* Support only documented FPU op-codes */ + +static unsigned char const type_table[64] = { + _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_, + _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_, + _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_, + _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_, + _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, + _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_, + _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_, + _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_ +}; + +#endif NO_UNDOC_CODE + + +#ifdef RE_ENTRANT_CHECKING +char emulating=0; +#endif RE_ENTRANT_CHECKING + +static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, + overrides *override); + +asmlinkage void math_emulate(long arg) +{ + unsigned char FPU_modrm, byte1; + unsigned short code; + fpu_addr_modes addr_modes; + int unmasked; + FPU_REG loaded_data; + void *data_address; + struct address data_sel_off; + struct address entry_sel_off; + unsigned long code_base = 0; + unsigned long code_limit = 0; /* Initialized to stop compiler warnings */ + char st0_tag; + FPU_REG *st0_ptr; + struct desc_struct code_descriptor; + +#ifdef RE_ENTRANT_CHECKING + if ( emulating ) + { + printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n"); + } + RE_ENTRANT_CHECK_ON; +#endif RE_ENTRANT_CHECKING + + if (!current->used_math) + { + int i; + for ( i = 0; i < 8; i++ ) + { + /* Make sure that the registers are compatible + with the assumptions of the emulator. */ + regs[i].exp = 0; + regs[i].sigh = 0x80000000; + } + finit(); + current->used_math = 1; + } + + SETUP_DATA_AREA(arg); + + FPU_ORIG_EIP = FPU_EIP; + + if ( (FPU_EFLAGS & 0x00020000) != 0 ) + { + /* Virtual 8086 mode */ + addr_modes.default_mode = VM86; + FPU_EIP += code_base = FPU_CS << 4; + code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */ + } + else if ( FPU_CS == USER_CS && FPU_DS == USER_DS ) + { + addr_modes.default_mode = 0; + } + else if ( FPU_CS == KERNEL_CS ) + { + printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP); + panic("Math emulation needed in kernel"); + } + else + { + + if ( (FPU_CS & 4) != 4 ) /* Must be in the LDT */ + { + /* Can only handle segmented addressing via the LDT + for now, and it must be 16 bit */ + printk("FPU emulator: Unsupported addressing mode\n"); + math_abort(FPU_info, SIGILL); + } + + if ( SEG_D_SIZE(code_descriptor = LDT_DESCRIPTOR(FPU_CS)) ) + { + /* The above test may be wrong, the book is not clear */ + /* Segmented 32 bit protected mode */ + addr_modes.default_mode = SEG32; + } + else + { + /* 16 bit protected mode */ + addr_modes.default_mode = PM16; + } + FPU_EIP += code_base = SEG_BASE_ADDR(code_descriptor); + code_limit = code_base + + (SEG_LIMIT(code_descriptor)+1) * SEG_GRANULARITY(code_descriptor) + - 1; + if ( code_limit < code_base ) code_limit = 0xffffffff; + } + + FPU_lookahead = 1; + if (current->flags & PF_PTRACED) + FPU_lookahead = 0; + + if ( !valid_prefix(&byte1, (unsigned char **)&FPU_EIP, + &addr_modes.override) ) + { + RE_ENTRANT_CHECK_OFF; + printk("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n" + "FPU emulator: self-modifying code! (emulation impossible)\n", + byte1); + RE_ENTRANT_CHECK_ON; + EXCEPTION(EX_INTERNAL|0x126); + math_abort(FPU_info,SIGILL); + } + +do_another_FPU_instruction: + + no_ip_update = 0; + + FPU_EIP++; /* We have fetched the prefix and first code bytes. */ + + if ( addr_modes.default_mode ) + { + /* This checks for the minimum instruction bytes. + We also need to check any extra (address mode) code access. */ + if ( FPU_EIP > code_limit ) + math_abort(FPU_info,SIGSEGV); + } + + if ( (byte1 & 0xf8) != 0xd8 ) + { + if ( byte1 == FWAIT_OPCODE ) + { + if (partial_status & SW_Summary) + goto do_the_FPU_interrupt; + else + goto FPU_fwait_done; + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL|0x128); + math_abort(FPU_info,SIGILL); +#endif PARANOID + } + + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + FPU_modrm = get_fs_byte((unsigned char *) FPU_EIP); + RE_ENTRANT_CHECK_ON; + FPU_EIP++; + + if (partial_status & SW_Summary) + { + /* Ignore the error for now if the current instruction is a no-wait + control instruction */ + /* The 80486 manual contradicts itself on this topic, + but a real 80486 uses the following instructions: + fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex. + */ + code = (FPU_modrm << 8) | byte1; + if ( ! ( (((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */ + (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv, + fnstsw */ + ((code & 0xc000) != 0xc000))) ) ) + { + /* + * 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; + current->tss.trap_no = 16; + current->tss.error_code = 0; + send_sig(SIGFPE, current, 1); + return; + } + } + + entry_sel_off.offset = FPU_ORIG_EIP; + entry_sel_off.selector = FPU_CS; + entry_sel_off.opcode = (byte1 << 8) | FPU_modrm; + + FPU_rm = FPU_modrm & 7; + + if ( FPU_modrm < 0300 ) + { + /* All of these instructions use the mod/rm byte to get a data address */ + + if ( (addr_modes.default_mode & SIXTEEN) + ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) ) + data_address = get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off, + addr_modes); + else + data_address = get_address(FPU_modrm, &FPU_EIP, &data_sel_off, + addr_modes); + + if ( addr_modes.default_mode ) + { + if ( FPU_EIP-1 > code_limit ) + math_abort(FPU_info,SIGSEGV); + } + + if ( !(byte1 & 1) ) + { + unsigned short status1 = partial_status; + + st0_ptr = &st(0); + st0_tag = st0_ptr->tag; + + /* Stack underflow has priority */ + if ( NOT_EMPTY_ST0 ) + { + if ( addr_modes.default_mode & PROTECTED ) + { + /* This table works for 16 and 32 bit protected mode */ + if ( access_limit < data_sizes_16[(byte1 >> 1) & 3] ) + math_abort(FPU_info,SIGSEGV); + } + + unmasked = 0; /* Do this here to stop compiler warnings. */ + switch ( (byte1 >> 1) & 3 ) + { + case 0: + unmasked = reg_load_single((float *)data_address, + &loaded_data); + break; + case 1: + reg_load_int32((long *)data_address, &loaded_data); + break; + case 2: + unmasked = reg_load_double((double *)data_address, + &loaded_data); + break; + case 3: + reg_load_int16((short *)data_address, &loaded_data); + break; + } + + /* No more access to user memory, it is safe + to use static data now */ + + /* NaN operands have the next priority. */ + /* We have to delay looking at st(0) until after + loading the data, because that data might contain an SNaN */ + if ( (st0_tag == TW_NaN) || + (loaded_data.tag == TW_NaN) ) + { + /* Restore the status word; we might have loaded a + denormal. */ + partial_status = status1; + if ( (FPU_modrm & 0x30) == 0x10 ) + { + /* fcom or fcomp */ + EXCEPTION(EX_Invalid); + setcc(SW_C3 | SW_C2 | SW_C0); + if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) + pop(); /* fcomp, masked, so we pop. */ + } + else + { +#ifdef PECULIAR_486 + /* This is not really needed, but gives behaviour + identical to an 80486 */ + if ( (FPU_modrm & 0x28) == 0x20 ) + /* fdiv or fsub */ + real_2op_NaN(&loaded_data, st0_ptr, + st0_ptr); + else +#endif PECULIAR_486 + /* fadd, fdivr, fmul, or fsubr */ + real_2op_NaN(st0_ptr, &loaded_data, + st0_ptr); + } + goto reg_mem_instr_done; + } + + if ( unmasked && !((FPU_modrm & 0x30) == 0x10) ) + { + /* Is not a comparison instruction. */ + if ( (FPU_modrm & 0x38) == 0x38 ) + { + /* fdivr */ + if ( (st0_tag == TW_Zero) && + (loaded_data.tag == TW_Valid) ) + { + if ( divide_by_zero(loaded_data.sign, + st0_ptr) ) + { + /* We use the fact here that the unmasked + exception in the loaded data was for a + denormal operand */ + /* Restore the state of the denormal op bit */ + partial_status &= ~SW_Denorm_Op; + partial_status |= status1 & SW_Denorm_Op; + } + } + } + goto reg_mem_instr_done; + } + + switch ( (FPU_modrm >> 3) & 7 ) + { + case 0: /* fadd */ + clear_C1(); + reg_add(st0_ptr, &loaded_data, st0_ptr, + control_word); + break; + case 1: /* fmul */ + clear_C1(); + reg_mul(st0_ptr, &loaded_data, st0_ptr, + control_word); + break; + case 2: /* fcom */ + compare_st_data(&loaded_data); + break; + case 3: /* fcomp */ + if ( !compare_st_data(&loaded_data) && !unmasked ) + pop(); + break; + case 4: /* fsub */ + clear_C1(); + reg_sub(st0_ptr, &loaded_data, st0_ptr, + control_word); + break; + case 5: /* fsubr */ + clear_C1(); + reg_sub(&loaded_data, st0_ptr, st0_ptr, + control_word); + break; + case 6: /* fdiv */ + clear_C1(); + reg_div(st0_ptr, &loaded_data, st0_ptr, + control_word); + break; + case 7: /* fdivr */ + clear_C1(); + if ( st0_tag == TW_Zero ) + partial_status = status1; /* Undo any denorm tag, + zero-divide has priority. */ + reg_div(&loaded_data, st0_ptr, st0_ptr, + control_word); + break; + } + } + else + { + if ( (FPU_modrm & 0x30) == 0x10 ) + { + /* The instruction is fcom or fcomp */ + EXCEPTION(EX_StackUnder); + setcc(SW_C3 | SW_C2 | SW_C0); + if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) + pop(); /* fcomp */ + } + else + stack_underflow(); + } + reg_mem_instr_done: + operand_address = data_sel_off; + } + else + { + if ( !(no_ip_update = + load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, + addr_modes, data_address)) ) + { + operand_address = data_sel_off; + } + } + + } + else + { + /* None of these instructions access user memory */ + unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7); + +#ifdef PECULIAR_486 + /* This is supposed to be undefined, but a real 80486 seems + to do this: */ + operand_address.offset = 0; + operand_address.selector = FPU_DS; +#endif PECULIAR_486 + + st0_ptr = &st(0); + st0_tag = st0_ptr->tag; + switch ( type_table[(int) instr_index] ) + { + case _NONE_: /* also _REGIc: _REGIn */ + break; + case _REG0_: + if ( !NOT_EMPTY_ST0 ) + { + stack_underflow(); + goto FPU_instruction_done; + } + break; + case _REGIi: + if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) + { + stack_underflow_i(FPU_rm); + goto FPU_instruction_done; + } + break; + case _REGIp: + if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) + { + stack_underflow_pop(FPU_rm); + goto FPU_instruction_done; + } + break; + case _REGI_: + if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) + { + stack_underflow(); + goto FPU_instruction_done; + } + break; + case _PUSH_: /* Only used by the fld st(i) instruction */ + break; + case _null_: + FPU_illegal(); + goto FPU_instruction_done; + default: + EXCEPTION(EX_INTERNAL|0x111); + goto FPU_instruction_done; + } + (*st_instr_table[(int) instr_index])(); + +FPU_instruction_done: + ; + } + + if ( ! no_ip_update ) + instruction_address = entry_sel_off; + +FPU_fwait_done: + +#ifdef DEBUG + RE_ENTRANT_CHECK_OFF; + emu_printall(); + RE_ENTRANT_CHECK_ON; +#endif DEBUG + + if (FPU_lookahead && !need_resched) + { + FPU_ORIG_EIP = FPU_EIP - code_base; + if ( valid_prefix(&byte1, (unsigned char **)&FPU_EIP, + &addr_modes.override) ) + goto do_another_FPU_instruction; + } + + if ( addr_modes.default_mode ) + FPU_EIP -= code_base; + + RE_ENTRANT_CHECK_OFF; +} + + +/* Support for prefix bytes is not yet complete. To properly handle + all prefix bytes, further changes are needed in the emulator code + which accesses user address space. Access to separate segments is + important for msdos emulation. */ +static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, + overrides *override) +{ + unsigned char byte; + unsigned char *ip = *fpu_eip; + + *override = (overrides) { 0, 0, PREFIX_DEFAULT }; /* defaults */ + + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + byte = get_fs_byte(ip); + RE_ENTRANT_CHECK_ON; + + while ( 1 ) + { + switch ( byte ) + { + case ADDR_SIZE_PREFIX: + override->address_size = ADDR_SIZE_PREFIX; + goto do_next_byte; + + case OP_SIZE_PREFIX: + override->operand_size = OP_SIZE_PREFIX; + goto do_next_byte; + + case PREFIX_CS: + override->segment = PREFIX_CS_; + goto do_next_byte; + case PREFIX_ES: + override->segment = PREFIX_ES_; + goto do_next_byte; + case PREFIX_SS: + override->segment = PREFIX_SS_; + goto do_next_byte; + case PREFIX_FS: + override->segment = PREFIX_FS_; + goto do_next_byte; + case PREFIX_GS: + override->segment = PREFIX_GS_; + goto do_next_byte; + case PREFIX_DS: + override->segment = PREFIX_DS_; + goto do_next_byte; + +/* lock is not a valid prefix for FPU instructions, + let the cpu handle it to generate a SIGILL. */ +/* case PREFIX_LOCK: */ + + /* rep.. prefixes have no meaning for FPU instructions */ + case PREFIX_REPE: + case PREFIX_REPNE: + + do_next_byte: + ip++; + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + byte = get_fs_byte(ip); + RE_ENTRANT_CHECK_ON; + break; + case FWAIT_OPCODE: + *Byte = byte; + return 1; + default: + if ( (byte & 0xf8) == 0xd8 ) + { + *Byte = byte; + *fpu_eip = ip; + return 1; + } + else + { + /* Not a valid sequence of prefix bytes followed by + an FPU instruction. */ + *Byte = byte; /* Needed for error message. */ + return 0; + } + } + } +} + + +void math_abort(struct info * info, unsigned int signal) +{ + FPU_EIP = FPU_ORIG_EIP; + current->tss.trap_no = 16; + current->tss.error_code = 0; + send_sig(signal,current,1); + RE_ENTRANT_CHECK_OFF; + __asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4)); +#ifdef PARANOID + printk("ERROR: wm-FPU-emu math_abort failed!\n"); +#endif PARANOID +} diff --git a/arch/i386/math-emu/fpu_etc.c b/arch/i386/math-emu/fpu_etc.c new file mode 100644 index 000000000..20e3294ca --- /dev/null +++ b/arch/i386/math-emu/fpu_etc.c @@ -0,0 +1,129 @@ +/*---------------------------------------------------------------------------+ + | fpu_etc.c | + | | + | Implement a few FPU instructions. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "reg_constant.h" + + +static void fchs(FPU_REG *st0_ptr) +{ + if ( st0_ptr->tag ^ TW_Empty ) + { + st0_ptr->sign ^= SIGN_POS^SIGN_NEG; + clear_C1(); + } + else + stack_underflow(); +} + +static void fabs(FPU_REG *st0_ptr) +{ + if ( st0_ptr->tag ^ TW_Empty ) + { + st0_ptr->sign = SIGN_POS; + clear_C1(); + } + else + stack_underflow(); +} + + +static void ftst_(FPU_REG *st0_ptr) +{ + switch (st0_ptr->tag) + { + case TW_Zero: + setcc(SW_C3); + break; + case TW_Valid: + if (st0_ptr->sign == SIGN_POS) + setcc(0); + else + setcc(SW_C0); + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + { +#ifdef PECULIAR_486 + /* This is weird! */ + if (st0_ptr->sign == SIGN_POS) + setcc(SW_C3); +#endif PECULIAR_486 + return; + } +#endif DENORM_OPERAND + + break; + case TW_NaN: + setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ + EXCEPTION(EX_Invalid); + break; + case TW_Infinity: + if (st0_ptr->sign == SIGN_POS) + setcc(0); + else + setcc(SW_C0); + break; + case TW_Empty: + setcc(SW_C0|SW_C2|SW_C3); + EXCEPTION(EX_StackUnder); + break; + default: + setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ + EXCEPTION(EX_INTERNAL|0x14); + break; + } +} + +static void fxam(FPU_REG *st0_ptr) +{ + int c=0; + switch (st0_ptr->tag) + { + case TW_Empty: + c = SW_C3|SW_C0; + break; + case TW_Zero: + c = SW_C3; + break; + case TW_Valid: + /* This will need to be changed if TW_Denormal is ever used. */ + if ( st0_ptr->exp <= EXP_UNDER ) + c = SW_C2|SW_C3; /* Denormal */ + else + c = SW_C2; + break; + case TW_NaN: + c = SW_C0; + break; + case TW_Infinity: + c = SW_C2|SW_C0; + break; + } + if (st0_ptr->sign == SIGN_NEG) + c |= SW_C1; + setcc(c); +} + + +static FUNC_ST0 const fp_etc_table[] = { + fchs, fabs, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal, + ftst_, fxam, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal +}; + +void fp_etc() +{ + (fp_etc_table[FPU_rm])(&st(0)); +} diff --git a/arch/i386/math-emu/fpu_proto.h b/arch/i386/math-emu/fpu_proto.h new file mode 100644 index 000000000..b4392fe57 --- /dev/null +++ b/arch/i386/math-emu/fpu_proto.h @@ -0,0 +1,137 @@ +/* errors.c */ +extern void Un_impl(void); +extern void FPU_illegal(void); +extern void emu_printall(void); +extern void stack_overflow(void); +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 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); +asmlinkage void set_precision_flag_up(void); +asmlinkage void set_precision_flag_down(void); +asmlinkage int denormal_operand(void); +asmlinkage int arith_overflow(FPU_REG *dest); +asmlinkage int arith_underflow(FPU_REG *dest); + +/* fpu_arith.c */ +extern void fadd__(void); +extern void fmul__(void); +extern void fsub__(void); +extern void fsubr_(void); +extern void fdiv__(void); +extern void fdivr_(void); +extern void fadd_i(void); +extern void fmul_i(void); +extern void fsubri(void); +extern void fsub_i(void); +extern void fdivri(void); +extern void fdiv_i(void); +extern void faddp_(void); +extern void fmulp_(void); +extern void fsubrp(void); +extern void fsubp_(void); +extern void fdivrp(void); +extern void fdivp_(void); + +/* fpu_aux.c */ +extern void fclex(void); +extern void finit(void); +extern void finit_(void); +extern void fstsw_(void); +extern void fp_nop(void); +extern void fld_i_(void); +extern void fxch_i(void); +extern void ffree_(void); +extern void ffreep(void); +extern void fst_i_(void); +extern void fstp_i(void); + +/* fpu_entry.c */ +asmlinkage void math_emulate(long arg); +extern void math_abort(struct info *info, unsigned int signal); + +/* fpu_etc.c */ +extern void fp_etc(void); + +/* fpu_trig.c */ +extern void convert_l2reg(long const *arg, FPU_REG *dest); +extern void trig_a(void); +extern void trig_b(void); + +/* get_address.c */ +extern void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, + fpu_addr_modes); +extern void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, + fpu_addr_modes); + +/* load_store.c */ +extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, + void *address); + +/* poly_2xm1.c */ +extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result); + +/* poly_atan.c */ +extern void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result); + +/* poly_l2.c */ +extern void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result); +extern int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result); + +/* poly_sin.c */ +extern void poly_sine(FPU_REG const *arg, FPU_REG *result); +extern void poly_cos(FPU_REG const *arg, FPU_REG *result); + +/* poly_tan.c */ +extern void poly_tan(FPU_REG const *arg, FPU_REG *result); + +/* reg_add_sub.c */ +extern int reg_add(FPU_REG const *a, FPU_REG const *b, + FPU_REG *dest, int control_w); +extern int reg_sub(FPU_REG const *a, FPU_REG const *b, + FPU_REG *dest, int control_w); + +/* reg_compare.c */ +extern int compare(FPU_REG const *b); +extern int compare_st_data(FPU_REG const *b); +extern void fcom_st(void); +extern void fcompst(void); +extern void fcompp(void); +extern void fucom_(void); +extern void fucomp(void); +extern void fucompp(void); + +/* reg_constant.c */ +extern void fconst(void); + +/* reg_ld_str.c */ +extern int reg_load_extended(long double *addr, FPU_REG *loaded_data); +extern int reg_load_double(double *dfloat, FPU_REG *loaded_data); +extern int reg_load_single(float *single, FPU_REG *loaded_data); +extern void reg_load_int64(long long *_s, FPU_REG *loaded_data); +extern void reg_load_int32(long *_s, FPU_REG *loaded_data); +extern void reg_load_int16(short *_s, FPU_REG *loaded_data); +extern void reg_load_bcd(char *s, FPU_REG *loaded_data); +extern int reg_store_extended(long double *d, FPU_REG *st0_ptr); +extern int reg_store_double(double *dfloat, FPU_REG *st0_ptr); +extern int reg_store_single(float *single, FPU_REG *st0_ptr); +extern int reg_store_int64(long long *d, FPU_REG *st0_ptr); +extern int reg_store_int32(long *d, FPU_REG *st0_ptr); +extern int reg_store_int16(short *d, FPU_REG *st0_ptr); +extern int reg_store_bcd(char *d, FPU_REG *st0_ptr); +extern int round_to_int(FPU_REG *r); +extern char *fldenv(fpu_addr_modes addr_modes, char *address); +extern void frstor(fpu_addr_modes addr_modes, char *address); +extern unsigned short tag_word(void); +extern char *fstenv(fpu_addr_modes addr_modes, char *address); +extern void fsave(fpu_addr_modes addr_modes, char *address); + +/* reg_mul.c */ +extern int reg_mul(FPU_REG const *a, FPU_REG const *b, + FPU_REG *dest, unsigned int control_w); diff --git a/arch/i386/math-emu/fpu_system.h b/arch/i386/math-emu/fpu_system.h new file mode 100644 index 000000000..d2c3fa716 --- /dev/null +++ b/arch/i386/math-emu/fpu_system.h @@ -0,0 +1,83 @@ +/*---------------------------------------------------------------------------+ + | fpu_system.h | + | | + | Copyright (C) 1992,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _FPU_SYSTEM_H +#define _FPU_SYSTEM_H + +/* system dependent definitions */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> + +/* This sets the pointer FPU_info to point to the argument part + of the stack frame of math_emulate() */ +#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg + +#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3]) +#define SEG_D_SIZE(x) ((x).b & (3 << 21)) +#define SEG_G_BIT(x) ((x).b & (1 << 23)) +#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) +#define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23))) +#define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \ + | (((s).b & 0xff) << 16) | ((s).a >> 16)) +#define SEG_LIMIT(s) (((s).b & 0xff0000) | ((s).a & 0xffff)) +#define SEG_EXECUTE_ONLY(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11)) +#define SEG_WRITE_PERM(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9)) +#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \ + == (1 << 10)) + +#define I387 (current->tss.i387) +#define FPU_info (I387.soft.info) + +#define FPU_CS (*(unsigned short *) &(FPU_info->___cs)) +#define FPU_SS (*(unsigned short *) &(FPU_info->___ss)) +#define FPU_DS (*(unsigned short *) &(FPU_info->___ds)) +#define FPU_EAX (FPU_info->___eax) +#define FPU_EFLAGS (FPU_info->___eflags) +#define FPU_EIP (FPU_info->___eip) +#define FPU_ORIG_EIP (FPU_info->___orig_eip) + +#define FPU_lookahead (I387.soft.lookahead) + +/* nz if ip_offset and cs_selector are not to be set for the current + instruction. */ +#define no_ip_update (((char *)&(I387.soft.twd))[0]) +#define FPU_rm (((unsigned char *)&(I387.soft.twd))[1]) + +/* Number of bytes of data which can be legally accessed by the current + instruction. This only needs to hold a number <= 108, so a byte will do. */ +#define access_limit (((unsigned char *)&(I387.soft.twd))[2]) + +#define partial_status (I387.soft.swd) +#define control_word (I387.soft.cwd) +#define regs (I387.soft.regs) +#define top (I387.soft.top) + +#define instruction_address (*(struct address *)&I387.soft.fip) +#define operand_address (*(struct address *)&I387.soft.foo) + +#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \ + math_abort(FPU_info,SIGSEGV) + +#undef FPU_IGNORE_CODE_SEGV +#ifdef FPU_IGNORE_CODE_SEGV +/* verify_area() is very expensive, and causes the emulator to run + about 20% slower if applied to the code. Anyway, errors due to bad + code addresses should be much rarer than errors due to bad data + addresses. */ +#define FPU_code_verify_area(z) +#else +/* A simpler test than verify_area() can probably be done for + FPU_code_verify_area() because the only possible error is to step + past the upper boundary of a legal code area. */ +#define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z) +#endif + +#endif diff --git a/arch/i386/math-emu/fpu_trig.c b/arch/i386/math-emu/fpu_trig.c new file mode 100644 index 000000000..05241f700 --- /dev/null +++ b/arch/i386/math-emu/fpu_trig.c @@ -0,0 +1,1718 @@ +/*---------------------------------------------------------------------------+ + | fpu_trig.c | + | | + | Implementation of the FPU "transcendental" functions. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "control_w.h" +#include "reg_constant.h" + + +static void rem_kernel(unsigned long long st0, unsigned long long *y, + unsigned long long st1, + unsigned long long q, int n); + +#define BETTER_THAN_486 + +#define FCOS 4 +/* Not needed now with new code +#define FPTAN 1 + */ + +/* Used only by fptan, fsin, fcos, and fsincos. */ +/* This routine produces very accurate results, similar to + using a value of pi with more than 128 bits precision. */ +/* Limited measurements show no results worse than 64 bit precision + except for the results for arguments close to 2^63, where the + precision of the result sometimes degrades to about 63.9 bits */ +static int trig_arg(FPU_REG *X, int even) +{ + FPU_REG tmp; + unsigned long long q; + int old_cw = control_word, saved_status = partial_status; + + if ( X->exp >= EXP_BIAS + 63 ) + { + partial_status |= SW_C2; /* Reduction incomplete. */ + return -1; + } + + control_word &= ~CW_RC; + control_word |= RC_CHOP; + + reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + round_to_int(&tmp); /* Fortunately, this can't overflow + to 2^64 */ + q = significand(&tmp); + if ( q ) + { + rem_kernel(significand(X), + &significand(&tmp), + significand(&CONST_PI2), + q, X->exp - CONST_PI2.exp); + tmp.exp = CONST_PI2.exp; + normalize(&tmp); + reg_move(&tmp, X); + } + +#ifdef FPTAN + if ( even == FPTAN ) + { + if ( ((X->exp >= EXP_BIAS) || + ((X->exp == EXP_BIAS-1) + && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) ) + even = FCOS; + else + even = 0; + } +#endif FPTAN + + if ( (even && !(q & 1)) || (!even && (q & 1)) ) + { + reg_sub(&CONST_PI2, X, X, FULL_PRECISION); +#ifdef BETTER_THAN_486 + /* So far, the results are exact but based upon a 64 bit + precision approximation to pi/2. The technique used + now is equivalent to using an approximation to pi/2 which + is accurate to about 128 bits. */ + if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) ) + { + /* This code gives the effect of having p/2 to better than + 128 bits precision. */ + significand(&tmp) = q + 1; + tmp.exp = EXP_BIAS + 63; + tmp.tag = TW_Valid; + normalize(&tmp); + reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); + reg_add(X, &tmp, X, FULL_PRECISION); + if ( X->sign == SIGN_NEG ) + { + /* CONST_PI2extra is negative, so the result of the addition + can be negative. This means that the argument is actually + in a different quadrant. The correction is always < pi/2, + so it can't overflow into yet another quadrant. */ + X->sign = SIGN_POS; + q++; + } + } +#endif BETTER_THAN_486 + } +#ifdef BETTER_THAN_486 + else + { + /* So far, the results are exact but based upon a 64 bit + precision approximation to pi/2. The technique used + now is equivalent to using an approximation to pi/2 which + is accurate to about 128 bits. */ + if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) ) + { + /* This code gives the effect of having p/2 to better than + 128 bits precision. */ + significand(&tmp) = q; + tmp.exp = EXP_BIAS + 63; + tmp.tag = TW_Valid; + normalize(&tmp); + reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); + reg_sub(X, &tmp, X, FULL_PRECISION); + if ( (X->exp == CONST_PI2.exp) && + ((X->sigh > CONST_PI2.sigh) + || ((X->sigh == CONST_PI2.sigh) + && (X->sigl > CONST_PI2.sigl))) ) + { + /* CONST_PI2extra is negative, so the result of the + subtraction can be larger than pi/2. This means + that the argument is actually in a different quadrant. + The correction is always < pi/2, so it can't overflow + into yet another quadrant. */ + reg_sub(&CONST_PI, X, X, FULL_PRECISION); + q++; + } + } + } +#endif BETTER_THAN_486 + + control_word = old_cw; + partial_status = saved_status & ~SW_C2; /* Reduction complete. */ + + return (q & 3) | even; +} + + +/* Convert a long to register */ +void convert_l2reg(long const *arg, FPU_REG *dest) +{ + long num = *arg; + + if (num == 0) + { reg_move(&CONST_Z, dest); return; } + + if (num > 0) + dest->sign = SIGN_POS; + else + { num = -num; dest->sign = SIGN_NEG; } + + dest->sigh = num; + dest->sigl = 0; + dest->exp = EXP_BIAS + 31; + dest->tag = TW_Valid; + normalize(dest); +} + + +static void single_arg_error(FPU_REG *st0_ptr) +{ + switch ( st0_ptr->tag ) + { + case TW_NaN: + if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ + { + EXCEPTION(EX_Invalid); + if ( control_word & CW_Invalid ) + st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */ + } + break; /* return with a NaN in st(0) */ + case TW_Empty: + stack_underflow(); /* Puts a QNaN in st(0) */ + break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x0112); +#endif PARANOID + } +} + + +static void single_arg_2_error(FPU_REG *st0_ptr) +{ + FPU_REG *st_new_ptr; + + switch ( st0_ptr->tag ) + { + case TW_NaN: + if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ + { + EXCEPTION(EX_Invalid); + if ( control_word & CW_Invalid ) + { + /* The masked response */ + /* Convert to a QNaN */ + st0_ptr->sigh |= 0x40000000; + st_new_ptr = &st(-1); + push(); + reg_move(&st(1), st_new_ptr); + } + } + else + { + /* A QNaN */ + st_new_ptr = &st(-1); + push(); + reg_move(&st(1), st_new_ptr); + } + break; /* return with a NaN in st(0) */ +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x0112); +#endif PARANOID + } +} + + +/*---------------------------------------------------------------------------*/ + +static void f2xm1(FPU_REG *st0_ptr) +{ + clear_C1(); + switch ( st0_ptr->tag ) + { + case TW_Valid: + { + if ( st0_ptr->exp >= 0 ) + { + /* For an 80486 FPU, the result is undefined. */ + } +#ifdef DENORM_OPERAND + else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + else + { + /* poly_2xm1(x) requires 0 < x < 1. */ + poly_2xm1(st0_ptr, st0_ptr); + } + if ( st0_ptr->exp <= EXP_UNDER ) + { + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + arith_underflow(st0_ptr); + } + set_precision_flag_up(); /* 80486 appears to always do this */ + return; + } + case TW_Zero: + return; + case TW_Infinity: + if ( st0_ptr->sign == SIGN_NEG ) + { + /* -infinity gives -1 (p16-10) */ + reg_move(&CONST_1, st0_ptr); + st0_ptr->sign = SIGN_NEG; + } + return; + default: + single_arg_error(st0_ptr); + } +} + + +static void fptan(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st_new_ptr; + int q; + char arg_sign = st0_ptr->sign; + + /* Stack underflow has higher priority */ + if ( st0_tag == TW_Empty ) + { + stack_underflow(); /* Puts a QNaN in st(0) */ + if ( control_word & CW_Invalid ) + { + st_new_ptr = &st(-1); + push(); + stack_underflow(); /* Puts a QNaN in the new st(0) */ + } + return; + } + + if ( STACK_OVERFLOW ) + { stack_overflow(); return; } + + switch ( st0_tag ) + { + case TW_Valid: + if ( st0_ptr->exp > EXP_BIAS - 40 ) + { + st0_ptr->sign = SIGN_POS; + if ( (q = trig_arg(st0_ptr, 0)) != -1 ) + { + poly_tan(st0_ptr, st0_ptr); + st0_ptr->sign = (q & 1) ^ arg_sign; + } + else + { + /* Operand is out of range */ + st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + } + set_precision_flag_up(); /* We do not really know if up or down */ + } + else + { + /* For a small arg, the result == the argument */ + /* Underflow may happen */ + + if ( st0_ptr->exp <= EXP_UNDER ) + { +#ifdef DENORM_OPERAND + if ( denormal_operand() ) + return; +#endif DENORM_OPERAND + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + if ( arith_underflow(st0_ptr) ) + return; + } + set_precision_flag_down(); /* Must be down. */ + } + push(); + reg_move(&CONST_1, st_new_ptr); + return; + break; + case TW_Infinity: + /* The 80486 treats infinity as an invalid operand */ + arith_invalid(st0_ptr); + if ( control_word & CW_Invalid ) + { + st_new_ptr = &st(-1); + push(); + arith_invalid(st_new_ptr); + } + return; + case TW_Zero: + push(); + reg_move(&CONST_1, st_new_ptr); + setcc(0); + break; + default: + single_arg_2_error(st0_ptr); + break; + } +} + + +static void fxtract(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st_new_ptr; + register FPU_REG *st1_ptr = st0_ptr; /* anticipate */ + + if ( STACK_OVERFLOW ) + { stack_overflow(); return; } + clear_C1(); + if ( !(st0_tag ^ TW_Valid) ) + { + long e; + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + push(); + reg_move(st1_ptr, st_new_ptr); + st_new_ptr->exp = EXP_BIAS; + e = st1_ptr->exp - EXP_BIAS; + convert_l2reg(&e, st1_ptr); + return; + } + else if ( st0_tag == TW_Zero ) + { + char sign = st0_ptr->sign; + if ( divide_by_zero(SIGN_NEG, st0_ptr) ) + return; + push(); + reg_move(&CONST_Z, st_new_ptr); + st_new_ptr->sign = sign; + return; + } + else if ( st0_tag == TW_Infinity ) + { + char sign = st0_ptr->sign; + st0_ptr->sign = SIGN_POS; + push(); + reg_move(&CONST_INF, st_new_ptr); + st_new_ptr->sign = sign; + return; + } + else if ( st0_tag == TW_NaN ) + { + if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) ) + return; + push(); + reg_move(st1_ptr, st_new_ptr); + return; + } + else if ( st0_tag == TW_Empty ) + { + /* Is this the correct behaviour? */ + if ( control_word & EX_Invalid ) + { + stack_underflow(); + push(); + stack_underflow(); + } + else + EXCEPTION(EX_StackUnder); + } +#ifdef PARANOID + else + EXCEPTION(EX_INTERNAL | 0x119); +#endif PARANOID +} + + +static void fdecstp(FPU_REG *st0_ptr) +{ + clear_C1(); + top--; /* st0_ptr will be fixed in math_emulate() before the next instr */ +} + +static void fincstp(FPU_REG *st0_ptr) +{ + clear_C1(); + top++; /* st0_ptr will be fixed in math_emulate() before the next instr */ +} + + +static void fsqrt_(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + + clear_C1(); + if ( !(st0_tag ^ TW_Valid) ) + { + int expon; + + if (st0_ptr->sign == SIGN_NEG) + { + arith_invalid(st0_ptr); /* sqrt(negative) is invalid */ + return; + } + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + expon = st0_ptr->exp - EXP_BIAS; + st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */ + + wm_sqrt(st0_ptr, control_word); /* Do the computation */ + + st0_ptr->exp += expon >> 1; + st0_ptr->sign = SIGN_POS; + } + else if ( st0_tag == TW_Zero ) + return; + else if ( st0_tag == TW_Infinity ) + { + if ( st0_ptr->sign == SIGN_NEG ) + arith_invalid(st0_ptr); /* sqrt(-Infinity) is invalid */ + return; + } + else + { single_arg_error(st0_ptr); return; } + +} + + +static void frndint_(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + int flags; + + if ( !(st0_tag ^ TW_Valid) ) + { + if (st0_ptr->exp > EXP_BIAS+63) + return; + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + /* Fortunately, this can't overflow to 2^64 */ + if ( (flags = round_to_int(st0_ptr)) ) + set_precision_flag(flags); + + st0_ptr->exp = EXP_BIAS + 63; + normalize(st0_ptr); + return; + } + else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) ) + return; + else + single_arg_error(st0_ptr); +} + + +static void fsin(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + char arg_sign = st0_ptr->sign; + + if ( st0_tag == TW_Valid ) + { + FPU_REG rv; + int q; + + if ( st0_ptr->exp > EXP_BIAS - 40 ) + { + st0_ptr->sign = SIGN_POS; + if ( (q = trig_arg(st0_ptr, 0)) != -1 ) + { + + poly_sine(st0_ptr, &rv); + + if (q & 2) + rv.sign ^= SIGN_POS ^ SIGN_NEG; + rv.sign ^= arg_sign; + reg_move(&rv, st0_ptr); + + /* We do not really know if up or down */ + set_precision_flag_up(); + return; + } + else + { + /* Operand is out of range */ + st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + } + } + else + { + /* For a small arg, the result == the argument */ + /* Underflow may happen */ + + if ( st0_ptr->exp <= EXP_UNDER ) + { +#ifdef DENORM_OPERAND + if ( denormal_operand() ) + return; +#endif DENORM_OPERAND + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + arith_underflow(st0_ptr); + return; + } + + set_precision_flag_up(); /* Must be up. */ + } + } + else if ( st0_tag == TW_Zero ) + { + setcc(0); + return; + } + else if ( st0_tag == TW_Infinity ) + { + /* The 80486 treats infinity as an invalid operand */ + arith_invalid(st0_ptr); + return; + } + else + single_arg_error(st0_ptr); +} + + +static int f_cos(FPU_REG *arg) +{ + char arg_sign = arg->sign; + + if ( arg->tag == TW_Valid ) + { + FPU_REG rv; + int q; + + if ( arg->exp > EXP_BIAS - 40 ) + { + arg->sign = SIGN_POS; + if ( (arg->exp < EXP_BIAS) + || ((arg->exp == EXP_BIAS) + && (significand(arg) <= 0xc90fdaa22168c234LL)) ) + { + poly_cos(arg, &rv); + reg_move(&rv, arg); + + /* We do not really know if up or down */ + set_precision_flag_down(); + + return 0; + } + else if ( (q = trig_arg(arg, FCOS)) != -1 ) + { + poly_sine(arg, &rv); + + if ((q+1) & 2) + rv.sign ^= SIGN_POS ^ SIGN_NEG; + reg_move(&rv, arg); + + /* We do not really know if up or down */ + set_precision_flag_down(); + + return 0; + } + else + { + /* Operand is out of range */ + arg->sign = arg_sign; /* restore st(0) */ + return 1; + } + } + else + { +#ifdef DENORM_OPERAND + if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) ) + return 1; +#endif DENORM_OPERAND + + setcc(0); + reg_move(&CONST_1, arg); +#ifdef PECULIAR_486 + set_precision_flag_down(); /* 80486 appears to do this. */ +#else + set_precision_flag_up(); /* Must be up. */ +#endif PECULIAR_486 + return 0; + } + } + else if ( arg->tag == TW_Zero ) + { + reg_move(&CONST_1, arg); + setcc(0); + return 0; + } + else if ( arg->tag == TW_Infinity ) + { + /* The 80486 treats infinity as an invalid operand */ + arith_invalid(arg); + return 1; + } + else + { + single_arg_error(arg); /* requires arg == &st(0) */ + return 1; + } +} + + +static void fcos(FPU_REG *st0_ptr) +{ + f_cos(st0_ptr); +} + + +static void fsincos(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st_new_ptr; + FPU_REG arg; + + /* Stack underflow has higher priority */ + if ( st0_tag == TW_Empty ) + { + stack_underflow(); /* Puts a QNaN in st(0) */ + if ( control_word & CW_Invalid ) + { + st_new_ptr = &st(-1); + push(); + stack_underflow(); /* Puts a QNaN in the new st(0) */ + } + return; + } + + if ( STACK_OVERFLOW ) + { stack_overflow(); return; } + + if ( st0_tag == TW_NaN ) + { + single_arg_2_error(st0_ptr); + return; + } + else if ( st0_tag == TW_Infinity ) + { + /* The 80486 treats infinity as an invalid operand */ + if ( !arith_invalid(st0_ptr) ) + { + /* unmasked response */ + push(); + arith_invalid(st_new_ptr); + } + return; + } + + reg_move(st0_ptr,&arg); + if ( !f_cos(&arg) ) + { + fsin(st0_ptr); + push(); + reg_move(&arg,st_new_ptr); + } + +} + + +/*---------------------------------------------------------------------------*/ +/* The following all require two arguments: st(0) and st(1) */ + +/* A lean, mean kernel for the fprem instructions. This relies upon + the division and rounding to an integer in do_fprem giving an + exact result. Because of this, rem_kernel() needs to deal only with + the least significant 64 bits, the more significant bits of the + result must be zero. + */ +static void rem_kernel(unsigned long long st0, unsigned long long *y, + unsigned long long st1, + unsigned long long q, int n) +{ + unsigned long long x; + + x = st0 << n; + + /* Do the required multiplication and subtraction in the one operation */ + asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1; + movl %3,%%eax; mull %4; subl %%eax,%1; + movl %2,%%eax; mull %5; subl %%eax,%1;" + :"=m" (x), "=m" (((unsigned *)&x)[1]) + :"m" (st1),"m" (((unsigned *)&st1)[1]), + "m" (q),"m" (((unsigned *)&q)[1]) + :"%ax","%dx"); + + *y = x; +} + + +/* Remainder of st(0) / st(1) */ +/* This routine produces exact results, i.e. there is never any + rounding or truncation, etc of the result. */ +static void do_fprem(FPU_REG *st0_ptr, int round) +{ + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + char st0_tag = st0_ptr->tag; + char sign = st0_ptr->sign; + + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { + FPU_REG tmp; + int old_cw = control_word; + int expdif = st0_ptr->exp - st1_ptr->exp; + long long q; + unsigned short saved_status; + int cc = 0; + +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + /* We want the status following the denorm tests, but don't want + the status changed by the arithmetic operations. */ + saved_status = partial_status; + control_word &= ~CW_RC; + control_word |= RC_CHOP; + + if (expdif < 64) + { + /* This should be the most common case */ + + if ( expdif > -2 ) + { + reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + + if ( tmp.exp >= EXP_BIAS ) + { + round_to_int(&tmp); /* Fortunately, this can't overflow + to 2^64 */ + q = significand(&tmp); + + rem_kernel(significand(st0_ptr), + &significand(&tmp), + significand(st1_ptr), + q, expdif); + + tmp.exp = st1_ptr->exp; + } + else + { + reg_move(st0_ptr, &tmp); + q = 0; + } + tmp.sign = sign; + + if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) ) + { + /* We may need to subtract st(1) once more, + to get a result <= 1/2 of st(1). */ + unsigned long long x; + expdif = st1_ptr->exp - tmp.exp; + if ( expdif <= 1 ) + { + if ( expdif == 0 ) + x = significand(st1_ptr) - significand(&tmp); + else /* expdif is 1 */ + x = (significand(st1_ptr) << 1) - significand(&tmp); + if ( (x < significand(&tmp)) || + /* or equi-distant (from 0 & st(1)) and q is odd */ + ((x == significand(&tmp)) && (q & 1) ) ) + { + tmp.sign ^= (SIGN_POS^SIGN_NEG); + significand(&tmp) = x; + q++; + } + } + } + + if (q & 4) cc |= SW_C0; + if (q & 2) cc |= SW_C3; + if (q & 1) cc |= SW_C1; + } + else + { + control_word = old_cw; + setcc(0); + return; + } + } + else + { + /* There is a large exponent difference ( >= 64 ) */ + /* To make much sense, the code in this section should + be done at high precision. */ + int exp_1; + + /* prevent overflow here */ + /* N is 'a number between 32 and 63' (p26-113) */ + reg_move(st0_ptr, &tmp); + tmp.exp = EXP_BIAS + 56; + exp_1 = st1_ptr->exp; st1_ptr->exp = EXP_BIAS; + expdif -= 56; + + reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + st1_ptr->exp = exp_1; + + round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */ + + rem_kernel(significand(st0_ptr), + &significand(&tmp), + significand(st1_ptr), + significand(&tmp), + tmp.exp - EXP_BIAS + ); + tmp.exp = exp_1 + expdif; + tmp.sign = sign; + + /* It is possible for the operation to be complete here. + What does the IEEE standard say? The Intel 80486 manual + implies that the operation will never be completed at this + point, and the behaviour of a real 80486 confirms this. + */ + if ( !(tmp.sigh | tmp.sigl) ) + { + /* The result is zero */ + control_word = old_cw; + partial_status = saved_status; + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; +#ifdef PECULIAR_486 + setcc(SW_C2); +#else + setcc(0); +#endif PECULIAR_486 + return; + } + cc = SW_C2; + } + + control_word = old_cw; + partial_status = saved_status; + normalize_nuo(&tmp); + reg_move(&tmp, st0_ptr); + setcc(cc); + + /* The only condition to be looked for is underflow, + and it can occur here only if underflow is unmasked. */ + if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero) + && !(control_word & CW_Underflow) ) + arith_underflow(st0_ptr); + + return; + } + else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) + { + stack_underflow(); + return; + } + else if ( st0_tag == TW_Zero ) + { + if ( st1_tag == TW_Valid ) + { +#ifdef DENORM_OPERAND + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + setcc(0); return; + } + else if ( st1_tag == TW_Zero ) + { arith_invalid(st0_ptr); return; } /* fprem(?,0) always invalid */ + else if ( st1_tag == TW_Infinity ) + { setcc(0); return; } + } + else if ( st0_tag == TW_Valid ) + { + if ( st1_tag == TW_Zero ) + { + arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */ + return; + } + else if ( st1_tag != TW_NaN ) + { +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + if ( st1_tag == TW_Infinity ) + { + /* fprem(Valid,Infinity) is o.k. */ + setcc(0); return; + } + } + } + else if ( st0_tag == TW_Infinity ) + { + if ( st1_tag != TW_NaN ) + { + arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */ + return; + } + } + + /* One of the registers must contain a NaN is we got here. */ + +#ifdef PARANOID + if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) ) + EXCEPTION(EX_INTERNAL | 0x118); +#endif PARANOID + + real_2op_NaN(st1_ptr, st0_ptr, st0_ptr); + +} + + +/* ST(1) <- ST(1) * log ST; pop ST */ +static void fyl2x(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st1_ptr = &st(1), exponent; + char st1_tag = st1_ptr->tag; + int e; + + clear_C1(); + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { + if ( st0_ptr->sign == SIGN_POS ) + { +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) ) + { + /* Special case. The result can be precise. */ + e = st0_ptr->exp - EXP_BIAS; + if ( e > 0 ) + { + exponent.sigh = e; + exponent.sign = SIGN_POS; + } + else + { + exponent.sigh = -e; + exponent.sign = SIGN_NEG; + } + exponent.sigl = 0; + exponent.exp = EXP_BIAS + 31; + exponent.tag = TW_Valid; + normalize_nuo(&exponent); + reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION); + } + else + { + /* The usual case */ + poly_l2(st0_ptr, st1_ptr, st1_ptr); + if ( st1_ptr->exp <= EXP_UNDER ) + { + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + arith_underflow(st1_ptr); + } + else + set_precision_flag_up(); /* 80486 appears to always do this */ + } + pop(); + return; + } + else + { + /* negative */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + } + else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) + { + stack_underflow_pop(1); + return; + } + else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) ) + { + /* one of the args is zero, the other valid, or both zero */ + if ( st0_tag == TW_Zero ) + { + if ( st1_tag == TW_Zero ) + { + /* Both args zero is invalid */ + if ( !arith_invalid(st1_ptr) ) + pop(); + } +#ifdef PECULIAR_486 + /* This case is not specifically covered in the manual, + but divide-by-zero would seem to be the best response. + However, a real 80486 does it this way... */ + else if ( st0_ptr->tag == TW_Infinity ) + { + reg_move(&CONST_INF, st1_ptr); + pop(); + } +#endif PECULIAR_486 + else + { + if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) ) + pop(); + } + return; + } + else + { + /* st(1) contains zero, st(0) valid <> 0 */ + /* Zero is the valid answer */ + char sign = st1_ptr->sign; + + if ( st0_ptr->sign == SIGN_NEG ) + { + /* log(negative) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS; + pop(); st0_ptr = &st(0); + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; + return; + } + } + /* One or both arg must be an infinity */ + else if ( st0_tag == TW_Infinity ) + { + if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) ) + { + /* log(-infinity) or 0*log(infinity) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + else + { + char sign = st1_ptr->sign; + +#ifdef DENORM_OPERAND + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + pop(); st0_ptr = &st(0); + reg_move(&CONST_INF, st0_ptr); + st0_ptr->sign = sign; + return; + } + } + /* st(1) must be infinity here */ + else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) ) + { + if ( st0_ptr->exp >= EXP_BIAS ) + { + if ( (st0_ptr->exp == EXP_BIAS) && + (st0_ptr->sigh == 0x80000000) && + (st0_ptr->sigl == 0) ) + { + /* st(0) holds 1.0 */ + /* infinity*log(1) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + /* st(0) is positive and > 1.0 */ + pop(); + } + else + { + /* st(0) is positive and < 1.0 */ + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + st1_ptr->sign ^= SIGN_NEG; + pop(); + } + return; + } + else + { + /* st(0) must be zero or negative */ + if ( st0_ptr->tag == TW_Zero ) + { + /* This should be invalid, but a real 80486 is happy with it. */ +#ifndef PECULIAR_486 + if ( !divide_by_zero(st1_ptr->sign, st1_ptr) ) +#endif PECULIAR_486 + { + st1_ptr->sign ^= SIGN_NEG^SIGN_POS; + pop(); + } + } + else + { + /* log(negative) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + } + return; + } +} + + +static void fpatan(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + + clear_C1(); + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + poly_atan(st0_ptr, st1_ptr, st1_ptr); + + if ( st1_ptr->exp <= EXP_UNDER ) + { + /* A denormal result has been produced. + Precision must have been lost. + This is by definition an underflow. */ + arith_underflow(st1_ptr); + pop(); + return; + } + } + else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) + { + stack_underflow_pop(1); + return; + } + else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) ) + { + char sign = st1_ptr->sign; + if ( st0_tag == TW_Infinity ) + { + if ( st1_tag == TW_Infinity ) + { + if ( st0_ptr->sign == SIGN_POS ) + { reg_move(&CONST_PI4, st1_ptr); } + else + reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION); + } + else + { +#ifdef DENORM_OPERAND + if ( st1_tag != TW_Zero ) + { + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; + } +#endif DENORM_OPERAND + + if ( st0_ptr->sign == SIGN_POS ) + { + reg_move(&CONST_Z, st1_ptr); + st1_ptr->sign = sign; /* An 80486 preserves the sign */ + pop(); + return; + } + else + reg_move(&CONST_PI, st1_ptr); + } + } + else + { + /* st(1) is infinity, st(0) not infinity */ +#ifdef DENORM_OPERAND + if ( st0_tag != TW_Zero ) + { + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; + } +#endif DENORM_OPERAND + + reg_move(&CONST_PI2, st1_ptr); + } + st1_ptr->sign = sign; + } + else if ( st1_tag == TW_Zero ) + { + /* st(0) must be valid or zero */ + char sign = st1_ptr->sign; + +#ifdef DENORM_OPERAND + if ( st0_tag != TW_Zero ) + { + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; + } +#endif DENORM_OPERAND + + if ( st0_ptr->sign == SIGN_POS ) + { /* An 80486 preserves the sign */ pop(); return; } + else + reg_move(&CONST_PI, st1_ptr); + st1_ptr->sign = sign; + } + else if ( st0_tag == TW_Zero ) + { + /* st(1) must be TW_Valid here */ + char sign = st1_ptr->sign; + +#ifdef DENORM_OPERAND + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + reg_move(&CONST_PI2, st1_ptr); + st1_ptr->sign = sign; + } +#ifdef PARANOID + else + EXCEPTION(EX_INTERNAL | 0x125); +#endif PARANOID + + pop(); + set_precision_flag_up(); /* We do not really know if up or down */ +} + + +static void fprem(FPU_REG *st0_ptr) +{ + do_fprem(st0_ptr, RC_CHOP); +} + + +static void fprem1(FPU_REG *st0_ptr) +{ + do_fprem(st0_ptr, RC_RND); +} + + +static void fyl2xp1(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag, sign; + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + + clear_C1(); + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() ) + return; +#endif DENORM_OPERAND + + if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) ) + { +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; +#else + if ( arith_invalid(st1_ptr) ) /* poly_l2p1() returned invalid */ + return; +#endif PECULIAR_486 + } + if ( st1_ptr->exp <= EXP_UNDER ) + { + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + sign = st1_ptr->sign; + arith_underflow(st1_ptr); + st1_ptr->sign = sign; + } + else + set_precision_flag_up(); /* 80486 appears to always do this */ + pop(); + return; + } + else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) + { + stack_underflow_pop(1); + return; + } + else if ( st0_tag == TW_Zero ) + { + if ( st1_tag <= TW_Zero ) + { +#ifdef DENORM_OPERAND + if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) && + (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + st0_ptr->sign ^= st1_ptr->sign; + reg_move(st0_ptr, st1_ptr); + } + else if ( st1_tag == TW_Infinity ) + { + /* Infinity*log(1) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + else if ( st1_tag == TW_NaN ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } +#ifdef PARANOID + else + { + EXCEPTION(EX_INTERNAL | 0x116); + return; + } +#endif PARANOID + pop(); return; + } + else if ( st0_tag == TW_Valid ) + { + if ( st1_tag == TW_Zero ) + { + if ( st0_ptr->sign == SIGN_NEG ) + { + if ( st0_ptr->exp >= EXP_BIAS ) + { + /* st(0) holds <= -1.0 */ +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; +#else + if ( arith_invalid(st1_ptr) ) return; +#endif PECULIAR_486 + pop(); return; + } +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; + pop(); return; + } +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + pop(); return; + } + if ( st1_tag == TW_Infinity ) + { + if ( st0_ptr->sign == SIGN_NEG ) + { + if ( (st0_ptr->exp >= EXP_BIAS) && + !((st0_ptr->sigh == 0x80000000) && + (st0_ptr->sigl == 0)) ) + { + /* st(0) holds < -1.0 */ +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; +#else + if ( arith_invalid(st1_ptr) ) return; +#endif PECULIAR_486 + pop(); return; + } +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; + pop(); return; + } +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + pop(); return; + } + if ( st1_tag == TW_NaN ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + } + else if ( st0_tag == TW_NaN ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + else if ( st0_tag == TW_Infinity ) + { + if ( st1_tag == TW_NaN ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + else if ( st0_ptr->sign == SIGN_NEG ) + { + int exponent = st1_ptr->exp; +#ifndef PECULIAR_486 + /* This should have higher priority than denormals, but... */ + if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ + return; +#endif PECULIAR_486 +#ifdef DENORM_OPERAND + if ( st1_tag != TW_Zero ) + { + if ( (exponent <= EXP_UNDER) && (denormal_operand()) ) + return; + } +#endif DENORM_OPERAND +#ifdef PECULIAR_486 + /* Denormal operands actually get higher priority */ + if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ + return; +#endif PECULIAR_486 + pop(); + return; + } + else if ( st1_tag == TW_Zero ) + { + /* log(infinity) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + + /* st(1) must be valid here. */ + +#ifdef DENORM_OPERAND + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + /* The Manual says that log(Infinity) is invalid, but a real + 80486 sensibly says that it is o.k. */ + { char sign = st1_ptr->sign; + reg_move(&CONST_INF, st1_ptr); + st1_ptr->sign = sign; + } + pop(); + return; + } +#ifdef PARANOID + else + { + EXCEPTION(EX_INTERNAL | 0x117); + } +#endif PARANOID +} + + +static void fscale(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + int old_cw = control_word; + char sign = st0_ptr->sign; + + clear_C1(); + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { + long scale; + FPU_REG tmp; + +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + if ( st1_ptr->exp > EXP_BIAS + 30 ) + { + /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */ + char sign; + + if ( st1_ptr->sign == SIGN_POS ) + { + EXCEPTION(EX_Overflow); + sign = st0_ptr->sign; + reg_move(&CONST_INF, st0_ptr); + st0_ptr->sign = sign; + } + else + { + EXCEPTION(EX_Underflow); + sign = st0_ptr->sign; + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; + } + return; + } + + control_word &= ~CW_RC; + control_word |= RC_CHOP; + reg_move(st1_ptr, &tmp); + round_to_int(&tmp); /* This can never overflow here */ + control_word = old_cw; + scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl; + scale += st0_ptr->exp; + st0_ptr->exp = scale; + + /* Use round_reg() to properly detect under/overflow etc */ + round_reg(st0_ptr, 0, control_word); + + return; + } + else if ( st0_tag == TW_Valid ) + { + if ( st1_tag == TW_Zero ) + { + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + return; + } + if ( st1_tag == TW_Infinity ) + { +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + if ( st1_ptr->sign == SIGN_POS ) + { reg_move(&CONST_INF, st0_ptr); } + else + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; + return; + } + if ( st1_tag == TW_NaN ) + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } + } + else if ( st0_tag == TW_Zero ) + { + if ( st1_tag == TW_Valid ) + { + +#ifdef DENORM_OPERAND + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + return; + } + else if ( st1_tag == TW_Zero ) { return; } + else if ( st1_tag == TW_Infinity ) + { + if ( st1_ptr->sign == SIGN_NEG ) + return; + else + { + arith_invalid(st0_ptr); /* Zero scaled by +Infinity */ + return; + } + } + else if ( st1_tag == TW_NaN ) + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } + } + else if ( st0_tag == TW_Infinity ) + { + if ( st1_tag == TW_Valid ) + { + +#ifdef DENORM_OPERAND + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + return; + } + if ( ((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS)) + || (st1_tag == TW_Zero) ) + return; + else if ( st1_tag == TW_Infinity ) + { + arith_invalid(st0_ptr); /* Infinity scaled by -Infinity */ + return; + } + else if ( st1_tag == TW_NaN ) + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } + } + else if ( st0_tag == TW_NaN ) + { + if ( st1_tag != TW_Empty ) + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } + } + +#ifdef PARANOID + if ( !((st0_tag == TW_Empty) || (st1_tag == TW_Empty)) ) + { + EXCEPTION(EX_INTERNAL | 0x115); + return; + } +#endif + + /* At least one of st(0), st(1) must be empty */ + stack_underflow(); + +} + + +/*---------------------------------------------------------------------------*/ + +static FUNC_ST0 const trig_table_a[] = { + f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp +}; + +void trig_a(void) +{ + (trig_table_a[FPU_rm])(&st(0)); +} + + +static FUNC_ST0 const trig_table_b[] = + { + fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos + }; + +void trig_b(void) +{ + (trig_table_b[FPU_rm])(&st(0)); +} diff --git a/arch/i386/math-emu/get_address.c b/arch/i386/math-emu/get_address.c new file mode 100644 index 000000000..6f3270ae3 --- /dev/null +++ b/arch/i386/math-emu/get_address.c @@ -0,0 +1,423 @@ +/*---------------------------------------------------------------------------+ + | get_address.c | + | | + | Get the effective address from an FPU instruction. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ + + +#include <linux/stddef.h> +#include <linux/head.h> + +#include <asm/segment.h> + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" + + +#define FPU_WRITE_BIT 0x10 + +static int reg_offset[] = { + offsetof(struct info,___eax), + offsetof(struct info,___ecx), + offsetof(struct info,___edx), + offsetof(struct info,___ebx), + offsetof(struct info,___esp), + offsetof(struct info,___ebp), + offsetof(struct info,___esi), + offsetof(struct info,___edi) +}; + +#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info)) + +static int reg_offset_vm86[] = { + offsetof(struct info,___cs), + offsetof(struct info,___vm86_ds), + offsetof(struct info,___vm86_es), + offsetof(struct info,___vm86_fs), + offsetof(struct info,___vm86_gs), + offsetof(struct info,___ss), + offsetof(struct info,___vm86_ds) + }; + +#define VM86_REG_(x) (*(unsigned short *) \ + (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info)) + +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,___ss), + offsetof(struct info,___ds) + }; + +#define PM_REG_(x) (*(unsigned short *) \ + (reg_offset_pm[((unsigned)x)]+(char *) FPU_info)) + + +/* Decode the SIB byte. This function assumes mod != 0 */ +static int sib(int mod, unsigned long *fpu_eip) +{ + unsigned char ss,index,base; + long offset; + + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + base = get_fs_byte((char *) (*fpu_eip)); /* The SIB byte */ + RE_ENTRANT_CHECK_ON; + (*fpu_eip)++; + ss = base >> 6; + index = (base >> 3) & 7; + base &= 7; + + if ((mod == 0) && (base == 5)) + offset = 0; /* No base register */ + else + offset = REG_(base); + + if (index == 4) + { + /* No index register */ + /* A non-zero ss is illegal */ + if ( ss ) + EXCEPTION(EX_Invalid); + } + else + { + offset += (REG_(index)) << ss; + } + + if (mod == 1) + { + /* 8 bit signed displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + offset += (signed char) get_fs_byte((char *) (*fpu_eip)); + RE_ENTRANT_CHECK_ON; + (*fpu_eip)++; + } + else if (mod == 2 || base == 5) /* The second condition also has mod==0 */ + { + /* 32 bit displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(4); + offset += (signed) get_fs_long((unsigned long *) (*fpu_eip)); + RE_ENTRANT_CHECK_ON; + (*fpu_eip) += 4; + } + + return offset; +} + + +static unsigned long vm86_segment(unsigned char segment, + unsigned short *selector) +{ + segment--; +#ifdef PARANOID + if ( segment > PREFIX_SS_ ) + { + EXCEPTION(EX_INTERNAL|0x130); + math_abort(FPU_info,SIGSEGV); + } +#endif PARANOID + *selector = VM86_REG_(segment); + return (unsigned long)VM86_REG_(segment) << 4; +} + + +/* This should work for 16 and 32 bit protected mode. */ +static long pm_address(unsigned char FPU_modrm, unsigned char segment, + unsigned short *selector, long offset) +{ + struct desc_struct descriptor; + unsigned long base_address, limit, address, seg_top; + + segment--; +#ifdef PARANOID + if ( segment > PREFIX_SS_ ) + { + EXCEPTION(EX_INTERNAL|0x132); + math_abort(FPU_info,SIGSEGV); + } +#endif PARANOID + + *selector = PM_REG_(segment); + + descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); + base_address = SEG_BASE_ADDR(descriptor); + address = base_address + offset; + limit = base_address + + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1; + if ( limit < base_address ) limit = 0xffffffff; + + if ( SEG_EXPAND_DOWN(descriptor) ) + { + if ( SEG_G_BIT(descriptor) ) + seg_top = 0xffffffff; + else + { + seg_top = base_address + (1 << 20); + if ( seg_top < base_address ) seg_top = 0xffffffff; + } + access_limit = + (address <= limit) || (address >= seg_top) ? 0 : + ((seg_top-address) >= 255 ? 255 : seg_top-address); + } + else + { + access_limit = + (address > limit) || (address < base_address) ? 0 : + ((limit-address) >= 254 ? 255 : limit-address+1); + } + if ( SEG_EXECUTE_ONLY(descriptor) || + (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) ) + { + access_limit = 0; + } + return address; +} + + +/* + MOD R/M byte: MOD == 3 has a special use for the FPU + SIB byte used iff R/M = 100b + + 7 6 5 4 3 2 1 0 + ..... ......... ......... + MOD OPCODE(2) R/M + + + SIB byte + + 7 6 5 4 3 2 1 0 + ..... ......... ......... + SS INDEX BASE + +*/ + +void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, +/* unsigned short *selector, unsigned long *offset, */ + fpu_addr_modes addr_modes) +{ + unsigned char mod; + unsigned rm = FPU_modrm & 7; + long *cpu_reg_ptr; + int address = 0; /* Initialized just to stop compiler warnings. */ + + /* Memory accessed via the cs selector is write protected + in `non-segmented' 32 bit protected mode. */ + if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) + && (addr_modes.override.segment == PREFIX_CS_) ) + { + math_abort(FPU_info,SIGSEGV); + } + + addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ + + mod = (FPU_modrm >> 6) & 3; + + if (rm == 4 && mod != 3) + { + address = sib(mod, fpu_eip); + } + else + { + cpu_reg_ptr = & REG_(rm); + switch (mod) + { + case 0: + if (rm == 5) + { + /* Special case: disp32 */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(4); + address = get_fs_long((unsigned long *) (*fpu_eip)); + (*fpu_eip) += 4; + RE_ENTRANT_CHECK_ON; + addr->offset = address; + return (void *) address; + } + else + { + address = *cpu_reg_ptr; /* Just return the contents + of the cpu register */ + addr->offset = address; + return (void *) address; + } + case 1: + /* 8 bit signed displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + address = (signed char) get_fs_byte((char *) (*fpu_eip)); + RE_ENTRANT_CHECK_ON; + (*fpu_eip)++; + break; + case 2: + /* 32 bit displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(4); + address = (signed) get_fs_long((unsigned long *) (*fpu_eip)); + (*fpu_eip) += 4; + RE_ENTRANT_CHECK_ON; + break; + case 3: + /* Not legal for the FPU */ + EXCEPTION(EX_Invalid); + } + address += *cpu_reg_ptr; + } + + addr->offset = address; + + switch ( addr_modes.default_mode ) + { + case 0: + break; + case VM86: + address += vm86_segment(addr_modes.override.segment, + (unsigned short *)&(addr->selector)); + break; + case PM16: + case SEG32: + address = pm_address(FPU_modrm, addr_modes.override.segment, + (unsigned short *)&(addr->selector), address); + break; + default: + EXCEPTION(EX_INTERNAL|0x133); + } + + return (void *)address; +} + + +void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, +/* unsigned short *selector, unsigned long *offset, */ + fpu_addr_modes addr_modes) +{ + unsigned char mod; + unsigned rm = FPU_modrm & 7; + int address = 0; /* Default used for mod == 0 */ + + /* Memory accessed via the cs selector is write protected + in `non-segmented' 32 bit protected mode. */ + if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) + && (addr_modes.override.segment == PREFIX_CS_) ) + { + math_abort(FPU_info,SIGSEGV); + } + + addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ + + mod = (FPU_modrm >> 6) & 3; + + switch (mod) + { + case 0: + if (rm == 6) + { + /* Special case: disp16 */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(2); + address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip)); + (*fpu_eip) += 2; + RE_ENTRANT_CHECK_ON; + goto add_segment; + } + break; + case 1: + /* 8 bit signed displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + address = (signed char) get_fs_byte((signed char *) (*fpu_eip)); + RE_ENTRANT_CHECK_ON; + (*fpu_eip)++; + break; + case 2: + /* 16 bit displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(2); + address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip)); + (*fpu_eip) += 2; + RE_ENTRANT_CHECK_ON; + break; + case 3: + /* Not legal for the FPU */ + EXCEPTION(EX_Invalid); + break; + } + switch ( rm ) + { + case 0: + address += FPU_info->___ebx + FPU_info->___esi; + break; + case 1: + address += FPU_info->___ebx + FPU_info->___edi; + break; + case 2: + address += FPU_info->___ebp + FPU_info->___esi; + if ( addr_modes.override.segment == PREFIX_DEFAULT ) + addr_modes.override.segment = PREFIX_SS_; + break; + case 3: + address += FPU_info->___ebp + FPU_info->___edi; + if ( addr_modes.override.segment == PREFIX_DEFAULT ) + addr_modes.override.segment = PREFIX_SS_; + break; + case 4: + address += FPU_info->___esi; + break; + case 5: + address += FPU_info->___edi; + break; + case 6: + address += FPU_info->___ebp; + if ( addr_modes.override.segment == PREFIX_DEFAULT ) + addr_modes.override.segment = PREFIX_SS_; + break; + case 7: + address += FPU_info->___ebx; + break; + } + + add_segment: + address &= 0xffff; + + addr->offset = address; + + switch ( addr_modes.default_mode ) + { + case 0: + break; + case VM86: + address += vm86_segment(addr_modes.override.segment, + (unsigned short *)&(addr->selector)); + break; + case PM16: + case SEG32: + address = pm_address(FPU_modrm, addr_modes.override.segment, + (unsigned short *)&(addr->selector), address); + break; + default: + EXCEPTION(EX_INTERNAL|0x131); + } + + return (void *)address ; +} diff --git a/arch/i386/math-emu/load_store.c b/arch/i386/math-emu/load_store.c new file mode 100644 index 000000000..6f0e167d6 --- /dev/null +++ b/arch/i386/math-emu/load_store.c @@ -0,0 +1,260 @@ +/*---------------------------------------------------------------------------+ + | load_store.c | + | | + | This file contains most of the code to interpret the FPU instructions | + | which load and store from user memory. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ + +#include <asm/segment.h> + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "control_w.h" + + +#define _NONE_ 0 /* st0_ptr etc not needed */ +#define _REG0_ 1 /* Will be storing st(0) */ +#define _PUSH_ 3 /* Need to check for space to push onto stack */ +#define _null_ 4 /* Function illegal or not implemented */ + +#define pop_0() { st0_ptr->tag = TW_Empty; top++; } + + +static unsigned char const type_table[32] = { + _PUSH_, _PUSH_, _PUSH_, _PUSH_, + _null_, _null_, _null_, _null_, + _REG0_, _REG0_, _REG0_, _REG0_, + _REG0_, _REG0_, _REG0_, _REG0_, + _NONE_, _null_, _NONE_, _PUSH_, + _NONE_, _PUSH_, _null_, _PUSH_, + _NONE_, _null_, _NONE_, _REG0_, + _NONE_, _REG0_, _NONE_, _REG0_ + }; + +unsigned char const data_sizes_16[32] = { + 4, 4, 8, 2, 0, 0, 0, 0, + 4, 4, 8, 2, 4, 4, 8, 2, + 14, 0, 94, 10, 2, 10, 0, 8, + 14, 0, 94, 10, 2, 10, 2, 8 +}; + +unsigned char const data_sizes_32[32] = { + 4, 4, 8, 2, 0, 0, 0, 0, + 4, 4, 8, 2, 4, 4, 8, 2, + 28, 0,108, 10, 2, 10, 0, 8, + 28, 0,108, 10, 2, 10, 2, 8 +}; + +int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, + void *data_address) +{ + FPU_REG loaded_data; + FPU_REG *st0_ptr; + + st0_ptr = NULL; /* Initialized just to stop compiler warnings. */ + + if ( addr_modes.default_mode & PROTECTED ) + { + if ( addr_modes.default_mode == SEG32 ) + { + if ( access_limit < data_sizes_32[type] ) + math_abort(FPU_info,SIGSEGV); + } + else if ( addr_modes.default_mode == PM16 ) + { + if ( access_limit < data_sizes_16[type] ) + math_abort(FPU_info,SIGSEGV); + } +#ifdef PARANOID + else + EXCEPTION(EX_INTERNAL|0x140); +#endif PARANOID + } + + switch ( type_table[type] ) + { + case _NONE_: + break; + case _REG0_: + st0_ptr = &st(0); /* Some of these instructions pop after + storing */ + break; + case _PUSH_: + { + st0_ptr = &st(-1); + if ( st0_ptr->tag != TW_Empty ) + { stack_overflow(); return 0; } + top--; + } + break; + case _null_: + FPU_illegal(); + return 0; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x141); + return 0; +#endif PARANOID + } + + switch ( type ) + { + case 000: /* fld m32real */ + clear_C1(); + reg_load_single((float *)data_address, &loaded_data); + if ( (loaded_data.tag == TW_NaN) && + real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) + { + top++; + break; + } + reg_move(&loaded_data, st0_ptr); + break; + case 001: /* fild m32int */ + clear_C1(); + reg_load_int32((long *)data_address, st0_ptr); + break; + case 002: /* fld m64real */ + clear_C1(); + reg_load_double((double *)data_address, &loaded_data); + if ( (loaded_data.tag == TW_NaN) && + real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) + { + top++; + break; + } + reg_move(&loaded_data, st0_ptr); + break; + case 003: /* fild m16int */ + clear_C1(); + reg_load_int16((short *)data_address, st0_ptr); + break; + case 010: /* fst m32real */ + clear_C1(); + reg_store_single((float *)data_address, st0_ptr); + break; + case 011: /* fist m32int */ + clear_C1(); + reg_store_int32((long *)data_address, st0_ptr); + break; + case 012: /* fst m64real */ + clear_C1(); + reg_store_double((double *)data_address, st0_ptr); + break; + case 013: /* fist m16int */ + clear_C1(); + reg_store_int16((short *)data_address, st0_ptr); + break; + case 014: /* fstp m32real */ + clear_C1(); + if ( reg_store_single((float *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 015: /* fistp m32int */ + clear_C1(); + if ( reg_store_int32((long *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 016: /* fstp m64real */ + clear_C1(); + if ( reg_store_double((double *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 017: /* fistp m16int */ + clear_C1(); + if ( reg_store_int16((short *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 020: /* fldenv m14/28byte */ + fldenv(addr_modes, (char *)data_address); + /* Ensure that the values just loaded are not changed by + fix-up operations. */ + return 1; + case 022: /* frstor m94/108byte */ + frstor(addr_modes, (char *)data_address); + /* Ensure that the values just loaded are not changed by + fix-up operations. */ + return 1; + case 023: /* fbld m80dec */ + clear_C1(); + reg_load_bcd((char *)data_address, st0_ptr); + break; + case 024: /* fldcw */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, data_address, 2); + control_word = get_fs_word((unsigned short *) data_address); + RE_ENTRANT_CHECK_ON; + if ( partial_status & ~control_word & CW_Exceptions ) + partial_status |= (SW_Summary | SW_Backward); + else + partial_status &= ~(SW_Summary | SW_Backward); +#ifdef PECULIAR_486 + control_word |= 0x40; /* An 80486 appears to always set this bit */ +#endif PECULIAR_486 + return 1; + case 025: /* fld m80real */ + clear_C1(); + reg_load_extended((long double *)data_address, st0_ptr); + break; + case 027: /* fild m64int */ + clear_C1(); + reg_load_int64((long long *)data_address, st0_ptr); + break; + case 030: /* fstenv m14/28byte */ + fstenv(addr_modes, (char *)data_address); + return 1; + case 032: /* fsave */ + fsave(addr_modes, (char *)data_address); + return 1; + case 033: /* fbstp m80dec */ + clear_C1(); + if ( reg_store_bcd((char *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 034: /* fstcw m16int */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,data_address,2); + put_fs_word(control_word, (short *) data_address); + RE_ENTRANT_CHECK_ON; + return 1; + case 035: /* fstp m80real */ + clear_C1(); + if ( reg_store_extended((long double *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 036: /* fstsw m2byte */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,data_address,2); + put_fs_word(status_word(),(short *) data_address); + RE_ENTRANT_CHECK_ON; + return 1; + case 037: /* fistp m64int */ + clear_C1(); + if ( reg_store_int64((long long *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + } + return 0; +} diff --git a/arch/i386/math-emu/mul_Xsig.S b/arch/i386/math-emu/mul_Xsig.S new file mode 100644 index 000000000..1d88d4466 --- /dev/null +++ b/arch/i386/math-emu/mul_Xsig.S @@ -0,0 +1,182 @@ +/*---------------------------------------------------------------------------+ + | mul_Xsig.S | + | | + | Multiply a 12 byte fixed point number by another fixed point number. | + | | + | Copyright (C) 1992,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void mul32_Xsig(Xsig *x, unsigned b) | + | | + | void mul64_Xsig(Xsig *x, unsigned long long *b) | + | | + | void mul_Xsig_Xsig(Xsig *x, unsigned *b) | + | | + | The result is neither rounded nor normalized, and the ls bit or so may | + | be wrong. | + | | + +---------------------------------------------------------------------------*/ + .file "mul_Xsig.S" + + +#include "fpu_asm.h" + +.text + .align 2,144 +.globl _mul32_Xsig +_mul32_Xsig: + pushl %ebp + movl %esp,%ebp + subl $16,%esp + pushl %esi + + movl PARAM1,%esi + movl PARAM2,%ecx + + xor %eax,%eax + movl %eax,-4(%ebp) + movl %eax,-8(%ebp) + + movl (%esi),%eax /* lsl of Xsig */ + mull %ecx /* msl of b */ + movl %edx,-12(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull %ecx /* msl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull %ecx /* msl of b */ + addl %eax,-8(%ebp) + adcl %edx,-4(%ebp) + + movl -12(%ebp),%eax + movl %eax,(%esi) + movl -8(%ebp),%eax + movl %eax,4(%esi) + movl -4(%ebp),%eax + movl %eax,8(%esi) + + popl %esi + leave + ret + + + .align 2,144 +.globl _mul64_Xsig +_mul64_Xsig: + pushl %ebp + movl %esp,%ebp + subl $16,%esp + pushl %esi + + movl PARAM1,%esi + movl PARAM2,%ecx + + xor %eax,%eax + movl %eax,-4(%ebp) + movl %eax,-8(%ebp) + + movl (%esi),%eax /* lsl of Xsig */ + mull 4(%ecx) /* msl of b */ + movl %edx,-12(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull (%ecx) /* lsl of b */ + addl %edx,-12(%ebp) + adcl $0,-8(%ebp) + adcl $0,-4(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull 4(%ecx) /* msl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull (%ecx) /* lsl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull 4(%ecx) /* msl of b */ + addl %eax,-8(%ebp) + adcl %edx,-4(%ebp) + + movl -12(%ebp),%eax + movl %eax,(%esi) + movl -8(%ebp),%eax + movl %eax,4(%esi) + movl -4(%ebp),%eax + movl %eax,8(%esi) + + popl %esi + leave + ret + + + + .align 2,144 +.globl _mul_Xsig_Xsig +_mul_Xsig_Xsig: + pushl %ebp + movl %esp,%ebp + subl $16,%esp + pushl %esi + + movl PARAM1,%esi + movl PARAM2,%ecx + + xor %eax,%eax + movl %eax,-4(%ebp) + movl %eax,-8(%ebp) + + movl (%esi),%eax /* lsl of Xsig */ + mull 8(%ecx) /* msl of b */ + movl %edx,-12(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull 4(%ecx) /* midl of b */ + addl %edx,-12(%ebp) + adcl $0,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull (%ecx) /* lsl of b */ + addl %edx,-12(%ebp) + adcl $0,-8(%ebp) + adcl $0,-4(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull 8(%ecx) /* msl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull 4(%ecx) /* midl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull 8(%ecx) /* msl of b */ + addl %eax,-8(%ebp) + adcl %edx,-4(%ebp) + + movl -12(%ebp),%edx + movl %edx,(%esi) + movl -8(%ebp),%edx + movl %edx,4(%esi) + movl -4(%ebp),%edx + movl %edx,8(%esi) + + popl %esi + leave + ret + diff --git a/arch/i386/math-emu/poly.h b/arch/i386/math-emu/poly.h new file mode 100644 index 000000000..397cb9e3e --- /dev/null +++ b/arch/i386/math-emu/poly.h @@ -0,0 +1,116 @@ +/*---------------------------------------------------------------------------+ + | poly.h | + | | + | Header file for the FPU-emu poly*.c source files. | + | | + | Copyright (C) 1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Declarations and definitions for functions operating on Xsig (12-byte | + | extended-significand) quantities. | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _POLY_H +#define _POLY_H + +/* This 12-byte structure is used to improve the accuracy of computation + of transcendental functions. + Intended to be used to get results better than 8-byte computation + allows. 9-byte would probably be sufficient. + */ +typedef struct { + unsigned long lsw; + unsigned long midw; + unsigned long msw; +} Xsig; + +asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b, + unsigned long long *result); +asmlinkage void polynomial_Xsig(Xsig *, const unsigned long long *x, + const unsigned long long terms[], const int n); + +asmlinkage void mul32_Xsig(Xsig *, const unsigned long mult); +asmlinkage void mul64_Xsig(Xsig *, const unsigned long long *mult); +asmlinkage void mul_Xsig_Xsig(Xsig *dest, const Xsig *mult); + +asmlinkage void shr_Xsig(Xsig *, const int n); +asmlinkage int round_Xsig(Xsig *); +asmlinkage int norm_Xsig(Xsig *); +asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, const Xsig *dest); + +/* Macro to extract the most significant 32 bits from a long long */ +#define LL_MSW(x) (((unsigned long *)&x)[1]) + +/* Macro to initialize an Xsig struct */ +#define MK_XSIG(a,b,c) { c, b, a } + +/* Macro to access the 8 ms bytes of an Xsig as a long long */ +#define XSIG_LL(x) (*(unsigned long long *)&x.midw) + + +/* + Need to run gcc with optimizations on to get these to + actually be in-line. + */ + +/* Multiply two fixed-point 32 bit numbers. */ +extern inline void mul_32_32(const unsigned long arg1, + const unsigned long arg2, + unsigned long *out) +{ + asm volatile ("movl %1,%%eax; mull %2; movl %%edx,%0" \ + :"=g" (*out) \ + :"g" (arg1), "g" (arg2) \ + :"ax","dx"); +} + + +/* Add the 12 byte Xsig x2 to Xsig dest, with no checks for overflow. */ +extern inline void add_Xsig_Xsig(Xsig *dest, const Xsig *x2) +{ + asm volatile ("movl %1,%%edi; movl %2,%%esi; + movl (%%esi),%%eax; addl %%eax,(%%edi); + movl 4(%%esi),%%eax; adcl %%eax,4(%%edi); + movl 8(%%esi),%%eax; adcl %%eax,8(%%edi);" + :"=g" (*dest):"g" (dest), "g" (x2) + :"ax","si","di"); +} + + +/* Add the 12 byte Xsig x2 to Xsig dest, adjust exp if overflow occurs. */ +/* Note: the constraints in the asm statement didn't always work properly + with gcc 2.5.8. Changing from using edi to using ecx got around the + problem, but keep fingers crossed! */ +extern inline int add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp) +{ + asm volatile ("movl %2,%%ecx; movl %3,%%esi; + movl (%%esi),%%eax; addl %%eax,(%%ecx); + movl 4(%%esi),%%eax; adcl %%eax,4(%%ecx); + movl 8(%%esi),%%eax; adcl %%eax,8(%%ecx); + jnc 0f; + rcrl 8(%%ecx); rcrl 4(%%ecx); rcrl (%%ecx) + movl %4,%%ecx; incl (%%ecx) + movl $1,%%eax; jmp 1f; + 0: xorl %%eax,%%eax; + 1:" + :"=g" (*exp), "=g" (*dest) + :"g" (dest), "g" (x2), "g" (exp) + :"cx","si","ax"); +} + + +/* Negate (subtract from 1.0) the 12 byte Xsig */ +/* This is faster in a loop on my 386 than using the "neg" instruction. */ +extern inline void negate_Xsig(Xsig *x) +{ + asm volatile("movl %1,%%esi; " + "xorl %%ecx,%%ecx; " + "movl %%ecx,%%eax; subl (%%esi),%%eax; movl %%eax,(%%esi); " + "movl %%ecx,%%eax; sbbl 4(%%esi),%%eax; movl %%eax,4(%%esi); " + "movl %%ecx,%%eax; sbbl 8(%%esi),%%eax; movl %%eax,8(%%esi); " + :"=g" (*x):"g" (x):"si","ax","cx"); +} + +#endif _POLY_H diff --git a/arch/i386/math-emu/poly_2xm1.c b/arch/i386/math-emu/poly_2xm1.c new file mode 100644 index 000000000..f7c585d60 --- /dev/null +++ b/arch/i386/math-emu/poly_2xm1.c @@ -0,0 +1,152 @@ +/*---------------------------------------------------------------------------+ + | poly_2xm1.c | + | | + | Function to compute 2^x-1 by a polynomial approximation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "poly.h" + + +#define HIPOWER 11 +static const unsigned long long lterms[HIPOWER] = +{ + 0x0000000000000000LL, /* This term done separately as 12 bytes */ + 0xf5fdeffc162c7543LL, + 0x1c6b08d704a0bfa6LL, + 0x0276556df749cc21LL, + 0x002bb0ffcf14f6b8LL, + 0x0002861225ef751cLL, + 0x00001ffcbfcd5422LL, + 0x00000162c005d5f1LL, + 0x0000000da96ccb1bLL, + 0x0000000078d1b897LL, + 0x000000000422b029LL +}; + +static const Xsig hiterm = MK_XSIG(0xb17217f7, 0xd1cf79ab, 0xc8a39194); + +/* Four slices: 0.0 : 0.25 : 0.50 : 0.75 : 1.0, + These numbers are 2^(1/4), 2^(1/2), and 2^(3/4) + */ +static const Xsig shiftterm0 = MK_XSIG(0, 0, 0); +static const Xsig shiftterm1 = MK_XSIG(0x9837f051, 0x8db8a96f, 0x46ad2318); +static const Xsig shiftterm2 = MK_XSIG(0xb504f333, 0xf9de6484, 0x597d89b3); +static const Xsig shiftterm3 = MK_XSIG(0xd744fcca, 0xd69d6af4, 0x39a68bb9); + +static const Xsig *shiftterm[] = { &shiftterm0, &shiftterm1, + &shiftterm2, &shiftterm3 }; + + +/*--- poly_2xm1() -----------------------------------------------------------+ + | Requires an argument which is TW_Valid and < 1. | + +---------------------------------------------------------------------------*/ +int poly_2xm1(FPU_REG const *arg, FPU_REG *result) +{ + long int exponent, shift; + unsigned long long Xll; + Xsig accumulator, Denom, argSignif; + + + exponent = arg->exp - EXP_BIAS; + +#ifdef PARANOID + if ( (exponent >= 0) /* Don't want a |number| >= 1.0 */ + || (arg->tag != TW_Valid) ) + { + /* Number negative, too large, or not Valid. */ + EXCEPTION(EX_INTERNAL|0x127); + return 1; + } +#endif PARANOID + + argSignif.lsw = 0; + XSIG_LL(argSignif) = Xll = significand(arg); + + if ( exponent == -1 ) + { + shift = (argSignif.msw & 0x40000000) ? 3 : 2; + /* subtract 0.5 or 0.75 */ + exponent -= 2; + XSIG_LL(argSignif) <<= 2; + Xll <<= 2; + } + else if ( exponent == -2 ) + { + shift = 1; + /* subtract 0.25 */ + exponent--; + XSIG_LL(argSignif) <<= 1; + Xll <<= 1; + } + else + shift = 0; + + if ( exponent < -2 ) + { + /* Shift the argument right by the required places. */ + if ( shrx(&Xll, -2-exponent) >= 0x80000000U ) + Xll++; /* round up */ + } + + accumulator.lsw = accumulator.midw = accumulator.msw = 0; + polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER-1); + mul_Xsig_Xsig(&accumulator, &argSignif); + shr_Xsig(&accumulator, 3); + + mul_Xsig_Xsig(&argSignif, &hiterm); /* The leading term */ + add_two_Xsig(&accumulator, &argSignif, &exponent); + + if ( shift ) + { + /* The argument is large, use the identity: + f(x+a) = f(a) * (f(x) + 1) - 1; + */ + shr_Xsig(&accumulator, - exponent); + accumulator.msw |= 0x80000000; /* add 1.0 */ + mul_Xsig_Xsig(&accumulator, shiftterm[shift]); + accumulator.msw &= 0x3fffffff; /* subtract 1.0 */ + exponent = 1; + } + + if ( arg->sign != SIGN_POS ) + { + /* The argument is negative, use the identity: + f(-x) = -f(x) / (1 + f(x)) + */ + Denom.lsw = accumulator.lsw; + XSIG_LL(Denom) = XSIG_LL(accumulator); + if ( exponent < 0 ) + shr_Xsig(&Denom, - exponent); + else if ( exponent > 0 ) + { + /* exponent must be 1 here */ + XSIG_LL(Denom) <<= 1; + if ( Denom.lsw & 0x80000000 ) + XSIG_LL(Denom) |= 1; + (Denom.lsw) <<= 1; + } + Denom.msw |= 0x80000000; /* add 1.0 */ + div_Xsig(&accumulator, &Denom, &accumulator); + } + + /* Convert to 64 bit signed-compatible */ + exponent += round_Xsig(&accumulator); + + significand(result) = XSIG_LL(accumulator); + result->tag = TW_Valid; + result->exp = exponent + EXP_BIAS; + result->sign = arg->sign; + + return 0; + +} diff --git a/arch/i386/math-emu/poly_atan.c b/arch/i386/math-emu/poly_atan.c new file mode 100644 index 000000000..6edca625f --- /dev/null +++ b/arch/i386/math-emu/poly_atan.c @@ -0,0 +1,197 @@ +/*---------------------------------------------------------------------------+ + | poly_atan.c | + | | + | Compute the arctan of a FPU_REG, using a polynomial approximation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "control_w.h" +#include "poly.h" + + +#define HIPOWERon 6 /* odd poly, negative terms */ +static const unsigned long long oddnegterms[HIPOWERon] = +{ + 0x0000000000000000LL, /* Dummy (not for - 1.0) */ + 0x015328437f756467LL, + 0x0005dda27b73dec6LL, + 0x0000226bf2bfb91aLL, + 0x000000ccc439c5f7LL, + 0x0000000355438407LL +} ; + +#define HIPOWERop 6 /* odd poly, positive terms */ +static const unsigned long long oddplterms[HIPOWERop] = +{ +/* 0xaaaaaaaaaaaaaaabLL, transferred to fixedpterm[] */ + 0x0db55a71875c9ac2LL, + 0x0029fce2d67880b0LL, + 0x0000dfd3908b4596LL, + 0x00000550fd61dab4LL, + 0x0000001c9422b3f9LL, + 0x000000003e3301e1LL +}; + +static const unsigned long long denomterm = 0xebd9b842c5c53a0eLL; + +static const Xsig fixedpterm = MK_XSIG(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa); + +static const Xsig pi_signif = MK_XSIG(0xc90fdaa2, 0x2168c234, 0xc4c6628b); + + +/*--- poly_atan() -----------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result) +{ + char transformed, inverted, + sign1 = arg1->sign, sign2 = arg2->sign; + long int exponent, dummy_exp; + Xsig accumulator, Numer, Denom, accumulatore, argSignif, + argSq, argSqSq; + + + arg1->sign = arg2->sign = SIGN_POS; + if ( (compare(arg2) & ~COMP_Denormal) == COMP_A_lt_B ) + { + inverted = 1; + exponent = arg1->exp - arg2->exp; + Numer.lsw = Denom.lsw = 0; + XSIG_LL(Numer) = significand(arg1); + XSIG_LL(Denom) = significand(arg2); + } + else + { + inverted = 0; + exponent = arg2->exp - arg1->exp; + Numer.lsw = Denom.lsw = 0; + XSIG_LL(Numer) = significand(arg2); + XSIG_LL(Denom) = significand(arg1); + } + div_Xsig(&Numer, &Denom, &argSignif); + exponent += norm_Xsig(&argSignif); + + if ( (exponent >= -1) + || ((exponent == -2) && (argSignif.msw > 0xd413ccd0)) ) + { + /* The argument is greater than sqrt(2)-1 (=0.414213562...) */ + /* Convert the argument by an identity for atan */ + transformed = 1; + + if ( exponent >= 0 ) + { +#ifdef PARANOID + if ( !( (exponent == 0) && + (argSignif.lsw == 0) && (argSignif.midw == 0) && + (argSignif.msw == 0x80000000) ) ) + { + EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */ + return; + } +#endif PARANOID + argSignif.msw = 0; /* Make the transformed arg -> 0.0 */ + } + else + { + Numer.lsw = Denom.lsw = argSignif.lsw; + XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif); + + if ( exponent < -1 ) + shr_Xsig(&Numer, -1-exponent); + negate_Xsig(&Numer); + + shr_Xsig(&Denom, -exponent); + Denom.msw |= 0x80000000; + + div_Xsig(&Numer, &Denom, &argSignif); + + exponent = -1 + norm_Xsig(&argSignif); + } + } + else + { + transformed = 0; + } + + argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw; + argSq.msw = argSignif.msw; + mul_Xsig_Xsig(&argSq, &argSq); + + argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw; + mul_Xsig_Xsig(&argSqSq, &argSqSq); + + accumulatore.lsw = argSq.lsw; + XSIG_LL(accumulatore) = XSIG_LL(argSq); + + shr_Xsig(&argSq, 2*(-1-exponent-1)); + shr_Xsig(&argSqSq, 4*(-1-exponent-1)); + + /* Now have argSq etc with binary point at the left + .1xxxxxxxx */ + + /* Do the basic fixed point polynomial evaluation */ + accumulator.msw = accumulator.midw = accumulator.lsw = 0; + polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), + oddplterms, HIPOWERop-1); + mul64_Xsig(&accumulator, &XSIG_LL(argSq)); + negate_Xsig(&accumulator); + polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddnegterms, HIPOWERon-1); + negate_Xsig(&accumulator); + add_two_Xsig(&accumulator, &fixedpterm, &dummy_exp); + + mul64_Xsig(&accumulatore, &denomterm); + shr_Xsig(&accumulatore, 1 + 2*(-1-exponent)); + accumulatore.msw |= 0x80000000; + + div_Xsig(&accumulator, &accumulatore, &accumulator); + + mul_Xsig_Xsig(&accumulator, &argSignif); + mul_Xsig_Xsig(&accumulator, &argSq); + + shr_Xsig(&accumulator, 3); + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &argSignif); + + if ( transformed ) + { + /* compute pi/4 - accumulator */ + shr_Xsig(&accumulator, -1-exponent); + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &pi_signif); + exponent = -1; + } + + if ( inverted ) + { + /* compute pi/2 - accumulator */ + shr_Xsig(&accumulator, -exponent); + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &pi_signif); + exponent = 0; + } + + if ( sign1 ) + { + /* compute pi - accumulator */ + shr_Xsig(&accumulator, 1 - exponent); + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &pi_signif); + exponent = 1; + } + + exponent += round_Xsig(&accumulator); + significand(result) = XSIG_LL(accumulator); + result->exp = exponent + EXP_BIAS; + result->tag = TW_Valid; + result->sign = sign2; + +} diff --git a/arch/i386/math-emu/poly_l2.c b/arch/i386/math-emu/poly_l2.c new file mode 100644 index 000000000..1677f4aff --- /dev/null +++ b/arch/i386/math-emu/poly_l2.c @@ -0,0 +1,255 @@ +/*---------------------------------------------------------------------------+ + | poly_l2.c | + | | + | Compute the base 2 log of a FPU_REG, using a polynomial approximation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "poly.h" + + + +static void log2_kernel(FPU_REG const *arg, + Xsig *accum_result, long int *expon); + + +/*--- poly_l2() -------------------------------------------------------------+ + | Base 2 logarithm by a polynomial approximation. | + +---------------------------------------------------------------------------*/ +void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result) +{ + long int exponent, expon, expon_expon; + Xsig accumulator, expon_accum, yaccum; + char sign; + FPU_REG x; + + + exponent = arg->exp - EXP_BIAS; + + /* From arg, make a number > sqrt(2)/2 and < sqrt(2) */ + if ( arg->sigh > (unsigned)0xb504f334 ) + { + /* Treat as sqrt(2)/2 < arg < 1 */ + significand(&x) = - significand(arg); + x.sign = SIGN_NEG; + x.tag = TW_Valid; + x.exp = EXP_BIAS-1; + exponent++; + normalize(&x); + } + else + { + /* Treat as 1 <= arg < sqrt(2) */ + x.sigh = arg->sigh - 0x80000000; + x.sigl = arg->sigl; + x.sign = SIGN_POS; + x.tag = TW_Valid; + x.exp = EXP_BIAS; + normalize(&x); + } + + if ( x.tag == TW_Zero ) + { + expon = 0; + accumulator.msw = accumulator.midw = accumulator.lsw = 0; + } + else + { + log2_kernel(&x, &accumulator, &expon); + } + + sign = exponent < 0; + if ( sign ) exponent = -exponent; + expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0; + if ( exponent ) + { + expon_expon = 31 + norm_Xsig(&expon_accum); + shr_Xsig(&accumulator, expon_expon - expon); + + if ( sign ^ (x.sign == SIGN_NEG) ) + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &expon_accum); + } + else + { + expon_expon = expon; + sign = x.sign; + } + + yaccum.lsw = 0; XSIG_LL(yaccum) = significand(y); + mul_Xsig_Xsig(&accumulator, &yaccum); + + expon_expon += round_Xsig(&accumulator); + + if ( accumulator.msw == 0 ) + { + reg_move(&CONST_Z, y); + } + else + { + result->exp = expon_expon + y->exp + 1; + significand(result) = XSIG_LL(accumulator); + result->tag = TW_Valid; /* set the tags to Valid */ + result->sign = sign ^ y->sign; + } + + return; +} + + +/*--- poly_l2p1() -----------------------------------------------------------+ + | Base 2 logarithm by a polynomial approximation. | + | log2(x+1) | + +---------------------------------------------------------------------------*/ +int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result) +{ + char sign; + long int exponent; + Xsig accumulator, yaccum; + + + sign = arg->sign; + + if ( arg->exp < EXP_BIAS ) + { + log2_kernel(arg, &accumulator, &exponent); + + yaccum.lsw = 0; + XSIG_LL(yaccum) = significand(y); + mul_Xsig_Xsig(&accumulator, &yaccum); + + exponent += round_Xsig(&accumulator); + + result->exp = exponent + y->exp + 1; + significand(result) = XSIG_LL(accumulator); + result->tag = TW_Valid; /* set the tags to Valid */ + result->sign = sign ^ y->sign; + + return 0; + } + else + { + /* The magnitude of arg is far too large. */ + reg_move(y, result); + if ( sign != SIGN_POS ) + { + /* Trying to get the log of a negative number. */ + return 1; + } + else + { + return 0; + } + } + +} + + + + +#undef HIPOWER +#define HIPOWER 10 +static const unsigned long long logterms[HIPOWER] = +{ + 0x2a8eca5705fc2ef0LL, + 0xf6384ee1d01febceLL, + 0x093bb62877cdf642LL, + 0x006985d8a9ec439bLL, + 0x0005212c4f55a9c8LL, + 0x00004326a16927f0LL, + 0x0000038d1d80a0e7LL, + 0x0000003141cc80c6LL, + 0x00000002b1668c9fLL, + 0x000000002c7a46aaLL +}; + +static const unsigned long leadterm = 0xb8000000; + + +/*--- log2_kernel() ---------------------------------------------------------+ + | Base 2 logarithm by a polynomial approximation. | + | log2(x+1) | + +---------------------------------------------------------------------------*/ +static void log2_kernel(FPU_REG const *arg, Xsig *accum_result, + long int *expon) +{ + char sign; + long int exponent, adj; + unsigned long long Xsq; + Xsig accumulator, Numer, Denom, argSignif, arg_signif; + + sign = arg->sign; + + exponent = arg->exp - EXP_BIAS; + Numer.lsw = Denom.lsw = 0; + XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg); + if ( sign == SIGN_POS ) + { + shr_Xsig(&Denom, 2 - (1 + exponent)); + Denom.msw |= 0x80000000; + div_Xsig(&Numer, &Denom, &argSignif); + } + else + { + shr_Xsig(&Denom, 1 - (1 + exponent)); + negate_Xsig(&Denom); + if ( Denom.msw & 0x80000000 ) + { + div_Xsig(&Numer, &Denom, &argSignif); + exponent ++; + } + else + { + /* Denom must be 1.0 */ + argSignif.lsw = Numer.lsw; argSignif.midw = Numer.midw; + argSignif.msw = Numer.msw; + } + } + +#ifndef PECULIAR_486 + /* Should check here that |local_arg| is within the valid range */ + if ( exponent >= -2 ) + { + if ( (exponent > -2) || + (argSignif.msw > (unsigned)0xafb0ccc0) ) + { + /* The argument is too large */ + } + } +#endif PECULIAR_486 + + arg_signif.lsw = argSignif.lsw; XSIG_LL(arg_signif) = XSIG_LL(argSignif); + adj = norm_Xsig(&argSignif); + accumulator.lsw = argSignif.lsw; XSIG_LL(accumulator) = XSIG_LL(argSignif); + mul_Xsig_Xsig(&accumulator, &accumulator); + shr_Xsig(&accumulator, 2*(-1 - (1 + exponent + adj))); + Xsq = XSIG_LL(accumulator); + if ( accumulator.lsw & 0x80000000 ) + Xsq++; + + accumulator.msw = accumulator.midw = accumulator.lsw = 0; + /* Do the basic fixed point polynomial evaluation */ + polynomial_Xsig(&accumulator, &Xsq, logterms, HIPOWER-1); + + mul_Xsig_Xsig(&accumulator, &argSignif); + shr_Xsig(&accumulator, 6 - adj); + + mul32_Xsig(&arg_signif, leadterm); + add_two_Xsig(&accumulator, &arg_signif, &exponent); + + *expon = exponent + 1; + accum_result->lsw = accumulator.lsw; + accum_result->midw = accumulator.midw; + accum_result->msw = accumulator.msw; + +} diff --git a/arch/i386/math-emu/poly_sin.c b/arch/i386/math-emu/poly_sin.c new file mode 100644 index 000000000..03db5b6aa --- /dev/null +++ b/arch/i386/math-emu/poly_sin.c @@ -0,0 +1,408 @@ +/*---------------------------------------------------------------------------+ + | poly_sin.c | + | | + | Computation of an approximation of the sin function and the cosine | + | function by a polynomial. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "poly.h" + + +#define N_COEFF_P 4 +#define N_COEFF_N 4 + +static const unsigned long long pos_terms_l[N_COEFF_P] = +{ + 0xaaaaaaaaaaaaaaabLL, + 0x00d00d00d00cf906LL, + 0x000006b99159a8bbLL, + 0x000000000d7392e6LL +}; + +static const unsigned long long neg_terms_l[N_COEFF_N] = +{ + 0x2222222222222167LL, + 0x0002e3bc74aab624LL, + 0x0000000b09229062LL, + 0x00000000000c7973LL +}; + + + +#define N_COEFF_PH 4 +#define N_COEFF_NH 4 +static const unsigned long long pos_terms_h[N_COEFF_PH] = +{ + 0x0000000000000000LL, + 0x05b05b05b05b0406LL, + 0x000049f93edd91a9LL, + 0x00000000c9c9ed62LL +}; + +static const unsigned long long neg_terms_h[N_COEFF_NH] = +{ + 0xaaaaaaaaaaaaaa98LL, + 0x001a01a01a019064LL, + 0x0000008f76c68a77LL, + 0x0000000000d58f5eLL +}; + + +/*--- poly_sine() -----------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void poly_sine(FPU_REG const *arg, FPU_REG *result) +{ + int exponent, echange; + Xsig accumulator, argSqrd, argTo4; + unsigned long fix_up, adj; + unsigned long long fixed_arg; + + +#ifdef PARANOID + if ( arg->tag == TW_Zero ) + { + /* Return 0.0 */ + reg_move(&CONST_Z, result); + return; + } +#endif PARANOID + + exponent = arg->exp - EXP_BIAS; + + accumulator.lsw = accumulator.midw = accumulator.msw = 0; + + /* Split into two ranges, for arguments below and above 1.0 */ + /* The boundary between upper and lower is approx 0.88309101259 */ + if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xe21240aa)) ) + { + /* The argument is <= 0.88309101259 */ + + argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &significand(arg)); + shr_Xsig(&argSqrd, 2*(-1-exponent)); + argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; + argTo4.lsw = argSqrd.lsw; + mul_Xsig_Xsig(&argTo4, &argTo4); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l, + N_COEFF_N-1); + mul_Xsig_Xsig(&accumulator, &argSqrd); + negate_Xsig(&accumulator); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l, + N_COEFF_P-1); + + shr_Xsig(&accumulator, 2); /* Divide by four */ + accumulator.msw |= 0x80000000; /* Add 1.0 */ + + mul64_Xsig(&accumulator, &significand(arg)); + mul64_Xsig(&accumulator, &significand(arg)); + mul64_Xsig(&accumulator, &significand(arg)); + + /* Divide by four, FPU_REG compatible, etc */ + exponent = 3*exponent + EXP_BIAS; + + /* The minimum exponent difference is 3 */ + shr_Xsig(&accumulator, arg->exp - exponent); + + negate_Xsig(&accumulator); + XSIG_LL(accumulator) += significand(arg); + + echange = round_Xsig(&accumulator); + + result->exp = arg->exp + echange; + } + else + { + /* The argument is > 0.88309101259 */ + /* We use sin(arg) = cos(pi/2-arg) */ + + fixed_arg = significand(arg); + + if ( exponent == 0 ) + { + /* The argument is >= 1.0 */ + + /* Put the binary point at the left. */ + fixed_arg <<= 1; + } + /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ + fixed_arg = 0x921fb54442d18469LL - fixed_arg; + + XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &fixed_arg); + + XSIG_LL(argTo4) = XSIG_LL(argSqrd); argTo4.lsw = argSqrd.lsw; + mul_Xsig_Xsig(&argTo4, &argTo4); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h, + N_COEFF_NH-1); + mul_Xsig_Xsig(&accumulator, &argSqrd); + negate_Xsig(&accumulator); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h, + N_COEFF_PH-1); + negate_Xsig(&accumulator); + + mul64_Xsig(&accumulator, &fixed_arg); + mul64_Xsig(&accumulator, &fixed_arg); + + shr_Xsig(&accumulator, 3); + negate_Xsig(&accumulator); + + add_Xsig_Xsig(&accumulator, &argSqrd); + + shr_Xsig(&accumulator, 1); + + accumulator.lsw |= 1; /* A zero accumulator here would cause problems */ + negate_Xsig(&accumulator); + + /* The basic computation is complete. Now fix the answer to + compensate for the error due to the approximation used for + pi/2 + */ + + /* This has an exponent of -65 */ + fix_up = 0x898cc517; + /* The fix-up needs to be improved for larger args */ + if ( argSqrd.msw & 0xffc00000 ) + { + /* Get about 32 bit precision in these: */ + mul_32_32(0x898cc517, argSqrd.msw, &adj); + fix_up -= adj/6; + } + mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up); + + adj = accumulator.lsw; /* temp save */ + accumulator.lsw -= fix_up; + if ( accumulator.lsw > adj ) + XSIG_LL(accumulator) --; + + echange = round_Xsig(&accumulator); + + result->exp = EXP_BIAS - 1 + echange; + } + + significand(result) = XSIG_LL(accumulator); + result->tag = TW_Valid; + result->sign = arg->sign; + +#ifdef PARANOID + if ( (result->exp >= EXP_BIAS) + && (significand(result) > 0x8000000000000000LL) ) + { + EXCEPTION(EX_INTERNAL|0x150); + } +#endif PARANOID + +} + + + +/*--- poly_cos() ------------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void poly_cos(FPU_REG const *arg, FPU_REG *result) +{ + long int exponent, exp2, echange; + Xsig accumulator, argSqrd, fix_up, argTo4; + unsigned long adj; + unsigned long long fixed_arg; + + +#ifdef PARANOID + if ( arg->tag == TW_Zero ) + { + /* Return 1.0 */ + reg_move(&CONST_1, result); + return; + } + + if ( (arg->exp > EXP_BIAS) + || ((arg->exp == EXP_BIAS) + && (significand(arg) > 0xc90fdaa22168c234LL)) ) + { + EXCEPTION(EX_Invalid); + reg_move(&CONST_QNaN, result); + return; + } +#endif PARANOID + + exponent = arg->exp - EXP_BIAS; + + accumulator.lsw = accumulator.midw = accumulator.msw = 0; + + if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xb00d6f54)) ) + { + /* arg is < 0.687705 */ + + argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &significand(arg)); + + if ( exponent < -1 ) + { + /* shift the argument right by the required places */ + shr_Xsig(&argSqrd, 2*(-1-exponent)); + } + + argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; + argTo4.lsw = argSqrd.lsw; + mul_Xsig_Xsig(&argTo4, &argTo4); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h, + N_COEFF_NH-1); + mul_Xsig_Xsig(&accumulator, &argSqrd); + negate_Xsig(&accumulator); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h, + N_COEFF_PH-1); + negate_Xsig(&accumulator); + + mul64_Xsig(&accumulator, &significand(arg)); + mul64_Xsig(&accumulator, &significand(arg)); + shr_Xsig(&accumulator, -2*(1+exponent)); + + shr_Xsig(&accumulator, 3); + negate_Xsig(&accumulator); + + add_Xsig_Xsig(&accumulator, &argSqrd); + + shr_Xsig(&accumulator, 1); + + /* It doesn't matter if accumulator is all zero here, the + following code will work ok */ + negate_Xsig(&accumulator); + + if ( accumulator.lsw & 0x80000000 ) + XSIG_LL(accumulator) ++; + if ( accumulator.msw == 0 ) + { + /* The result is 1.0 */ + reg_move(&CONST_1, result); + } + else + { + significand(result) = XSIG_LL(accumulator); + + /* will be a valid positive nr with expon = -1 */ + *(short *)&(result->sign) = 0; + result->exp = EXP_BIAS - 1; + } + } + else + { + fixed_arg = significand(arg); + + if ( exponent == 0 ) + { + /* The argument is >= 1.0 */ + + /* Put the binary point at the left. */ + fixed_arg <<= 1; + } + /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ + fixed_arg = 0x921fb54442d18469LL - fixed_arg; + + exponent = -1; + exp2 = -1; + + /* A shift is needed here only for a narrow range of arguments, + i.e. for fixed_arg approx 2^-32, but we pick up more... */ + if ( !(LL_MSW(fixed_arg) & 0xffff0000) ) + { + fixed_arg <<= 16; + exponent -= 16; + exp2 -= 16; + } + + XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &fixed_arg); + + if ( exponent < -1 ) + { + /* shift the argument right by the required places */ + shr_Xsig(&argSqrd, 2*(-1-exponent)); + } + + argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; + argTo4.lsw = argSqrd.lsw; + mul_Xsig_Xsig(&argTo4, &argTo4); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l, + N_COEFF_N-1); + mul_Xsig_Xsig(&accumulator, &argSqrd); + negate_Xsig(&accumulator); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l, + N_COEFF_P-1); + + shr_Xsig(&accumulator, 2); /* Divide by four */ + accumulator.msw |= 0x80000000; /* Add 1.0 */ + + mul64_Xsig(&accumulator, &fixed_arg); + mul64_Xsig(&accumulator, &fixed_arg); + mul64_Xsig(&accumulator, &fixed_arg); + + /* Divide by four, FPU_REG compatible, etc */ + exponent = 3*exponent; + + /* The minimum exponent difference is 3 */ + shr_Xsig(&accumulator, exp2 - exponent); + + negate_Xsig(&accumulator); + XSIG_LL(accumulator) += fixed_arg; + + /* The basic computation is complete. Now fix the answer to + compensate for the error due to the approximation used for + pi/2 + */ + + /* This has an exponent of -65 */ + XSIG_LL(fix_up) = 0x898cc51701b839a2ll; + fix_up.lsw = 0; + + /* The fix-up needs to be improved for larger args */ + if ( argSqrd.msw & 0xffc00000 ) + { + /* Get about 32 bit precision in these: */ + mul_32_32(0x898cc517, argSqrd.msw, &adj); + fix_up.msw -= adj/2; + mul_32_32(0x898cc517, argTo4.msw, &adj); + fix_up.msw += adj/24; + } + + exp2 += norm_Xsig(&accumulator); + shr_Xsig(&accumulator, 1); /* Prevent overflow */ + exp2++; + shr_Xsig(&fix_up, 65 + exp2); + + add_Xsig_Xsig(&accumulator, &fix_up); + + echange = round_Xsig(&accumulator); + + result->exp = exp2 + EXP_BIAS + echange; + *(short *)&(result->sign) = 0; /* Is a valid positive nr */ + significand(result) = XSIG_LL(accumulator); + } + +#ifdef PARANOID + if ( (result->exp >= EXP_BIAS) + && (significand(result) > 0x8000000000000000LL) ) + { + EXCEPTION(EX_INTERNAL|0x151); + } +#endif PARANOID + +} diff --git a/arch/i386/math-emu/poly_tan.c b/arch/i386/math-emu/poly_tan.c new file mode 100644 index 000000000..d9b09e438 --- /dev/null +++ b/arch/i386/math-emu/poly_tan.c @@ -0,0 +1,213 @@ +/*---------------------------------------------------------------------------+ + | poly_tan.c | + | | + | Compute the tan of a FPU_REG, using a polynomial approximation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "poly.h" + + +#define HiPOWERop 3 /* odd poly, positive terms */ +static const unsigned long long oddplterm[HiPOWERop] = +{ + 0x0000000000000000LL, + 0x0051a1cf08fca228LL, + 0x0000000071284ff7LL +}; + +#define HiPOWERon 2 /* odd poly, negative terms */ +static const unsigned long long oddnegterm[HiPOWERon] = +{ + 0x1291a9a184244e80LL, + 0x0000583245819c21LL +}; + +#define HiPOWERep 2 /* even poly, positive terms */ +static const unsigned long long evenplterm[HiPOWERep] = +{ + 0x0e848884b539e888LL, + 0x00003c7f18b887daLL +}; + +#define HiPOWERen 2 /* even poly, negative terms */ +static const unsigned long long evennegterm[HiPOWERen] = +{ + 0xf1f0200fd51569ccLL, + 0x003afb46105c4432LL +}; + +static const unsigned long long twothirds = 0xaaaaaaaaaaaaaaabLL; + + +/*--- poly_tan() ------------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void poly_tan(FPU_REG const *arg, FPU_REG *result) +{ + long int exponent; + int invert; + Xsig argSq, argSqSq, accumulatoro, accumulatore, accum, + argSignif, fix_up; + unsigned long adj; + + exponent = arg->exp - EXP_BIAS; + +#ifdef PARANOID + if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */ + { arith_invalid(result); return; } /* Need a positive number */ +#endif PARANOID + + /* Split the problem into two domains, smaller and larger than pi/4 */ + if ( (exponent == 0) || ((exponent == -1) && (arg->sigh > 0xc90fdaa2)) ) + { + /* The argument is greater than (approx) pi/4 */ + invert = 1; + accum.lsw = 0; + XSIG_LL(accum) = significand(arg); + + if ( exponent == 0 ) + { + /* The argument is >= 1.0 */ + /* Put the binary point at the left. */ + XSIG_LL(accum) <<= 1; + } + /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ + XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum); + + argSignif.lsw = accum.lsw; + XSIG_LL(argSignif) = XSIG_LL(accum); + exponent = -1 + norm_Xsig(&argSignif); + } + else + { + invert = 0; + argSignif.lsw = 0; + XSIG_LL(accum) = XSIG_LL(argSignif) = significand(arg); + + if ( exponent < -1 ) + { + /* shift the argument right by the required places */ + if ( shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U ) + XSIG_LL(accum) ++; /* round up */ + } + } + + XSIG_LL(argSq) = XSIG_LL(accum); argSq.lsw = accum.lsw; + mul_Xsig_Xsig(&argSq, &argSq); + XSIG_LL(argSqSq) = XSIG_LL(argSq); argSqSq.lsw = argSq.lsw; + mul_Xsig_Xsig(&argSqSq, &argSqSq); + + /* Compute the negative terms for the numerator polynomial */ + accumulatoro.msw = accumulatoro.midw = accumulatoro.lsw = 0; + polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddnegterm, HiPOWERon-1); + mul_Xsig_Xsig(&accumulatoro, &argSq); + negate_Xsig(&accumulatoro); + /* Add the positive terms */ + polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddplterm, HiPOWERop-1); + + + /* Compute the positive terms for the denominator polynomial */ + accumulatore.msw = accumulatore.midw = accumulatore.lsw = 0; + polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evenplterm, HiPOWERep-1); + mul_Xsig_Xsig(&accumulatore, &argSq); + negate_Xsig(&accumulatore); + /* Add the negative terms */ + polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evennegterm, HiPOWERen-1); + /* Multiply by arg^2 */ + mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); + mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); + /* de-normalize and divide by 2 */ + shr_Xsig(&accumulatore, -2*(1+exponent) + 1); + negate_Xsig(&accumulatore); /* This does 1 - accumulator */ + + /* Now find the ratio. */ + if ( accumulatore.msw == 0 ) + { + /* accumulatoro must contain 1.0 here, (actually, 0) but it + really doesn't matter what value we use because it will + have negligible effect in later calculations + */ + XSIG_LL(accum) = 0x8000000000000000LL; + accum.lsw = 0; + } + else + { + div_Xsig(&accumulatoro, &accumulatore, &accum); + } + + /* Multiply by 1/3 * arg^3 */ + mul64_Xsig(&accum, &XSIG_LL(argSignif)); + mul64_Xsig(&accum, &XSIG_LL(argSignif)); + mul64_Xsig(&accum, &XSIG_LL(argSignif)); + mul64_Xsig(&accum, &twothirds); + shr_Xsig(&accum, -2*(exponent+1)); + + /* tan(arg) = arg + accum */ + add_two_Xsig(&accum, &argSignif, &exponent); + + if ( invert ) + { + /* We now have the value of tan(pi_2 - arg) where pi_2 is an + approximation for pi/2 + */ + /* The next step is to fix the answer to compensate for the + error due to the approximation used for pi/2 + */ + + /* This is (approx) delta, the error in our approx for pi/2 + (see above). It has an exponent of -65 + */ + XSIG_LL(fix_up) = 0x898cc51701b839a2LL; + fix_up.lsw = 0; + + if ( exponent == 0 ) + adj = 0xffffffff; /* We want approx 1.0 here, but + this is close enough. */ + else if ( exponent > -30 ) + { + adj = accum.msw >> -(exponent+1); /* tan */ + mul_32_32(adj, adj, &adj); /* tan^2 */ + } + else + adj = 0; + mul_32_32(0x898cc517, adj, &adj); /* delta * tan^2 */ + + fix_up.msw += adj; + if ( !(fix_up.msw & 0x80000000) ) /* did fix_up overflow ? */ + { + /* Yes, we need to add an msb */ + shr_Xsig(&fix_up, 1); + fix_up.msw |= 0x80000000; + shr_Xsig(&fix_up, 64 + exponent); + } + else + shr_Xsig(&fix_up, 65 + exponent); + + add_two_Xsig(&accum, &fix_up, &exponent); + + /* accum now contains tan(pi/2 - arg). + Use tan(arg) = 1.0 / tan(pi/2 - arg) + */ + accumulatoro.lsw = accumulatoro.midw = 0; + accumulatoro.msw = 0x80000000; + div_Xsig(&accumulatoro, &accum, &accum); + exponent = - exponent - 1; + } + + /* Transfer the result */ + round_Xsig(&accum); + *(short *)&(result->sign) = 0; + significand(result) = XSIG_LL(accum); + result->exp = EXP_BIAS + exponent; + +} diff --git a/arch/i386/math-emu/polynom_Xsig.S b/arch/i386/math-emu/polynom_Xsig.S new file mode 100644 index 000000000..585221f96 --- /dev/null +++ b/arch/i386/math-emu/polynom_Xsig.S @@ -0,0 +1,137 @@ +/*---------------------------------------------------------------------------+ + | polynomial_Xsig.S | + | | + | Fixed point arithmetic polynomial evaluation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void polynomial_Xsig(Xsig *accum, unsigned long long x, | + | unsigned long long terms[], int n) | + | | + | Computes: | + | terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x | + | and adds the result to the 12 byte Xsig. | + | The terms[] are each 8 bytes, but all computation is performed to 12 byte | + | precision. | + | | + | This function must be used carefully: most overflow of intermediate | + | results is controlled, but overflow of the result is not. | + | | + +---------------------------------------------------------------------------*/ + .file "polynomial_Xsig.S" + +#include "fpu_asm.h" + + +#define TERM_SIZE $8 +#define SUM_MS -20(%ebp) /* sum ms long */ +#define SUM_MIDDLE -24(%ebp) /* sum middle long */ +#define SUM_LS -28(%ebp) /* sum ls long */ +#define ACCUM_MS -4(%ebp) /* accum ms long */ +#define ACCUM_MIDDLE -8(%ebp) /* accum middle long */ +#define ACCUM_LS -12(%ebp) /* accum ls long */ +#define OVERFLOWED -16(%ebp) /* addition overflow flag */ + +.text + .align 2,144 +.globl _polynomial_Xsig +_polynomial_Xsig: + pushl %ebp + movl %esp,%ebp + subl $32,%esp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM2,%esi /* x */ + movl PARAM3,%edi /* terms */ + + movl TERM_SIZE,%eax + mull PARAM4 /* n */ + addl %eax,%edi + + movl 4(%edi),%edx /* terms[n] */ + movl %edx,SUM_MS + movl (%edi),%edx /* terms[n] */ + movl %edx,SUM_MIDDLE + xor %eax,%eax + movl %eax,SUM_LS + movb %al,OVERFLOWED + + subl TERM_SIZE,%edi + decl PARAM4 + js L_accum_done + +L_accum_loop: + xor %eax,%eax + movl %eax,ACCUM_MS + movl %eax,ACCUM_MIDDLE + + movl SUM_MIDDLE,%eax + mull (%esi) /* x ls long */ + movl %edx,ACCUM_LS + + movl SUM_MIDDLE,%eax + mull 4(%esi) /* x ms long */ + addl %eax,ACCUM_LS + adcl %edx,ACCUM_MIDDLE + adcl $0,ACCUM_MS + + movl SUM_MS,%eax + mull (%esi) /* x ls long */ + addl %eax,ACCUM_LS + adcl %edx,ACCUM_MIDDLE + adcl $0,ACCUM_MS + + movl SUM_MS,%eax + mull 4(%esi) /* x ms long */ + addl %eax,ACCUM_MIDDLE + adcl %edx,ACCUM_MS + + testb $0xff,OVERFLOWED + jz L_no_overflow + + movl (%esi),%eax + addl %eax,ACCUM_MIDDLE + movl 4(%esi),%eax + adcl %eax,ACCUM_MS /* This could overflow too */ + +L_no_overflow: + +/* + * Now put the sum of next term and the accumulator + * into the sum register + */ + movl ACCUM_LS,%eax + addl (%edi),%eax /* term ls long */ + movl %eax,SUM_LS + movl ACCUM_MIDDLE,%eax + adcl (%edi),%eax /* term ls long */ + movl %eax,SUM_MIDDLE + movl ACCUM_MS,%eax + adcl 4(%edi),%eax /* term ms long */ + movl %eax,SUM_MS + sbbb %al,%al + movb %al,OVERFLOWED /* Used in the next iteration */ + + subl TERM_SIZE,%edi + decl PARAM4 + jns L_accum_loop + +L_accum_done: + movl PARAM1,%edi /* accum */ + movl SUM_LS,%eax + addl %eax,(%edi) + movl SUM_MIDDLE,%eax + adcl %eax,4(%edi) + movl SUM_MS,%eax + adcl %eax,8(%edi) + + popl %ebx + popl %edi + popl %esi + leave + ret diff --git a/arch/i386/math-emu/reg_add_sub.c b/arch/i386/math-emu/reg_add_sub.c new file mode 100644 index 000000000..d70889b40 --- /dev/null +++ b/arch/i386/math-emu/reg_add_sub.c @@ -0,0 +1,318 @@ +/*---------------------------------------------------------------------------+ + | reg_add_sub.c | + | | + | Functions to add or subtract two registers and put the result in a third. | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | For each function, the destination may be any FPU_REG, including one of | + | the source FPU_REGs. | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "fpu_system.h" + + +int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w) +{ + char saved_sign = dest->sign; + int diff; + + if ( !(a->tag | b->tag) ) + { + /* Both registers are valid */ + if (!(a->sign ^ b->sign)) + { + /* signs are the same */ + dest->sign = a->sign; + if ( reg_u_add(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + return 0; + } + + /* The signs are different, so do a subtraction */ + diff = a->exp - b->exp; + if (!diff) + { + diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ + if (!diff) + { + diff = a->sigl > b->sigl; + if (!diff) + diff = -(a->sigl < b->sigl); + } + } + + if (diff > 0) + { + dest->sign = a->sign; + if ( reg_u_sub(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + } + else if ( diff == 0 ) + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(&CONST_Z, dest); + /* sign depends upon rounding mode */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + else + { + dest->sign = b->sign; + if ( reg_u_sub(b, a, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + } + return 0; + } + else + { + if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) + { return real_2op_NaN(a, b, dest); } + else if (a->tag == TW_Zero) + { + if (b->tag == TW_Zero) + { + char different_signs = a->sign ^ b->sign; + /* Both are zero, result will be zero. */ + reg_move(a, dest); + if (different_signs) + { + /* Signs are different. */ + /* Sign of answer depends upon rounding mode. */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + } + else + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); + } + return 0; + } + else if (b->tag == TW_Zero) + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(a, dest); return 0; + } + else if (a->tag == TW_Infinity) + { + if (b->tag != TW_Infinity) + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(a, dest); return 0; + } + if (a->sign == b->sign) + { + /* They are both + or - infinity */ + reg_move(a, dest); return 0; + } + return arith_invalid(dest); /* Infinity-Infinity is undefined. */ + } + else if (b->tag == TW_Infinity) + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); return 0; + } + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL|0x101); +#endif + return 1; +} + + +/* Subtract b from a. (a-b) -> dest */ +int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w) +{ + char saved_sign = dest->sign; + int diff; + + if ( !(a->tag | b->tag) ) + { + /* Both registers are valid */ + diff = a->exp - b->exp; + if (!diff) + { + diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ + if (!diff) + { + diff = a->sigl > b->sigl; + if (!diff) + diff = -(a->sigl < b->sigl); + } + } + + switch (a->sign*2 + b->sign) + { + case 0: /* P - P */ + case 3: /* N - N */ + if (diff > 0) + { + /* |a| > |b| */ + dest->sign = a->sign; + if ( reg_u_sub(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + return 0; + } + else if ( diff == 0 ) + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(&CONST_Z, dest); + /* sign depends upon rounding mode */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + else + { + dest->sign = a->sign ^ SIGN_POS^SIGN_NEG; + if ( reg_u_sub(b, a, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + } + break; + case 1: /* P - N */ + dest->sign = SIGN_POS; + if ( reg_u_add(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + break; + case 2: /* N - P */ + dest->sign = SIGN_NEG; + if ( reg_u_add(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + break; + } + return 0; + } + else + { + if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) + { return real_2op_NaN(b, a, dest); } + else if (b->tag == TW_Zero) + { + if (a->tag == TW_Zero) + { + char same_signs = !(a->sign ^ b->sign); + /* Both are zero, result will be zero. */ + reg_move(a, dest); /* Answer for different signs. */ + if (same_signs) + { + /* Sign depends upon rounding mode */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + } + else + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(a, dest); + } + return 0; + } + else if (a->tag == TW_Zero) + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); + dest->sign ^= SIGN_POS^SIGN_NEG; + return 0; + } + else if (a->tag == TW_Infinity) + { + if (b->tag != TW_Infinity) + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(a, dest); return 0; + } + /* Both args are Infinity */ + if (a->sign == b->sign) + { + /* Infinity-Infinity is undefined. */ + return arith_invalid(dest); + } + reg_move(a, dest); + return 0; + } + else if (b->tag == TW_Infinity) + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); + dest->sign ^= SIGN_POS^SIGN_NEG; + return 0; + } + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL|0x110); +#endif + return 1; +} + diff --git a/arch/i386/math-emu/reg_compare.c b/arch/i386/math-emu/reg_compare.c new file mode 100644 index 000000000..eb4a1fa99 --- /dev/null +++ b/arch/i386/math-emu/reg_compare.c @@ -0,0 +1,378 @@ +/*---------------------------------------------------------------------------+ + | reg_compare.c | + | | + | Compare two floating point registers | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | compare() is the core FPU_REG comparison function | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "status_w.h" + + +int compare(FPU_REG const *b) +{ + int diff; + char st0_tag; + FPU_REG *st0_ptr; + + st0_ptr = &st(0); + st0_tag = st0_ptr->tag; + + if ( st0_tag | b->tag ) + { + if ( st0_tag == TW_Zero ) + { + if ( b->tag == TW_Zero ) return COMP_A_eq_B; + if ( b->tag == TW_Valid ) + { + return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) +#ifdef DENORM_OPERAND + | ((b->exp <= EXP_UNDER) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + } + else if ( b->tag == TW_Zero ) + { + if ( st0_tag == TW_Valid ) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B + : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | ((st0_ptr->exp <= EXP_UNDER ) + ? COMP_Denormal : 0 ) +#endif DENORM_OPERAND + ; + } + } + + if ( st0_tag == TW_Infinity ) + { + if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) ) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B + : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0 ) +#endif DENORM_OPERAND +; + } + else if ( b->tag == TW_Infinity ) + { + /* The 80486 book says that infinities can be equal! */ + return (st0_ptr->sign == b->sign) ? COMP_A_eq_B : + ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); + } + /* Fall through to the NaN code */ + } + else if ( b->tag == TW_Infinity ) + { + if ( (st0_tag == TW_Valid) || (st0_tag == TW_Zero) ) + { + return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) +#ifdef DENORM_OPERAND + | (((st0_tag == TW_Valid) + && (st0_ptr->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + /* Fall through to the NaN code */ + } + + /* The only possibility now should be that one of the arguments + is a NaN */ + if ( (st0_tag == TW_NaN) || (b->tag == TW_NaN) ) + { + if ( ((st0_tag == TW_NaN) && !(st0_ptr->sigh & 0x40000000)) + || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) ) + /* At least one arg is a signaling NaN */ + return COMP_No_Comp | COMP_SNaN | COMP_NaN; + else + /* Neither is a signaling NaN */ + return COMP_No_Comp | COMP_NaN; + } + + EXCEPTION(EX_Invalid); + } + +#ifdef PARANOID + if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); + if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); +#endif PARANOID + + + if (st0_ptr->sign != b->sign) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + + diff = st0_ptr->exp - b->exp; + if ( diff == 0 ) + { + diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are + identical */ + if ( diff == 0 ) + { + diff = st0_ptr->sigl > b->sigl; + if ( diff == 0 ) + diff = -(st0_ptr->sigl < b->sigl); + } + } + + if ( diff > 0 ) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + if ( diff < 0 ) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) +#ifdef DENORM_OPERAND + | + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + + return COMP_A_eq_B +#ifdef DENORM_OPERAND + | + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + +} + + +/* This function requires that st(0) is not empty */ +int compare_st_data(FPU_REG const *loaded_data) +{ + int f, c; + + c = compare(loaded_data); + + if (c & COMP_NaN) + { + EXCEPTION(EX_Invalid); + f = SW_C3 | SW_C2 | SW_C0; + } + else + switch (c & 7) + { + case COMP_A_lt_B: + f = SW_C0; + break; + case COMP_A_eq_B: + f = SW_C3; + break; + case COMP_A_gt_B: + f = 0; + break; + case COMP_No_Comp: + f = SW_C3 | SW_C2 | SW_C0; + break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x121); + f = SW_C3 | SW_C2 | SW_C0; + break; +#endif PARANOID + } + setcc(f); + if (c & COMP_Denormal) + { + return denormal_operand(); + } + return 0; +} + + +static int compare_st_st(int nr) +{ + int f, c; + + if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) + { + setcc(SW_C3 | SW_C2 | SW_C0); + /* Stack fault */ + EXCEPTION(EX_StackUnder); + return !(control_word & CW_Invalid); + } + + c = compare(&st(nr)); + if (c & COMP_NaN) + { + setcc(SW_C3 | SW_C2 | SW_C0); + EXCEPTION(EX_Invalid); + return !(control_word & CW_Invalid); + } + else + switch (c & 7) + { + case COMP_A_lt_B: + f = SW_C0; + break; + case COMP_A_eq_B: + f = SW_C3; + break; + case COMP_A_gt_B: + f = 0; + break; + case COMP_No_Comp: + f = SW_C3 | SW_C2 | SW_C0; + break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x122); + f = SW_C3 | SW_C2 | SW_C0; + break; +#endif PARANOID + } + setcc(f); + if (c & COMP_Denormal) + { + return denormal_operand(); + } + return 0; +} + + +static int compare_u_st_st(int nr) +{ + int f, c; + + if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) + { + setcc(SW_C3 | SW_C2 | SW_C0); + /* Stack fault */ + EXCEPTION(EX_StackUnder); + return !(control_word & CW_Invalid); + } + + c = compare(&st(nr)); + if (c & COMP_NaN) + { + setcc(SW_C3 | SW_C2 | SW_C0); + if (c & COMP_SNaN) /* This is the only difference between + un-ordered and ordinary comparisons */ + { + EXCEPTION(EX_Invalid); + return !(control_word & CW_Invalid); + } + return 0; + } + else + switch (c & 7) + { + case COMP_A_lt_B: + f = SW_C0; + break; + case COMP_A_eq_B: + f = SW_C3; + break; + case COMP_A_gt_B: + f = 0; + break; + case COMP_No_Comp: + f = SW_C3 | SW_C2 | SW_C0; + break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x123); + f = SW_C3 | SW_C2 | SW_C0; + break; +#endif PARANOID + } + setcc(f); + if (c & COMP_Denormal) + { + return denormal_operand(); + } + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void fcom_st() +{ + /* fcom st(i) */ + compare_st_st(FPU_rm); +} + + +void fcompst() +{ + /* fcomp st(i) */ + if ( !compare_st_st(FPU_rm) ) + pop(); +} + + +void fcompp() +{ + /* fcompp */ + if (FPU_rm != 1) + { + FPU_illegal(); + return; + } + if ( !compare_st_st(1) ) + poppop(); +} + + +void fucom_() +{ + /* fucom st(i) */ + compare_u_st_st(FPU_rm); + +} + + +void fucomp() +{ + /* fucomp st(i) */ + if ( !compare_u_st_st(FPU_rm) ) + pop(); +} + + +void fucompp() +{ + /* fucompp */ + if (FPU_rm == 1) + { + if ( !compare_u_st_st(1) ) + poppop(); + } + else + FPU_illegal(); +} diff --git a/arch/i386/math-emu/reg_constant.c b/arch/i386/math-emu/reg_constant.c new file mode 100644 index 000000000..c1981ce24 --- /dev/null +++ b/arch/i386/math-emu/reg_constant.c @@ -0,0 +1,116 @@ +/*---------------------------------------------------------------------------+ + | reg_constant.c | + | | + | 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 | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "reg_constant.h" + + +FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS, + 0x00000000, 0x80000000 }; +FPU_REG const CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1, + 0x00000000, 0x80000000 }; +FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1, + 0x00000000, 0x80000000 }; +FPU_REG const CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1, + 0xcd1b8afe, 0xd49a784b }; +FPU_REG const CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS, + 0x5c17f0bc, 0xb8aa3b29 }; +FPU_REG const CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1, + 0x2168c235, 0xc90fdaa2 }; +FPU_REG const CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS, + 0x2168c235, 0xc90fdaa2 }; +FPU_REG const CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1, + 0x2168c235, 0xc90fdaa2 }; +FPU_REG const CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2, + 0xfbcff799, 0x9a209a84 }; +FPU_REG const CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1, + 0xd1cf79ac, 0xb17217f7 }; + +/* Extra bits to take pi/2 to more than 128 bits precision. */ +FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66, + 0xfc8f8cbb, 0xece675d1 }; + +/* Only the sign (and tag) is used in internal zeroes */ +FPU_REG const CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 }; + +/* Only the sign and significand (and tag) are used in internal NaNs */ +/* The 80486 never generates one of these +FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 }; + */ +/* This is the real indefinite QNaN */ +FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 }; + +/* Only the sign (and tag) is used in internal infinities */ +FPU_REG const CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 }; + + + +static void fld_const(FPU_REG const *c) +{ + FPU_REG *st_new_ptr; + + if ( STACK_OVERFLOW ) + { + stack_overflow(); + return; + } + push(); + reg_move(c, st_new_ptr); + clear_C1(); +} + + +static void fld1(void) +{ + fld_const(&CONST_1); +} + +static void fldl2t(void) +{ + fld_const(&CONST_L2T); +} + +static void fldl2e(void) +{ + fld_const(&CONST_L2E); +} + +static void fldpi(void) +{ + fld_const(&CONST_PI); +} + +static void fldlg2(void) +{ + fld_const(&CONST_LG2); +} + +static void fldln2(void) +{ + fld_const(&CONST_LN2); +} + +static void fldz(void) +{ + fld_const(&CONST_Z); +} + +static FUNC constants_table[] = { + fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, FPU_illegal +}; + +void fconst(void) +{ + (constants_table[FPU_rm])(); +} diff --git a/arch/i386/math-emu/reg_constant.h b/arch/i386/math-emu/reg_constant.h new file mode 100644 index 000000000..b7db97e34 --- /dev/null +++ b/arch/i386/math-emu/reg_constant.h @@ -0,0 +1,31 @@ +/*---------------------------------------------------------------------------+ + | reg_constant.h | + | | + | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _REG_CONSTANT_H_ +#define _REG_CONSTANT_H_ + +#include "fpu_emu.h" + +extern FPU_REG const CONST_1; +extern FPU_REG const CONST_2; +extern FPU_REG const CONST_HALF; +extern FPU_REG const CONST_L2T; +extern FPU_REG const CONST_L2E; +extern FPU_REG const CONST_PI; +extern FPU_REG const CONST_PI2; +extern FPU_REG const CONST_PI2extra; +extern FPU_REG const CONST_PI4; +extern FPU_REG const CONST_LG2; +extern FPU_REG const CONST_LN2; +extern FPU_REG const CONST_Z; +extern FPU_REG const CONST_PINF; +extern FPU_REG const CONST_INF; +extern FPU_REG const CONST_MINF; +extern FPU_REG const CONST_QNaN; + +#endif _REG_CONSTANT_H_ diff --git a/arch/i386/math-emu/reg_div.S b/arch/i386/math-emu/reg_div.S new file mode 100644 index 000000000..2fbc5f7c4 --- /dev/null +++ b/arch/i386/math-emu/reg_div.S @@ -0,0 +1,251 @@ + .file "reg_div.S" +/*---------------------------------------------------------------------------+ + | reg_div.S | + | | + | Divide one FPU_REG by another and put the result in a destination FPU_REG.| + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, | + | unsigned int control_word) | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" + + +.text + .align 2 + +.globl _reg_div +_reg_div: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $28,%esp /* Needed by divide_kernel */ +#endif NON_REENTRANT_FPU + + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi + movl PARAM2,%ebx + movl PARAM3,%edi + + movb TAG(%esi),%al + orb TAG(%ebx),%al + + jne L_div_special /* Not (both numbers TW_Valid) */ + +#ifdef DENORM_OPERAND +/* Check for denormals */ + cmpl EXP_UNDER,EXP(%esi) + jg xL_arg1_not_denormal + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xL_arg1_not_denormal: + cmpl EXP_UNDER,EXP(%ebx) + jg xL_arg2_not_denormal + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xL_arg2_not_denormal: +#endif DENORM_OPERAND + +/* Both arguments are TW_Valid */ + movb TW_Valid,TAG(%edi) + + movb SIGN(%esi),%cl + cmpb %cl,SIGN(%ebx) + setne (%edi) /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */ + + movl EXP(%esi),%edx + movl EXP(%ebx),%eax + subl %eax,%edx + addl EXP_BIAS,%edx + movl %edx,EXP(%edi) + + jmp _divide_kernel + + +/*-----------------------------------------------------------------------*/ +L_div_special: + cmpb TW_NaN,TAG(%esi) /* A NaN with anything to give NaN */ + je L_arg1_NaN + + cmpb TW_NaN,TAG(%ebx) /* A NaN with anything to give NaN */ + jne L_no_NaN_arg + +/* Operations on NaNs */ +L_arg1_NaN: +L_arg2_NaN: + pushl %edi /* Destination */ + pushl %esi + pushl %ebx /* Ordering is important here */ + call _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 */ + jmp LDiv_exit + +L_no_NaN_arg: + cmpb TW_Infinity,TAG(%esi) + jne L_arg1_not_inf + + cmpb TW_Infinity,TAG(%ebx) + je L_inf_inf /* invalid operation */ + + cmpb TW_Valid,TAG(%ebx) + je L_inf_valid + +#ifdef PARANOID + /* arg2 must be zero or valid */ + cmpb TW_Zero,TAG(%ebx) + ja L_unknown_tags +#endif PARANOID + + /* Note that p16-9 says that infinity/0 returns infinity */ + jmp L_copy_arg1 /* Answer is Inf */ + +L_inf_valid: +#ifdef DENORM_OPERAND + cmpl EXP_UNDER,EXP(%ebx) + jg L_copy_arg1 /* Answer is Inf */ + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit +#endif DENORM_OPERAND + + jmp L_copy_arg1 /* Answer is Inf */ + +L_arg1_not_inf: + cmpb TW_Zero,TAG(%ebx) /* Priority to div-by-zero error */ + jne L_arg2_not_zero + + cmpb TW_Zero,TAG(%esi) + je L_zero_zero /* invalid operation */ + +#ifdef PARANOID + /* arg1 must be valid */ + cmpb TW_Valid,TAG(%esi) + ja L_unknown_tags +#endif PARANOID + +/* Division by zero error */ + pushl %edi /* destination */ + movb SIGN(%esi),%al + xorb SIGN(%ebx),%al + pushl %eax /* lower 8 bits have the sign */ + call _divide_by_zero + jmp LDiv_exit + +L_arg2_not_zero: + cmpb TW_Infinity,TAG(%ebx) + jne L_arg2_not_inf + +#ifdef DENORM_OPERAND + cmpb TW_Valid,TAG(%esi) + jne L_return_zero + + cmpl EXP_UNDER,EXP(%esi) + jg L_return_zero /* Answer is zero */ + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit +#endif DENORM_OPERAND + + jmp L_return_zero /* Answer is zero */ + +L_arg2_not_inf: + +#ifdef PARANOID + cmpb TW_Zero,TAG(%esi) + jne L_unknown_tags +#endif PARANOID + + /* arg1 is zero, arg2 is not Infinity or a NaN */ + +#ifdef DENORM_OPERAND + cmpl EXP_UNDER,EXP(%ebx) + jg L_copy_arg1 /* Answer is zero */ + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit +#endif DENORM_OPERAND + +L_copy_arg1: + movb TAG(%esi),%ax + movb %ax,TAG(%edi) + movl EXP(%esi),%eax + movl %eax,EXP(%edi) + movl SIGL(%esi),%eax + movl %eax,SIGL(%edi) + movl SIGH(%esi),%eax + movl %eax,SIGH(%edi) + +LDiv_set_result_sign: + movb SIGN(%esi),%cl + cmpb %cl,SIGN(%ebx) + jne LDiv_negative_result + + movb SIGN_POS,SIGN(%edi) + xorl %eax,%eax /* Valid result */ + jmp LDiv_exit + +LDiv_negative_result: + movb SIGN_NEG,SIGN(%edi) + xorl %eax,%eax /* Valid result */ + +LDiv_exit: +#ifndef NON_REENTRANT_FPU + leal -40(%ebp),%esp +#else + leal -12(%ebp),%esp +#endif NON_REENTRANT_FPU + + popl %ebx + popl %edi + popl %esi + leave + ret + + +L_return_zero: + xorl %eax,%eax + movl %eax,SIGH(%edi) + movl %eax,SIGL(%edi) + movl EXP_UNDER,EXP(%edi) + movb TW_Zero,TAG(%edi) + jmp LDiv_set_result_sign + +#ifdef PARANOID +L_unknown_tags: + pushl EX_INTERNAL | 0x208 + call EXCEPTION + + /* Generate a NaN for unknown tags */ + movl _CONST_QNaN,%eax + movl %eax,(%edi) + movl _CONST_QNaN+4,%eax + movl %eax,SIGL(%edi) + movl _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 new file mode 100644 index 000000000..efec9e010 --- /dev/null +++ b/arch/i386/math-emu/reg_ld_str.c @@ -0,0 +1,1438 @@ +/*---------------------------------------------------------------------------+ + | reg_ld_str.c | + | | + | 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 | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ + +#include <asm/segment.h> + +#include "fpu_system.h" +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "status_w.h" + + +#define EXTENDED_Ebias 0x3fff +#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */ + +#define DOUBLE_Emax 1023 /* largest valid exponent */ +#define DOUBLE_Ebias 1023 +#define DOUBLE_Emin (-1022) /* smallest valid exponent */ + +#define SINGLE_Emax 127 /* largest valid exponent */ +#define SINGLE_Ebias 127 +#define SINGLE_Emin (-126) /* smallest valid exponent */ + +static void write_to_extended(FPU_REG *rp, char *d); + + +/* Get a long double from user memory */ +int reg_load_extended(long double *s, FPU_REG *loaded_data) +{ + unsigned long sigl, sigh, exp; + + 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); + RE_ENTRANT_CHECK_ON; + + loaded_data->tag = TW_Valid; /* Default */ + loaded_data->sigl = sigl; + loaded_data->sigh = sigh; + if (exp & 0x8000) + loaded_data->sign = SIGN_NEG; + else + loaded_data->sign = SIGN_POS; + exp &= 0x7fff; + loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS; + + if ( exp == 0 ) + { + if ( !(sigh | sigl) ) + { + loaded_data->tag = TW_Zero; + return 0; + } + /* The number is a de-normal or pseudodenormal. */ + if (sigh & 0x80000000) + { + /* Is a pseudodenormal. */ + /* Convert it for internal use. */ + /* This is non-80486 behaviour because the number + loses its 'denormal' identity. */ + loaded_data->exp++; + return 1; + } + else + { + /* Is a denormal. */ + /* Convert it for internal use. */ + loaded_data->exp++; + normalize_nuo(loaded_data); + return 0; + } + } + else if ( exp == 0x7fff ) + { + if ( !((sigh ^ 0x80000000) | sigl) ) + { + /* Matches the bit pattern for Infinity. */ + loaded_data->exp = EXP_Infinity; + loaded_data->tag = TW_Infinity; + return 0; + } + + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + if ( !(sigh & 0x80000000) ) + { + /* NaNs have the ms bit set to 1. */ + /* This is therefore an Unsupported NaN data type. */ + /* This is non 80486 behaviour */ + /* This should generate an Invalid Operand exception + later, so we convert it to a SNaN */ + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000001; + loaded_data->sign = SIGN_NEG; + return 1; + } + return 0; + } + + if ( !(sigh & 0x80000000) ) + { + /* Unsupported data type. */ + /* Valid numbers have the ms bit set to 1. */ + /* Unnormal. */ + /* Convert it for internal use. */ + /* This is non-80486 behaviour */ + /* This should generate an Invalid Operand exception + later, so we convert it to a SNaN */ + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000001; + loaded_data->sign = SIGN_NEG; + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + return 1; + } + return 0; +} + + +/* Get a double from user memory */ +int reg_load_double(double *dfloat, FPU_REG *loaded_data) +{ + int exp; + unsigned m64, l64; + + 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); + RE_ENTRANT_CHECK_ON; + + if (m64 & 0x80000000) + loaded_data->sign = SIGN_NEG; + else + loaded_data->sign = SIGN_POS; + exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias; + m64 &= 0xfffff; + if (exp > DOUBLE_Emax) + { + /* Infinity or NaN */ + if ((m64 == 0) && (l64 == 0)) + { + /* +- infinity */ + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000000; + loaded_data->exp = EXP_Infinity; + loaded_data->tag = TW_Infinity; + return 0; + } + else + { + /* Must be a signaling or quiet NaN */ + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + loaded_data->sigh = (m64 << 11) | 0x80000000; + loaded_data->sigh |= l64 >> 21; + loaded_data->sigl = l64 << 11; + return 0; /* The calling function must look for NaNs */ + } + } + else if ( exp < DOUBLE_Emin ) + { + /* Zero or de-normal */ + if ((m64 == 0) && (l64 == 0)) + { + /* Zero */ + int c = loaded_data->sign; + reg_move(&CONST_Z, loaded_data); + loaded_data->sign = c; + return 0; + } + else + { + /* De-normal */ + loaded_data->exp = DOUBLE_Emin + EXP_BIAS; + loaded_data->tag = TW_Valid; + loaded_data->sigh = m64 << 11; + loaded_data->sigh |= l64 >> 21; + loaded_data->sigl = l64 << 11; + normalize_nuo(loaded_data); + return denormal_operand(); + } + } + else + { + loaded_data->exp = exp + EXP_BIAS; + loaded_data->tag = TW_Valid; + loaded_data->sigh = (m64 << 11) | 0x80000000; + loaded_data->sigh |= l64 >> 21; + loaded_data->sigl = l64 << 11; + + return 0; + } +} + + +/* Get a float from user memory */ +int reg_load_single(float *single, FPU_REG *loaded_data) +{ + unsigned m32; + int exp; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, single, 4); + m32 = get_fs_long((unsigned long *) single); + RE_ENTRANT_CHECK_ON; + + if (m32 & 0x80000000) + loaded_data->sign = SIGN_NEG; + else + loaded_data->sign = SIGN_POS; + if (!(m32 & 0x7fffffff)) + { + /* Zero */ + int c = loaded_data->sign; + reg_move(&CONST_Z, loaded_data); + loaded_data->sign = c; + return 0; + } + exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias; + m32 = (m32 & 0x7fffff) << 8; + if ( exp < SINGLE_Emin ) + { + /* De-normals */ + loaded_data->exp = SINGLE_Emin + EXP_BIAS; + loaded_data->tag = TW_Valid; + loaded_data->sigh = m32; + loaded_data->sigl = 0; + normalize_nuo(loaded_data); + return denormal_operand(); + } + else if ( exp > SINGLE_Emax ) + { + /* Infinity or NaN */ + if ( m32 == 0 ) + { + /* +- infinity */ + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000000; + loaded_data->exp = EXP_Infinity; + loaded_data->tag = TW_Infinity; + return 0; + } + else + { + /* Must be a signaling or quiet NaN */ + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + loaded_data->sigh = m32 | 0x80000000; + loaded_data->sigl = 0; + return 0; /* The calling function must look for NaNs */ + } + } + else + { + loaded_data->exp = exp + EXP_BIAS; + loaded_data->sigh = m32 | 0x80000000; + loaded_data->sigl = 0; + loaded_data->tag = TW_Valid; + return 0; + } +} + + +/* Get a long long from user memory */ +void reg_load_int64(long long *_s, FPU_REG *loaded_data) +{ + int e; + long long s; + + 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); + RE_ENTRANT_CHECK_ON; + + if (s == 0) + { reg_move(&CONST_Z, loaded_data); return; } + + if (s > 0) + loaded_data->sign = SIGN_POS; + else + { + s = -s; + loaded_data->sign = SIGN_NEG; + } + + e = EXP_BIAS + 63; + significand(loaded_data) = s; + loaded_data->exp = e; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); +} + + +/* Get a long from user memory */ +void reg_load_int32(long *_s, FPU_REG *loaded_data) +{ + long s; + int e; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, _s, 4); + s = (long)get_fs_long((unsigned long *) _s); + RE_ENTRANT_CHECK_ON; + + if (s == 0) + { reg_move(&CONST_Z, loaded_data); return; } + + if (s > 0) + loaded_data->sign = SIGN_POS; + else + { + s = -s; + loaded_data->sign = SIGN_NEG; + } + + e = EXP_BIAS + 31; + loaded_data->sigh = s; + loaded_data->sigl = 0; + loaded_data->exp = e; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); +} + + +/* Get a short from user memory */ +void reg_load_int16(short *_s, FPU_REG *loaded_data) +{ + int s, e; + + 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); + RE_ENTRANT_CHECK_ON; + + if (s == 0) + { reg_move(&CONST_Z, loaded_data); return; } + + if (s > 0) + loaded_data->sign = SIGN_POS; + else + { + s = -s; + loaded_data->sign = SIGN_NEG; + } + + e = EXP_BIAS + 15; + loaded_data->sigh = s << 16; + + loaded_data->sigl = 0; + loaded_data->exp = e; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); +} + + +/* Get a packed bcd array from user memory */ +void reg_load_bcd(char *s, FPU_REG *loaded_data) +{ + int pos; + unsigned char bcd; + long long l=0; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, s, 10); + RE_ENTRANT_CHECK_ON; + for ( pos = 8; pos >= 0; pos--) + { + l *= 10; + RE_ENTRANT_CHECK_OFF; + bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos); + RE_ENTRANT_CHECK_ON; + l += bcd >> 4; + l *= 10; + l += bcd & 0x0f; + } + + RE_ENTRANT_CHECK_OFF; + loaded_data->sign = + ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ? + SIGN_NEG : SIGN_POS; + RE_ENTRANT_CHECK_ON; + + if (l == 0) + { + char sign = loaded_data->sign; + reg_move(&CONST_Z, loaded_data); + loaded_data->sign = sign; + } + else + { + significand(loaded_data) = l; + loaded_data->exp = EXP_BIAS + 63; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); + } +} + +/*===========================================================================*/ + +/* Put a long double into user memory */ +int reg_store_extended(long double *d, FPU_REG *st0_ptr) +{ + /* + The only exception raised by an attempt to store to an + extended format is the Invalid Stack exception, i.e. + attempting to store from an empty register. + */ + + if ( st0_ptr->tag != TW_Empty ) + { + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE, d, 10); + RE_ENTRANT_CHECK_ON; + write_to_extended(st0_ptr, (char *) d); + return 1; + } + + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if ( control_word & CW_Invalid ) + { + /* The masked response */ + /* 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); + RE_ENTRANT_CHECK_ON; + return 1; + } + else + return 0; + +} + + +/* Put a double into user memory */ +int reg_store_double(double *dfloat, FPU_REG *st0_ptr) +{ + unsigned long l[2]; + unsigned long increment = 0; /* avoid gcc warnings */ + char st0_tag = st0_ptr->tag; + + if (st0_tag == TW_Valid) + { + int exp; + FPU_REG tmp; + + reg_move(st0_ptr, &tmp); + exp = tmp.exp - EXP_BIAS; + + 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 + a denormal exception here, but... */ + if ( st0_ptr->exp <= EXP_UNDER ) + { + /* Underflow has priority. */ + if ( control_word & CW_Underflow ) + denormal_operand(); + } +#endif PECULIAR_486 + + tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */ + + if ( (precision_loss = round_to_int(&tmp)) ) + { +#ifdef PECULIAR_486 + /* Did it round to a non-denormal ? */ + /* This behaviour might be regarded as peculiar, it appears + that the 80486 rounds to the dest precision, then + converts to decide underflow. */ + if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) && + (st0_ptr->sigl & 0x000007ff)) ) +#endif PECULIAR_486 + { + EXCEPTION(EX_Underflow); + /* This is a special case: see sec 16.2.5.1 of + the 80486 book */ + if ( !(control_word & CW_Underflow) ) + return 0; + } + EXCEPTION(precision_loss); + if ( !(control_word & CW_Precision) ) + return 0; + } + l[0] = tmp.sigl; + l[1] = tmp.sigh; + } + else + { + if ( tmp.sigl & 0x000007ff ) + { + switch (control_word & CW_RC) + { + case RC_RND: + /* Rounding can get a little messy.. */ + increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */ + ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */ + break; + case RC_DOWN: /* towards -infinity */ + increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff; + break; + case RC_UP: /* towards +infinity */ + increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0; + break; + case RC_CHOP: + increment = 0; + break; + } + + /* Truncate the mantissa */ + tmp.sigl &= 0xfffff800; + + if ( increment ) + { + set_precision_flag_up(); + + if ( tmp.sigl >= 0xfffff800 ) + { + /* the sigl part overflows */ + if ( tmp.sigh == 0xffffffff ) + { + /* The sigh part overflows */ + tmp.sigh = 0x80000000; + exp++; + if (exp >= EXP_OVER) + goto overflow; + } + else + { + tmp.sigh ++; + } + tmp.sigl = 0x00000000; + } + else + { + /* We only need to increment sigl */ + tmp.sigl += 0x00000800; + } + } + else + set_precision_flag_down(); + } + + l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21); + l[1] = ((tmp.sigh >> 11) & 0xfffff); + + if ( exp > DOUBLE_Emax ) + { + overflow: + EXCEPTION(EX_Overflow); + if ( !(control_word & CW_Overflow) ) + return 0; + set_precision_flag_up(); + if ( !(control_word & CW_Precision) ) + return 0; + + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + /* Overflow to infinity */ + l[0] = 0x00000000; /* Set to */ + l[1] = 0x7ff00000; /* + INF */ + } + else + { + /* Add the exponent */ + l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20); + } + } + } + else if (st0_tag == TW_Zero) + { + /* Number is zero */ + l[0] = 0; + l[1] = 0; + } + else if (st0_tag == TW_Infinity) + { + l[0] = 0; + l[1] = 0x7ff00000; + } + else if (st0_tag == TW_NaN) + { + /* See if we can get a valid NaN from the FPU_REG */ + l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21); + l[1] = ((st0_ptr->sigh >> 11) & 0xfffff); + if ( !(st0_ptr->sigh & 0x40000000) ) + { + /* It is a signalling NaN */ + EXCEPTION(EX_Invalid); + if ( !(control_word & CW_Invalid) ) + return 0; + l[1] |= (0x40000000 >> 11); + } + l[1] |= 0x7ff00000; + } + else if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if ( control_word & CW_Invalid ) + { + /* The masked response */ + /* 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); + RE_ENTRANT_CHECK_ON; + return 1; + } + else + return 0; + } + if ( st0_ptr->sign ) + l[1] |= 0x80000000; + + 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); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a float into user memory */ +int reg_store_single(float *single, FPU_REG *st0_ptr) +{ + long templ; + unsigned long increment = 0; /* avoid gcc warnings */ + char st0_tag = st0_ptr->tag; + + if (st0_tag == TW_Valid) + { + int exp; + FPU_REG tmp; + + reg_move(st0_ptr, &tmp); + exp = tmp.exp - EXP_BIAS; + + if ( exp < SINGLE_Emin ) + { + int precision_loss; + + /* A denormal will always underflow. */ +#ifndef PECULIAR_486 + /* An 80486 is supposed to be able to generate + a denormal exception here, but... */ + if ( st0_ptr->exp <= EXP_UNDER ) + { + /* Underflow has priority. */ + if ( control_word & CW_Underflow ) + denormal_operand(); + } +#endif PECULIAR_486 + + tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */ + + if ( (precision_loss = round_to_int(&tmp)) ) + { +#ifdef PECULIAR_486 + /* Did it round to a non-denormal ? */ + /* This behaviour might be regarded as peculiar, it appears + that the 80486 rounds to the dest precision, then + converts to decide underflow. */ + if ( !((tmp.sigl == 0x00800000) && + ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) ) +#endif PECULIAR_486 + { + EXCEPTION(EX_Underflow); + /* This is a special case: see sec 16.2.5.1 of + the 80486 book */ + if ( !(control_word & EX_Underflow) ) + return 0; + } + EXCEPTION(precision_loss); + if ( !(control_word & EX_Precision) ) + return 0; + } + templ = tmp.sigl; + } + else + { + if ( tmp.sigl | (tmp.sigh & 0x000000ff) ) + { + unsigned long sigh = tmp.sigh; + unsigned long sigl = tmp.sigl; + + switch (control_word & CW_RC) + { + case RC_RND: + increment = ((sigh & 0xff) > 0x80) /* more than half */ + || (((sigh & 0xff) == 0x80) && sigl) /* more than half */ + || ((sigh & 0x180) == 0x180); /* round to even */ + break; + case RC_DOWN: /* towards -infinity */ + increment = (tmp.sign == SIGN_POS) + ? 0 : (sigl | (sigh & 0xff)); + break; + case RC_UP: /* towards +infinity */ + increment = (tmp.sign == SIGN_POS) + ? (sigl | (sigh & 0xff)) : 0; + break; + case RC_CHOP: + increment = 0; + break; + } + + /* Truncate part of the mantissa */ + tmp.sigl = 0; + + if (increment) + { + set_precision_flag_up(); + + if ( sigh >= 0xffffff00 ) + { + /* The sigh part overflows */ + tmp.sigh = 0x80000000; + exp++; + if ( exp >= EXP_OVER ) + goto overflow; + } + else + { + tmp.sigh &= 0xffffff00; + tmp.sigh += 0x100; + } + } + else + { + set_precision_flag_down(); + tmp.sigh &= 0xffffff00; /* Finish the truncation */ + } + } + + templ = (tmp.sigh >> 8) & 0x007fffff; + + if ( exp > SINGLE_Emax ) + { + overflow: + EXCEPTION(EX_Overflow); + if ( !(control_word & CW_Overflow) ) + return 0; + set_precision_flag_up(); + if ( !(control_word & CW_Precision) ) + return 0; + + /* This is a special case: see sec 16.2.5.1 of the 80486 book. */ + /* Masked response is overflow to infinity. */ + templ = 0x7f800000; + } + else + templ |= ((exp+SINGLE_Ebias) & 0xff) << 23; + } + } + else if (st0_tag == TW_Zero) + { + templ = 0; + } + else if (st0_tag == TW_Infinity) + { + templ = 0x7f800000; + } + else if (st0_tag == TW_NaN) + { + /* See if we can get a valid NaN from the FPU_REG */ + templ = st0_ptr->sigh >> 8; + if ( !(st0_ptr->sigh & 0x40000000) ) + { + /* It is a signalling NaN */ + EXCEPTION(EX_Invalid); + if ( !(control_word & CW_Invalid) ) + return 0; + templ |= (0x40000000 >> 8); + } + templ |= 0x7f800000; + } + else if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if ( control_word & EX_Invalid ) + { + /* The masked response */ + /* Put out the QNaN indefinite */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,(void *)single,4); + put_fs_long(0xffc00000, (unsigned long *) single); + RE_ENTRANT_CHECK_ON; + return 1; + } + else + return 0; + } +#ifdef PARANOID + else + { + EXCEPTION(EX_INTERNAL|0x163); + return 0; + } +#endif + if (st0_ptr->sign) + templ |= 0x80000000; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,(void *)single,4); + put_fs_long(templ,(unsigned long *) single); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a long long into user memory */ +int reg_store_int64(long long *d, FPU_REG *st0_ptr) +{ + FPU_REG t; + long long tll; + int precision_loss; + char st0_tag = st0_ptr->tag; + + if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + goto invalid_operand; + } + else if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } + + reg_move(st0_ptr, &t); + precision_loss = round_to_int(&t); + ((long *)&tll)[0] = t.sigl; + ((long *)&tll)[1] = t.sigh; + if ( (precision_loss == 1) || + ((t.sigh & 0x80000000) && + !((t.sigh == 0x80000000) && (t.sigl == 0) && + (t.sign == SIGN_NEG))) ) + { + EXCEPTION(EX_Invalid); + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + invalid_operand: + if ( control_word & EX_Invalid ) + { + /* Produce something like QNaN "indefinite" */ + tll = 0x8000000000000000LL; + } + else + return 0; + } + else + { + if ( precision_loss ) + set_precision_flag(precision_loss); + if ( t.sign ) + tll = - tll; + } + + 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); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a long into user memory */ +int reg_store_int32(long *d, FPU_REG *st0_ptr) +{ + FPU_REG t; + int precision_loss; + char st0_tag = st0_ptr->tag; + + if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + goto invalid_operand; + } + else if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } + + reg_move(st0_ptr, &t); + precision_loss = round_to_int(&t); + if (t.sigh || + ((t.sigl & 0x80000000) && + !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) ) + { + EXCEPTION(EX_Invalid); + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + invalid_operand: + if ( control_word & EX_Invalid ) + { + /* Produce something like QNaN "indefinite" */ + t.sigl = 0x80000000; + } + else + return 0; + } + else + { + if ( precision_loss ) + set_precision_flag(precision_loss); + if ( t.sign ) + t.sigl = -(long)t.sigl; + } + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,4); + put_fs_long(t.sigl, (unsigned long *) d); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a short into user memory */ +int reg_store_int16(short *d, FPU_REG *st0_ptr) +{ + FPU_REG t; + int precision_loss; + char st0_tag = st0_ptr->tag; + + if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + goto invalid_operand; + } + else if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } + + reg_move(st0_ptr, &t); + precision_loss = round_to_int(&t); + if (t.sigh || + ((t.sigl & 0xffff8000) && + !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) ) + { + EXCEPTION(EX_Invalid); + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + invalid_operand: + if ( control_word & EX_Invalid ) + { + /* Produce something like QNaN "indefinite" */ + t.sigl = 0x8000; + } + else + return 0; + } + else + { + if ( precision_loss ) + set_precision_flag(precision_loss); + if ( t.sign ) + t.sigl = -t.sigl; + } + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,2); + put_fs_word((short)t.sigl,(short *) d); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a packed bcd array into user memory */ +int reg_store_bcd(char *d, FPU_REG *st0_ptr) +{ + FPU_REG t; + unsigned long long ll; + unsigned char b; + int i, precision_loss; + unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0; + char st0_tag = st0_ptr->tag; + + if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + goto invalid_operand; + } + + reg_move(st0_ptr, &t); + precision_loss = round_to_int(&t); + ll = significand(&t); + + /* Check for overflow, by comparing with 999999999999999999 decimal. */ + if ( (t.sigh > 0x0de0b6b3) || + ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) ) + { + EXCEPTION(EX_Invalid); + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + invalid_operand: + if ( control_word & CW_Invalid ) + { + /* Produce the QNaN "indefinite" */ + 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); + RE_ENTRANT_CHECK_ON; + return 1; + } + else + return 0; + } + else if ( precision_loss ) + { + /* Precision loss doesn't stop the data transfer */ + set_precision_flag(precision_loss); + } + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,10); + RE_ENTRANT_CHECK_ON; + for ( i = 0; i < 9; i++) + { + b = div_small(&ll, 10); + b |= (div_small(&ll, 10)) << 4; + RE_ENTRANT_CHECK_OFF; + put_fs_byte(b,(unsigned char *) d+i); + RE_ENTRANT_CHECK_ON; + } + RE_ENTRANT_CHECK_OFF; + put_fs_byte(sign,(unsigned char *) d+9); + RE_ENTRANT_CHECK_ON; + + return 1; +} + +/*===========================================================================*/ + +/* r gets mangled such that sig is int, sign: + it is NOT normalized */ +/* The return value (in eax) is zero if the result is exact, + if bits are changed due to rounding, truncation, etc, then + a non-zero value is returned */ +/* Overflow is signalled by a non-zero return value (in eax). + In the case of overflow, the returned significand always has the + largest possible value */ +int round_to_int(FPU_REG *r) +{ + char very_big; + unsigned eax; + + if (r->tag == TW_Zero) + { + /* Make sure that zero is returned */ + significand(r) = 0; + return 0; /* o.k. */ + } + + if (r->exp > EXP_BIAS + 63) + { + r->sigl = r->sigh = ~0; /* The largest representable number */ + return 1; /* overflow */ + } + + eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp); + very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */ +#define half_or_more (eax & 0x80000000) +#define frac_part (eax) +#define more_than_half ((eax & 0x80000001) == 0x80000001) + switch (control_word & CW_RC) + { + case RC_RND: + if ( more_than_half /* nearest */ + || (half_or_more && (r->sigl & 1)) ) /* odd -> even */ + { + if ( very_big ) return 1; /* overflow */ + significand(r) ++; + return PRECISION_LOST_UP; + } + break; + case RC_DOWN: + if (frac_part && r->sign) + { + if ( very_big ) return 1; /* overflow */ + significand(r) ++; + return PRECISION_LOST_UP; + } + break; + case RC_UP: + if (frac_part && !r->sign) + { + if ( very_big ) return 1; /* overflow */ + significand(r) ++; + return PRECISION_LOST_UP; + } + break; + case RC_CHOP: + break; + } + + return eax ? PRECISION_LOST_DOWN : 0; + +} + +/*===========================================================================*/ + +char *fldenv(fpu_addr_modes addr_modes, char *s) +{ + unsigned short tag_word = 0; + unsigned char tag; + int i; + + if ( (addr_modes.default_mode == VM86) || + ((addr_modes.default_mode == PM16) + ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) + { + 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)); + RE_ENTRANT_CHECK_ON; + s += 0x0e; + if ( addr_modes.default_mode == VM86 ) + { + instruction_address.offset + += (instruction_address.selector & 0xf000) << 4; + operand_address.offset += (operand_address.selector & 0xf000) << 4; + } + } + else + { + 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)); + RE_ENTRANT_CHECK_ON; + s += 0x1c; + } + +#ifdef PECULIAR_486 + control_word &= ~0xe080; +#endif PECULIAR_486 + + top = (partial_status >> SW_Top_Shift) & 7; + + if ( partial_status & ~control_word & CW_Exceptions ) + partial_status |= (SW_Summary | SW_Backward); + else + partial_status &= ~(SW_Summary | SW_Backward); + + for ( i = 0; i < 8; i++ ) + { + tag = tag_word & 3; + tag_word >>= 2; + + if ( tag == 3 ) + /* New tag is empty. Accept it */ + regs[i].tag = TW_Empty; + else if ( regs[i].tag == TW_Empty ) + { + /* Old tag is empty and new tag is not empty. New tag is determined + by old reg contents */ + if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias ) + { + if ( !(regs[i].sigl | regs[i].sigh) ) + regs[i].tag = TW_Zero; + else + regs[i].tag = TW_Valid; + } + else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias ) + { + if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) ) + regs[i].tag = TW_Infinity; + else + regs[i].tag = TW_NaN; + } + else + regs[i].tag = TW_Valid; + } + /* Else old tag is not empty and new tag is not empty. Old tag + remains correct */ + } + + return s; +} + + +void frstor(fpu_addr_modes addr_modes, char *data_address) +{ + int i, stnr; + unsigned char tag; + char *s = fldenv(addr_modes, data_address); + + for ( i = 0; i < 8; i++ ) + { + /* Load each register. */ + stnr = (i+top) & 7; + tag = regs[stnr].tag; /* Derived from the fldenv() loaded tag word. */ + reg_load_extended((long double *)(s+i*10), ®s[stnr]); + if ( tag == TW_Empty ) /* The loaded data over-rides all other cases. */ + regs[stnr].tag = tag; + } + +} + + +unsigned short tag_word(void) +{ + unsigned short word = 0; + unsigned char tag; + int i; + + for ( i = 7; i >= 0; i-- ) + { + switch ( tag = regs[i].tag ) + { + case TW_Valid: + if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) ) + tag = 2; + break; + case TW_Infinity: + case TW_NaN: + tag = 2; + break; + case TW_Empty: + tag = 3; + break; + /* TW_Zero already has the correct value */ + } + word <<= 2; + word |= tag; + } + return word; +} + + +char *fstenv(fpu_addr_modes addr_modes, char *d) +{ + if ( (addr_modes.default_mode == VM86) || + ((addr_modes.default_mode == PM16) + ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) + { + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,14); +#ifdef PECULIAR_486 + put_fs_long(control_word & ~0xe080, (unsigned short *) d); +#else + put_fs_word(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)); + if ( addr_modes.default_mode == VM86 ) + { + put_fs_word((instruction_address.offset & 0xf0000) >> 4, + (unsigned short *) (d+8)); + put_fs_word((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)); + } + RE_ENTRANT_CHECK_ON; + d += 0x0e; + } + else + { + RE_ENTRANT_CHECK_OFF; + 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)); +#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)); +#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)); +#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)); +#else + put_fs_long(operand_address.selector, (unsigned long *) (d+0x18)); +#endif PECULIAR_486 + RE_ENTRANT_CHECK_ON; + d += 0x1c; + } + + control_word |= CW_Exceptions; + partial_status &= ~(SW_Summary | SW_Backward); + + return d; +} + + +void fsave(fpu_addr_modes addr_modes, char *data_address) +{ + char *d; + int i; + + d = fstenv(addr_modes, data_address); + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,80); + RE_ENTRANT_CHECK_ON; + for ( i = 0; i < 8; i++ ) + write_to_extended(®s[(top + i) & 7], d + 10 * i); + + finit(); + +} + +/*===========================================================================*/ + +/* + A call to this function must be preceded by a call to + FPU_verify_area() to verify access to the 10 bytes at d + */ +static void write_to_extended(FPU_REG *rp, char *d) +{ + long e; + FPU_REG tmp; + + e = rp->exp - EXP_BIAS + EXTENDED_Ebias; + +#ifdef PARANOID + switch ( rp->tag ) + { + case TW_Zero: + if ( rp->sigh | rp->sigl | e ) + EXCEPTION(EX_INTERNAL | 0x160); + break; + case TW_Infinity: + case TW_NaN: + if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) ) + EXCEPTION(EX_INTERNAL | 0x161); + break; + default: + if (e > 0x7fff || e < -63) + EXCEPTION(EX_INTERNAL | 0x162); + } +#endif PARANOID + + /* + All numbers except denormals are stored internally in a + format which is compatible with the extended real number + format. + */ + if ( e > 0 ) + { + /* 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)); + RE_ENTRANT_CHECK_ON; + } + else + { + /* + The number is a de-normal stored as a normal using our + extra exponent range, or is Zero. + Convert it back to a de-normal, or leave it as Zero. + */ + reg_move(rp, &tmp); + tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 63 */ + 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)); + RE_ENTRANT_CHECK_ON; + } + e |= rp->sign == SIGN_POS ? 0 : 0x8000; + RE_ENTRANT_CHECK_OFF; + put_fs_word(e, (unsigned short *) (d + 8)); + RE_ENTRANT_CHECK_ON; +} diff --git a/arch/i386/math-emu/reg_mul.c b/arch/i386/math-emu/reg_mul.c new file mode 100644 index 000000000..75246187b --- /dev/null +++ b/arch/i386/math-emu/reg_mul.c @@ -0,0 +1,105 @@ +/*---------------------------------------------------------------------------+ + | reg_mul.c | + | | + | Multiply one FPU_REG by another, put the result in a destination FPU_REG. | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | The destination may be any FPU_REG, including one of the source FPU_REGs. | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "fpu_system.h" + + +/* This routine must be called with non-empty source registers */ +int reg_mul(FPU_REG const *a, FPU_REG const *b, + FPU_REG *dest, unsigned int control_w) +{ + char saved_sign = dest->sign; + char sign = (a->sign ^ b->sign); + + if (!(a->tag | b->tag)) + { + /* Both regs Valid, this should be the most common case. */ + dest->sign = sign; + if ( reg_u_mul(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + return 0; + } + else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero)) + { +#ifdef DENORM_OPERAND + if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) || + ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) ) + { + if ( denormal_operand() ) return 1; + } +#endif DENORM_OPERAND + /* Must have either both arguments == zero, or + one valid and the other zero. + The result is therefore zero. */ + reg_move(&CONST_Z, dest); + /* The 80486 book says that the answer is +0, but a real + 80486 behaves this way. + IEEE-754 apparently says it should be this way. */ + dest->sign = sign; + return 0; + } + else + { + /* Must have infinities, NaNs, etc */ + if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) + { return real_2op_NaN(a, b, dest); } + else if (a->tag == TW_Infinity) + { + if (b->tag == TW_Zero) + { return arith_invalid(dest); } /* Zero*Infinity is invalid */ + else + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(a, dest); + dest->sign = sign; + } + return 0; + } + else if (b->tag == TW_Infinity) + { + if (a->tag == TW_Zero) + { return arith_invalid(dest); } /* Zero*Infinity is invalid */ + else + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); + dest->sign = sign; + } + return 0; + } +#ifdef PARANOID + else + { + EXCEPTION(EX_INTERNAL|0x102); + return 1; + } +#endif PARANOID + } +} diff --git a/arch/i386/math-emu/reg_norm.S b/arch/i386/math-emu/reg_norm.S new file mode 100644 index 000000000..9b7a9d77d --- /dev/null +++ b/arch/i386/math-emu/reg_norm.S @@ -0,0 +1,150 @@ +/*---------------------------------------------------------------------------+ + | reg_norm.S | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Normalize the value in a FPU_REG. | + | | + | Call from C as: | + | void normalize(FPU_REG *n) | + | | + | void normalize_nuo(FPU_REG *n) | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_asm.h" + + +.text + + .align 2,144 +.globl _normalize + +_normalize: + pushl %ebp + movl %esp,%ebp + pushl %ebx + + movl PARAM1,%ebx + +#ifdef PARANOID + cmpb TW_Valid,TAG(%ebx) + je L_ok + + pushl $0x220 + call _exception + addl $4,%esp + +L_ok: +#endif PARANOID + + movl SIGH(%ebx),%edx + movl SIGL(%ebx),%eax + + orl %edx,%edx /* ms bits */ + js L_done /* Already normalized */ + jnz L_shift_1 /* Shift left 1 - 31 bits */ + + orl %eax,%eax + jz L_zero /* The contents are zero */ + + movl %eax,%edx + xorl %eax,%eax + subl $32,EXP(%ebx) /* This can cause an underflow */ + +/* We need to shift left by 1 - 31 bits */ +L_shift_1: + bsrl %edx,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + shld %cl,%eax,%edx + shl %cl,%eax + subl %ecx,EXP(%ebx) /* This can cause an underflow */ + + movl %edx,SIGH(%ebx) + movl %eax,SIGL(%ebx) + +L_done: + cmpl EXP_OVER,EXP(%ebx) + jge L_overflow + + cmpl EXP_UNDER,EXP(%ebx) + jle L_underflow + +L_exit: + popl %ebx + leave + ret + + +L_zero: + movl EXP_UNDER,EXP(%ebx) + movb TW_Zero,TAG(%ebx) + jmp L_exit + +L_underflow: + push %ebx + call _arith_underflow + pop %ebx + jmp L_exit + +L_overflow: + push %ebx + call _arith_overflow + pop %ebx + jmp L_exit + + + +/* Normalise without reporting underflow or overflow */ + .align 2,144 +.globl _normalize_nuo + +_normalize_nuo: + pushl %ebp + movl %esp,%ebp + pushl %ebx + + movl PARAM1,%ebx + +#ifdef PARANOID + cmpb TW_Valid,TAG(%ebx) + je L_ok_nuo + + pushl $0x221 + call _exception + addl $4,%esp + +L_ok_nuo: +#endif PARANOID + + movl SIGH(%ebx),%edx + movl SIGL(%ebx),%eax + + orl %edx,%edx /* ms bits */ + js L_exit /* Already normalized */ + jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */ + + orl %eax,%eax + jz L_zero /* The contents are zero */ + + movl %eax,%edx + xorl %eax,%eax + subl $32,EXP(%ebx) /* This can cause an underflow */ + +/* We need to shift left by 1 - 31 bits */ +L_nuo_shift_1: + bsrl %edx,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + shld %cl,%eax,%edx + shl %cl,%eax + subl %ecx,EXP(%ebx) /* This can cause an underflow */ + + movl %edx,SIGH(%ebx) + movl %eax,SIGL(%ebx) + jmp L_exit + + diff --git a/arch/i386/math-emu/reg_round.S b/arch/i386/math-emu/reg_round.S new file mode 100644 index 000000000..bd8a40dc4 --- /dev/null +++ b/arch/i386/math-emu/reg_round.S @@ -0,0 +1,701 @@ + .file "reg_round.S" +/*---------------------------------------------------------------------------+ + | reg_round.S | + | | + | Rounding/truncation/etc for FPU basic arithmetic functions. | + | | + | Copyright (C) 1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | This code has four possible entry points. | + | The following must be entered by a jmp instruction: | + | fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit. | + | | + | The _round_reg entry point is intended to be used by C code. | + | From C, call as: | + | void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) | + | | + | For correct "up" and "down" rounding, the argument must have the correct | + | sign. | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Four entry points. | + | | + | Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points: | + | %eax:%ebx 64 bit significand | + | %edx 32 bit extension of the significand | + | %edi pointer to an FPU_REG for the result to be stored | + | stack calling function must have set up a C stack frame and | + | pushed %esi, %edi, and %ebx | + | | + | Needed just for the fpu_reg_round_sqrt entry point: | + | %cx A control word in the same format as the FPU control word. | + | Otherwise, PARAM4 must give such a value. | + | | + | | + | The significand and its extension are assumed to be exact in the | + | following sense: | + | If the significand by itself is the exact result then the significand | + | extension (%edx) must contain 0, otherwise the significand extension | + | must be non-zero. | + | If the significand extension is non-zero then the significand is | + | smaller than the magnitude of the correct exact result by an amount | + | greater than zero and less than one ls bit of the significand. | + | The significand extension is only required to have three possible | + | non-zero values: | + | less than 0x80000000 <=> the significand is less than 1/2 an ls | + | bit smaller than the magnitude of the | + | true exact result. | + | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit | + | smaller than the magnitude of the true | + | exact result. | + | greater than 0x80000000 <=> the significand is more than 1/2 an ls | + | bit smaller than the magnitude of the | + | true exact result. | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | The code in this module has become quite complex, but it should handle | + | all of the FPU flags which are set at this stage of the basic arithmetic | + | computations. | + | There are a few rare cases where the results are not set identically to | + | a real FPU. These require a bit more thought because at this stage the | + | results of the code here appear to be more consistent... | + | This may be changed in a future version. | + +---------------------------------------------------------------------------*/ + + +#include "fpu_asm.h" +#include "exception.h" +#include "control_w.h" + +/* Flags for FPU_bits_lost */ +#define LOST_DOWN $1 +#define LOST_UP $2 + +/* Flags for FPU_denormal */ +#define DENORMAL $1 +#define UNMASKED_UNDERFLOW $2 + + +#ifndef NON_REENTRANT_FPU +/* Make the code re-entrant by putting + local storage on the stack: */ +#define FPU_bits_lost (%esp) +#define FPU_denormal 1(%esp) + +#else +/* Not re-entrant, so we can gain speed by putting + local storage in a static area: */ +.data + .align 2,0 +FPU_bits_lost: + .byte 0 +FPU_denormal: + .byte 0 +#endif NON_REENTRANT_FPU + + +.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: + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%edi + movl SIGH(%edi),%eax + movl SIGL(%edi),%ebx + movl PARAM2,%edx + movl PARAM3,%ecx + jmp fpu_reg_round_sqrt + +fpu_reg_round: /* Normal entry point */ + movl PARAM4,%ecx + +fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */ + +#ifndef NON_REENTRANT_FPU + pushl %ebx /* adjust the stack pointer */ +#endif NON_REENTRANT_FPU + +#ifdef PARANOID +/* Cannot use this here yet */ +/* orl %eax,%eax */ +/* jns L_entry_bugged */ +#endif PARANOID + + cmpl EXP_UNDER,EXP(%edi) + jle xMake_denorm /* The number is a de-normal */ + + movb $0,FPU_denormal /* 0 -> not a de-normal */ + +xDenorm_done: + movb $0,FPU_bits_lost /* No bits yet lost in rounding */ + + movl %ecx,%esi + andl CW_PC,%ecx + cmpl PR_64_BITS,%ecx + je LRound_To_64 + + cmpl PR_53_BITS,%ecx + je LRound_To_53 + + cmpl PR_24_BITS,%ecx + je LRound_To_24 + +#ifdef PECULIAR_486 +/* With the precision control bits set to 01 "(reserved)", a real 80486 + behaves as if the precision control bits were set to 11 "64 bits" */ + cmpl PR_RESERVED_BITS,%ecx + je LRound_To_64 +#ifdef PARANOID + jmp L_bugged_denorm_486 +#endif PARANOID +#else +#ifdef PARANOID + jmp L_bugged_denorm /* There is no bug, just a bad control word */ +#endif PARANOID +#endif PECULIAR_486 + + +/* Round etc to 24 bit precision */ +LRound_To_24: + movl %esi,%ecx + andl CW_RC,%ecx + cmpl RC_RND,%ecx + je LRound_nearest_24 + + cmpl RC_CHOP,%ecx + je LCheck_truncate_24 + + cmpl RC_UP,%ecx /* Towards +infinity */ + je LUp_24 + + cmpl RC_DOWN,%ecx /* Towards -infinity */ + je LDown_24 + +#ifdef PARANOID + jmp L_bugged_round24 +#endif PARANOID + +LUp_24: + cmpb SIGN_POS,SIGN(%edi) + jne LCheck_truncate_24 /* If negative then up==truncate */ + + jmp LCheck_24_round_up + +LDown_24: + cmpb SIGN_POS,SIGN(%edi) + je LCheck_truncate_24 /* If positive then down==truncate */ + +LCheck_24_round_up: + movl %eax,%ecx + andl $0x000000ff,%ecx + orl %ebx,%ecx + orl %edx,%ecx + jnz LDo_24_round_up + jmp LRe_normalise + +LRound_nearest_24: + /* Do rounding of the 24th bit if needed (nearest or even) */ + movl %eax,%ecx + andl $0x000000ff,%ecx + cmpl $0x00000080,%ecx + jc LCheck_truncate_24 /* less than half, no increment needed */ + + jne LGreater_Half_24 /* greater than half, increment needed */ + + /* Possibly half, we need to check the ls bits */ + orl %ebx,%ebx + jnz LGreater_Half_24 /* greater than half, increment needed */ + + orl %edx,%edx + jnz LGreater_Half_24 /* greater than half, increment needed */ + + /* Exactly half, increment only if 24th bit is 1 (round to even) */ + testl $0x00000100,%eax + jz LDo_truncate_24 + +LGreater_Half_24: /* Rounding: increment at the 24th bit */ +LDo_24_round_up: + andl $0xffffff00,%eax /* Truncate to 24 bits */ + xorl %ebx,%ebx + movb LOST_UP,FPU_bits_lost + addl $0x00000100,%eax + jmp LCheck_Round_Overflow + +LCheck_truncate_24: + movl %eax,%ecx + andl $0x000000ff,%ecx + orl %ebx,%ecx + orl %edx,%ecx + jz LRe_normalise /* No truncation needed */ + +LDo_truncate_24: + andl $0xffffff00,%eax /* Truncate to 24 bits */ + xorl %ebx,%ebx + movb LOST_DOWN,FPU_bits_lost + jmp LRe_normalise + + +/* Round etc to 53 bit precision */ +LRound_To_53: + movl %esi,%ecx + andl CW_RC,%ecx + cmpl RC_RND,%ecx + je LRound_nearest_53 + + cmpl RC_CHOP,%ecx + je LCheck_truncate_53 + + cmpl RC_UP,%ecx /* Towards +infinity */ + je LUp_53 + + cmpl RC_DOWN,%ecx /* Towards -infinity */ + je LDown_53 + +#ifdef PARANOID + jmp L_bugged_round53 +#endif PARANOID + +LUp_53: + cmpb SIGN_POS,SIGN(%edi) + jne LCheck_truncate_53 /* If negative then up==truncate */ + + jmp LCheck_53_round_up + +LDown_53: + cmpb SIGN_POS,SIGN(%edi) + je LCheck_truncate_53 /* If positive then down==truncate */ + +LCheck_53_round_up: + movl %ebx,%ecx + andl $0x000007ff,%ecx + orl %edx,%ecx + jnz LDo_53_round_up + jmp LRe_normalise + +LRound_nearest_53: + /* Do rounding of the 53rd bit if needed (nearest or even) */ + movl %ebx,%ecx + andl $0x000007ff,%ecx + cmpl $0x00000400,%ecx + jc LCheck_truncate_53 /* less than half, no increment needed */ + + jnz LGreater_Half_53 /* greater than half, increment needed */ + + /* Possibly half, we need to check the ls bits */ + orl %edx,%edx + jnz LGreater_Half_53 /* greater than half, increment needed */ + + /* Exactly half, increment only if 53rd bit is 1 (round to even) */ + testl $0x00000800,%ebx + jz LTruncate_53 + +LGreater_Half_53: /* Rounding: increment at the 53rd bit */ +LDo_53_round_up: + movb LOST_UP,FPU_bits_lost + andl $0xfffff800,%ebx /* Truncate to 53 bits */ + addl $0x00000800,%ebx + adcl $0,%eax + jmp LCheck_Round_Overflow + +LCheck_truncate_53: + movl %ebx,%ecx + andl $0x000007ff,%ecx + orl %edx,%ecx + jz LRe_normalise + +LTruncate_53: + movb LOST_DOWN,FPU_bits_lost + andl $0xfffff800,%ebx /* Truncate to 53 bits */ + jmp LRe_normalise + + +/* Round etc to 64 bit precision */ +LRound_To_64: + movl %esi,%ecx + andl CW_RC,%ecx + cmpl RC_RND,%ecx + je LRound_nearest_64 + + cmpl RC_CHOP,%ecx + je LCheck_truncate_64 + + cmpl RC_UP,%ecx /* Towards +infinity */ + je LUp_64 + + cmpl RC_DOWN,%ecx /* Towards -infinity */ + je LDown_64 + +#ifdef PARANOID + jmp L_bugged_round64 +#endif PARANOID + +LUp_64: + cmpb SIGN_POS,SIGN(%edi) + jne LCheck_truncate_64 /* If negative then up==truncate */ + + orl %edx,%edx + jnz LDo_64_round_up + jmp LRe_normalise + +LDown_64: + cmpb SIGN_POS,SIGN(%edi) + je LCheck_truncate_64 /* If positive then down==truncate */ + + orl %edx,%edx + jnz LDo_64_round_up + jmp LRe_normalise + +LRound_nearest_64: + cmpl $0x80000000,%edx + jc LCheck_truncate_64 + + jne LDo_64_round_up + + /* Now test for round-to-even */ + testb $1,%ebx + jz LCheck_truncate_64 + +LDo_64_round_up: + movb LOST_UP,FPU_bits_lost + addl $1,%ebx + adcl $0,%eax + +LCheck_Round_Overflow: + jnc LRe_normalise + + /* Overflow, adjust the result (significand to 1.0) */ + rcrl $1,%eax + rcrl $1,%ebx + incl EXP(%edi) + jmp LRe_normalise + +LCheck_truncate_64: + orl %edx,%edx + jz LRe_normalise + +LTruncate_64: + movb LOST_DOWN,FPU_bits_lost + +LRe_normalise: + testb $0xff,FPU_denormal + jnz xNormalise_result + +xL_Normalised: + cmpb LOST_UP,FPU_bits_lost + je xL_precision_lost_up + + cmpb LOST_DOWN,FPU_bits_lost + je xL_precision_lost_down + +xL_no_precision_loss: + /* store the result */ + movb TW_Valid,TAG(%edi) + +xL_Store_significand: + movl %eax,SIGH(%edi) + movl %ebx,SIGL(%edi) + + xorl %eax,%eax /* No errors detected. */ + + cmpl EXP_OVER,EXP(%edi) + jge L_overflow + +fpu_reg_round_exit: +#ifndef NON_REENTRANT_FPU + popl %ebx /* adjust the stack pointer */ +#endif NON_REENTRANT_FPU + +fpu_Arith_exit: + popl %ebx + popl %edi + popl %esi + leave + ret + + +/* + * Set the FPU status flags to represent precision loss due to + * round-up. + */ +xL_precision_lost_up: + push %eax + call _set_precision_flag_up + popl %eax + jmp xL_no_precision_loss + +/* + * Set the FPU status flags to represent precision loss due to + * truncation. + */ +xL_precision_lost_down: + push %eax + call _set_precision_flag_down + popl %eax + jmp xL_no_precision_loss + + +/* + * The number is a denormal (which might get rounded up to a normal) + * Shift the number right the required number of bits, which will + * have to be undone later... + */ +xMake_denorm: + /* The action to be taken depends upon whether the underflow + exception is masked */ + testb CW_Underflow,%cl /* Underflow mask. */ + jz xUnmasked_underflow /* Do not make a denormal. */ + + movb DENORMAL,FPU_denormal + + pushl %ecx /* Save */ + movl EXP_UNDER+1,%ecx + subl EXP(%edi),%ecx + + cmpl $64,%ecx /* shrd only works for 0..31 bits */ + jnc xDenorm_shift_more_than_63 + + cmpl $32,%ecx /* shrd only works for 0..31 bits */ + jnc xDenorm_shift_more_than_32 + +/* + * We got here without jumps by assuming that the most common requirement + * is for a small de-normalising shift. + * Shift by [1..31] bits + */ + addl %ecx,EXP(%edi) + orl %edx,%edx /* extension */ + setne %ch /* Save whether %edx is non-zero */ + xorl %edx,%edx + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + orb %ch,%dl + popl %ecx + jmp xDenorm_done + +/* Shift by [32..63] bits */ +xDenorm_shift_more_than_32: + addl %ecx,EXP(%edi) + subb $32,%cl + orl %edx,%edx + setne %ch + orb %ch,%bl + xorl %edx,%edx + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + orl %edx,%edx /* test these 32 bits */ + setne %cl + orb %ch,%bl + orb %cl,%bl + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + popl %ecx + jmp xDenorm_done + +/* Shift by [64..) bits */ +xDenorm_shift_more_than_63: + cmpl $64,%ecx + jne xDenorm_shift_more_than_64 + +/* Exactly 64 bit shift */ + addl %ecx,EXP(%edi) + xorl %ecx,%ecx + orl %edx,%edx + setne %cl + orl %ebx,%ebx + setne %ch + orb %ch,%cl + orb %cl,%al + movl %eax,%edx + xorl %eax,%eax + xorl %ebx,%ebx + popl %ecx + jmp xDenorm_done + +xDenorm_shift_more_than_64: + movl EXP_UNDER+1,EXP(%edi) +/* This is easy, %eax must be non-zero, so.. */ + movl $1,%edx + xorl %eax,%eax + xorl %ebx,%ebx + popl %ecx + jmp xDenorm_done + + +xUnmasked_underflow: + movb UNMASKED_UNDERFLOW,FPU_denormal + jmp xDenorm_done + + +/* Undo the de-normalisation. */ +xNormalise_result: + cmpb UNMASKED_UNDERFLOW,FPU_denormal + je xSignal_underflow + +/* The number must be a denormal if we got here. */ +#ifdef PARANOID + /* But check it... just in case. */ + cmpl EXP_UNDER+1,EXP(%edi) + jne L_norm_bugged +#endif PARANOID + +#ifdef PECULIAR_486 + /* + * This implements a special feature of 80486 behaviour. + * Underflow will be signalled even if the number is + * not a denormal after rounding. + * This difference occurs only for masked underflow, and not + * in the unmasked case. + * Actual 80486 behaviour differs from this in some circumstances. + */ + orl %eax,%eax /* ms bits */ + js LNormalise_shift_done /* Will be masked underflow */ +#endif PECULIAR_486 + + orl %eax,%eax /* ms bits */ + js xL_Normalised /* No longer a denormal */ + + jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits */ + + orl %ebx,%ebx + jz L_underflow_to_zero /* The contents are zero */ + +/* Shift left 32 - 63 bits */ + movl %ebx,%eax + xorl %ebx,%ebx + subl $32,EXP(%edi) + +LNormalise_shift_up_to_31: + bsrl %eax,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + shld %cl,%ebx,%eax + shl %cl,%ebx + subl %ecx,EXP(%edi) + +LNormalise_shift_done: + testb $0xff,FPU_bits_lost /* bits lost == underflow */ + jz xL_Normalised + + /* There must be a masked underflow */ + push %eax + pushl EX_Underflow + call _exception + popl %eax + popl %eax + jmp xL_Normalised + + +/* + * The operations resulted in a number too small to represent. + * Masked response. + */ +L_underflow_to_zero: + push %eax + call _set_precision_flag_down + popl %eax + + push %eax + pushl EX_Underflow + call _exception + popl %eax + popl %eax + +/* Reduce the exponent to EXP_UNDER */ + movl EXP_UNDER,EXP(%edi) + movb TW_Zero,TAG(%edi) + jmp xL_Store_significand + + +/* The operations resulted in a number too large to represent. */ +L_overflow: + push %edi + call _arith_overflow + pop %edi + jmp fpu_reg_round_exit + + +xSignal_underflow: + /* The number may have been changed to a non-denormal */ + /* by the rounding operations. */ + cmpl EXP_UNDER,EXP(%edi) + jle xDo_unmasked_underflow + + jmp xL_Normalised + +xDo_unmasked_underflow: + /* Increase the exponent by the magic number */ + addl $(3*(1<<13)),EXP(%edi) + push %eax + pushl EX_Underflow + call EXCEPTION + popl %eax + popl %eax + jmp xL_Normalised + + +#ifdef PARANOID +#ifdef PECULIAR_486 +L_bugged_denorm_486: + pushl EX_INTERNAL|0x236 + call EXCEPTION + popl %ebx + jmp L_exception_exit +#else +L_bugged_denorm: + pushl EX_INTERNAL|0x230 + call EXCEPTION + popl %ebx + jmp L_exception_exit +#endif PECULIAR_486 + +L_bugged_round24: + pushl EX_INTERNAL|0x231 + call EXCEPTION + popl %ebx + jmp L_exception_exit + +L_bugged_round53: + pushl EX_INTERNAL|0x232 + call EXCEPTION + popl %ebx + jmp L_exception_exit + +L_bugged_round64: + pushl EX_INTERNAL|0x233 + call EXCEPTION + popl %ebx + jmp L_exception_exit + +L_norm_bugged: + pushl EX_INTERNAL|0x234 + call EXCEPTION + popl %ebx + jmp L_exception_exit + +L_entry_bugged: + pushl EX_INTERNAL|0x235 + call EXCEPTION + popl %ebx +L_exception_exit: + mov $1,%eax + jmp fpu_reg_round_exit +#endif PARANOID diff --git a/arch/i386/math-emu/reg_u_add.S b/arch/i386/math-emu/reg_u_add.S new file mode 100644 index 000000000..4410f8fd4 --- /dev/null +++ b/arch/i386/math-emu/reg_u_add.S @@ -0,0 +1,189 @@ + .file "reg_u_add.S" +/*---------------------------------------------------------------------------+ + | reg_u_add.S | + | | + | 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 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | + | int control_w) | + | | + +---------------------------------------------------------------------------*/ + +/* + | Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ). + | Takes two valid reg f.p. numbers (TW_Valid), which are + | treated as unsigned numbers, + | and returns their sum as a TW_Valid or TW_S f.p. number. + | The returned number is normalized. + | Basic checks are performed if PARANOID is defined. + */ + +#include "exception.h" +#include "fpu_asm.h" +#include "control_w.h" + +.text + .align 2,144 +.globl _reg_u_add +_reg_u_add: + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi /* source 1 */ + movl PARAM2,%edi /* source 2 */ + +#ifdef DENORM_OPERAND + cmpl EXP_UNDER,EXP(%esi) + jg xOp1_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xOp1_not_denorm: + cmpl EXP_UNDER,EXP(%edi) + jg xOp2_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xOp2_not_denorm: +#endif DENORM_OPERAND + + movl EXP(%esi),%ecx + subl EXP(%edi),%ecx /* exp1 - exp2 */ + jge L_arg1_larger + + /* num1 is smaller */ + movl SIGL(%esi),%ebx + movl SIGH(%esi),%eax + + movl %edi,%esi + negw %cx + jmp L_accum_loaded + +L_arg1_larger: + /* num1 has larger or equal exponent */ + movl SIGL(%edi),%ebx + movl SIGH(%edi),%eax + +L_accum_loaded: + movl PARAM3,%edi /* destination */ +/* movb SIGN(%esi),%dl + movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */ + + + movl EXP(%esi),%edx + movl %edx,EXP(%edi) /* Copy exponent to destination */ + + xorl %edx,%edx /* clear the extension */ + +#ifdef PARANOID + testl $0x80000000,%eax + je L_bugged + + testl $0x80000000,SIGH(%esi) + je L_bugged +#endif PARANOID + +/* The number to be shifted is in %eax:%ebx:%edx */ + cmpw $32,%cx /* shrd only works for 0..31 bits */ + jnc L_more_than_31 + +/* less than 32 bits */ + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + jmp L_shift_done + +L_more_than_31: + cmpw $64,%cx + jnc L_more_than_63 + + subb $32,%cl + jz L_exactly_32 + + shrd %cl,%eax,%edx + shr %cl,%eax + orl %ebx,%ebx + jz L_more_31_no_low /* none of the lowest bits is set */ + + orl $1,%edx /* record the fact in the extension */ + +L_more_31_no_low: + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_exactly_32: + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_more_than_63: + cmpw $65,%cx + jnc L_more_than_64 + + movl %eax,%edx + orl %ebx,%ebx + jz L_more_63_no_low + + orl $1,%edx + jmp L_more_63_no_low + +L_more_than_64: + movl $1,%edx /* The shifted nr always at least one '1' */ + +L_more_63_no_low: + xorl %ebx,%ebx + xorl %eax,%eax + +L_shift_done: + /* Now do the addition */ + addl SIGL(%esi),%ebx + adcl SIGH(%esi),%eax + jnc L_round_the_result + + /* Overflow, adjust the result */ + rcrl $1,%eax + rcrl $1,%ebx + rcrl $1,%edx + jnc L_no_bit_lost + + orl $1,%edx + +L_no_bit_lost: + incl EXP(%edi) + +L_round_the_result: + jmp fpu_reg_round /* Round the result */ + + + +#ifdef PARANOID +/* If we ever get here then we have problems! */ +L_bugged: + pushl EX_INTERNAL|0x201 + call EXCEPTION + pop %ebx + jmp L_exit +#endif PARANOID + + +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret diff --git a/arch/i386/math-emu/reg_u_div.S b/arch/i386/math-emu/reg_u_div.S new file mode 100644 index 000000000..328e9116e --- /dev/null +++ b/arch/i386/math-emu/reg_u_div.S @@ -0,0 +1,477 @@ + .file "reg_u_div.S" +/*---------------------------------------------------------------------------+ + | reg_u_div.S | + | | + | Core division routines | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Kernel for the division routines. | + | | + | void reg_u_div(FPU_REG *a, FPU_REG *a, | + | FPU_REG *dest, unsigned int control_word) | + | | + | Does not compute the destination exponent, but does adjust it. | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" +#include "control_w.h" + + +/* #define dSIGL(x) (x) */ +/* #define dSIGH(x) 4(x) */ + + +#ifndef NON_REENTRANT_FPU +/* + Local storage on the stack: + Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 + Overflow flag: ovfl_flag + */ +#define FPU_accum_3 -4(%ebp) +#define FPU_accum_2 -8(%ebp) +#define FPU_accum_1 -12(%ebp) +#define FPU_accum_0 -16(%ebp) +#define FPU_result_1 -20(%ebp) +#define FPU_result_2 -24(%ebp) +#define FPU_ovfl_flag -28(%ebp) + +#else +.data +/* + Local storage in a static area: + Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 + Overflow flag: ovfl_flag + */ + .align 2,0 +FPU_accum_3: + .long 0 +FPU_accum_2: + .long 0 +FPU_accum_1: + .long 0 +FPU_accum_0: + .long 0 +FPU_result_1: + .long 0 +FPU_result_2: + .long 0 +FPU_ovfl_flag: + .byte 0 +#endif NON_REENTRANT_FPU + + +.text + .align 2,144 + +.globl _reg_u_div + +.globl _divide_kernel + +_reg_u_div: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $28,%esp +#endif NON_REENTRANT_FPU + + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi /* pointer to num */ + movl PARAM2,%ebx /* pointer to denom */ + movl PARAM3,%edi /* pointer to answer */ + +#ifdef DENORM_OPERAND + movl EXP(%esi),%eax + cmpl EXP_UNDER,%eax + jg xOp1_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xOp1_not_denorm: + movl EXP(%ebx),%eax + cmpl EXP_UNDER,%eax + jg xOp2_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xOp2_not_denorm: +#endif DENORM_OPERAND + +_divide_kernel: +#ifdef PARANOID +/* testl $0x80000000, SIGH(%esi) // Dividend */ +/* je L_bugged */ + testl $0x80000000, SIGH(%ebx) /* Divisor */ + je L_bugged +#endif PARANOID + +/* Check if the divisor can be treated as having just 32 bits */ + cmpl $0,SIGL(%ebx) + jnz L_Full_Division /* Can't do a quick divide */ + +/* We should be able to zip through the division here */ + movl SIGH(%ebx),%ecx /* The divisor */ + movl SIGH(%esi),%edx /* Dividend */ + movl SIGL(%esi),%eax /* Dividend */ + + cmpl %ecx,%edx + setaeb FPU_ovfl_flag /* Keep a record */ + jb L_no_adjust + + subl %ecx,%edx /* Prevent the overflow */ + +L_no_adjust: + /* Divide the 64 bit number by the 32 bit denominator */ + divl %ecx + movl %eax,FPU_result_2 + + /* Work on the remainder of the first division */ + xorl %eax,%eax + divl %ecx + movl %eax,FPU_result_1 + + /* Work on the remainder of the 64 bit division */ + xorl %eax,%eax + divl %ecx + + testb $255,FPU_ovfl_flag /* was the num > denom ? */ + je L_no_overflow + + /* Do the shifting here */ + /* increase the exponent */ + incl EXP(%edi) + + /* shift the mantissa right one bit */ + stc /* To set the ms bit */ + rcrl FPU_result_2 + rcrl FPU_result_1 + rcrl %eax + +L_no_overflow: + jmp LRound_precision /* Do the rounding as required */ + + +/*---------------------------------------------------------------------------+ + | Divide: Return arg1/arg2 to arg3. | + | | + | This routine does not use the exponents of arg1 and arg2, but does | + | adjust the exponent of arg3. | + | | + | The maximum returned value is (ignoring exponents) | + | .ffffffff ffffffff | + | ------------------ = 1.ffffffff fffffffe | + | .80000000 00000000 | + | and the minimum is | + | .80000000 00000000 | + | ------------------ = .80000000 00000001 (rounded) | + | .ffffffff ffffffff | + | | + +---------------------------------------------------------------------------*/ + + +L_Full_Division: + /* Save extended dividend in local register */ + movl SIGL(%esi),%eax + movl %eax,FPU_accum_2 + movl SIGH(%esi),%eax + movl %eax,FPU_accum_3 + xorl %eax,%eax + movl %eax,FPU_accum_1 /* zero the extension */ + movl %eax,FPU_accum_0 /* zero the extension */ + + movl SIGL(%esi),%eax /* Get the current num */ + movl SIGH(%esi),%edx + +/*----------------------------------------------------------------------*/ +/* Initialization done. + Do the first 32 bits. */ + + movb $0,FPU_ovfl_flag + cmpl SIGH(%ebx),%edx /* Test for imminent overflow */ + jb LLess_than_1 + ja LGreater_than_1 + + cmpl SIGL(%ebx),%eax + jb LLess_than_1 + +LGreater_than_1: +/* The dividend is greater or equal, would cause overflow */ + setaeb FPU_ovfl_flag /* Keep a record */ + + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx /* Prevent the overflow */ + movl %eax,FPU_accum_2 + movl %edx,FPU_accum_3 + +LLess_than_1: +/* At this point, we have a dividend < divisor, with a record of + adjustment in FPU_ovfl_flag */ + + /* We will divide by a number which is too large */ + movl SIGH(%ebx),%ecx + addl $1,%ecx + jnc LFirst_div_not_1 + + /* here we need to divide by 100000000h, + i.e., no division at all.. */ + mov %edx,%eax + jmp LFirst_div_done + +LFirst_div_not_1: + divl %ecx /* Divide the numerator by the augmented + denom ms dw */ + +LFirst_div_done: + movl %eax,FPU_result_2 /* Put the result in the answer */ + + mull SIGH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,FPU_accum_2 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_3 + + movl FPU_result_2,%eax /* Get the result back */ + mull SIGL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + sbbl $0,FPU_accum_3 + je LDo_2nd_32_bits /* Must check for non-zero result here */ + +#ifdef PARANOID + jb L_bugged_1 +#endif PARANOID + + /* need to subtract another once of the denom */ + incl FPU_result_2 /* Correct the answer */ + + movl SIGL(%ebx),%eax + movl SIGH(%ebx),%edx + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + +#ifdef PARANOID + sbbl $0,FPU_accum_3 + jne L_bugged_1 /* Must check for non-zero result here */ +#endif PARANOID + +/*----------------------------------------------------------------------*/ +/* Half of the main problem is done, there is just a reduced numerator + to handle now. + Work with the second 32 bits, FPU_accum_0 not used from now on */ +LDo_2nd_32_bits: + movl FPU_accum_2,%edx /* get the reduced num */ + movl FPU_accum_1,%eax + + /* need to check for possible subsequent overflow */ + cmpl SIGH(%ebx),%edx + jb LDo_2nd_div + ja LPrevent_2nd_overflow + + cmpl SIGL(%ebx),%eax + jb LDo_2nd_div + +LPrevent_2nd_overflow: +/* The numerator is greater or equal, would cause overflow */ + /* prevent overflow */ + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx + movl %edx,FPU_accum_2 + movl %eax,FPU_accum_1 + + incl FPU_result_2 /* Reflect the subtraction in the answer */ + +#ifdef PARANOID + je L_bugged_2 /* Can't bump the result to 1.0 */ +#endif PARANOID + +LDo_2nd_div: + cmpl $0,%ecx /* augmented denom msw */ + jnz LSecond_div_not_1 + + /* %ecx == 0, we are dividing by 1.0 */ + mov %edx,%eax + jmp LSecond_div_done + +LSecond_div_not_1: + divl %ecx /* Divide the numerator by the denom ms dw */ + +LSecond_div_done: + movl %eax,FPU_result_1 /* Put the result in the answer */ + + mull SIGH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + movl FPU_result_1,%eax /* Get the result back */ + mull SIGL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,FPU_accum_0 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */ + sbbl $0,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + jz LDo_3rd_32_bits + +#ifdef PARANOID + cmpl $1,FPU_accum_2 + jne L_bugged_2 +#endif PARANOID + + /* need to subtract another once of the denom */ + movl SIGL(%ebx),%eax + movl SIGH(%ebx),%edx + subl %eax,FPU_accum_0 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_1 + sbbl $0,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 + jne L_bugged_2 +#endif PARANOID + + addl $1,FPU_result_1 /* Correct the answer */ + adcl $0,FPU_result_2 + +#ifdef PARANOID + jc L_bugged_2 /* Must check for non-zero result here */ +#endif PARANOID + +/*----------------------------------------------------------------------*/ +/* The division is essentially finished here, we just need to perform + tidying operations. + Deal with the 3rd 32 bits */ +LDo_3rd_32_bits: + movl FPU_accum_1,%edx /* get the reduced num */ + movl FPU_accum_0,%eax + + /* need to check for possible subsequent overflow */ + cmpl SIGH(%ebx),%edx /* denom */ + jb LRound_prep + ja LPrevent_3rd_overflow + + cmpl SIGL(%ebx),%eax /* denom */ + jb LRound_prep + +LPrevent_3rd_overflow: + /* prevent overflow */ + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx + movl %edx,FPU_accum_1 + movl %eax,FPU_accum_0 + + addl $1,FPU_result_1 /* Reflect the subtraction in the answer */ + adcl $0,FPU_result_2 + jne LRound_prep + jnc LRound_prep + + /* This is a tricky spot, there is an overflow of the answer */ + movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */ + +LRound_prep: +/* + * Prepare for rounding. + * To test for rounding, we just need to compare 2*accum with the + * denom. + */ + movl FPU_accum_0,%ecx + movl FPU_accum_1,%edx + movl %ecx,%eax + orl %edx,%eax + jz LRound_ovfl /* The accumulator contains zero. */ + + /* Multiply by 2 */ + clc + rcll $1,%ecx + rcll $1,%edx + jc LRound_large /* No need to compare, denom smaller */ + + subl SIGL(%ebx),%ecx + sbbl SIGH(%ebx),%edx + jnc LRound_not_small + + movl $0x70000000,%eax /* Denom was larger */ + jmp LRound_ovfl + +LRound_not_small: + jnz LRound_large + + movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */ + jmp LRound_ovfl + +LRound_large: + movl $0xff000000,%eax /* Denom was smaller */ + +LRound_ovfl: +/* We are now ready to deal with rounding, but first we must get + the bits properly aligned */ + testb $255,FPU_ovfl_flag /* was the num > denom ? */ + je LRound_precision + + incl EXP(%edi) + + /* shift the mantissa right one bit */ + stc /* Will set the ms bit */ + rcrl FPU_result_2 + rcrl FPU_result_1 + rcrl %eax + +/* Round the result as required */ +LRound_precision: + decl EXP(%edi) /* binary point between 1st & 2nd bits */ + + movl %eax,%edx + movl FPU_result_1,%ebx + movl FPU_result_2,%eax + jmp fpu_reg_round + + +#ifdef PARANOID +/* The logic is wrong if we got here */ +L_bugged: + pushl EX_INTERNAL|0x202 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_1: + pushl EX_INTERNAL|0x203 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_2: + pushl EX_INTERNAL|0x204 + call EXCEPTION + pop %ebx + jmp L_exit + +L_exit: + popl %ebx + popl %edi + popl %esi + + leave + ret +#endif PARANOID diff --git a/arch/i386/math-emu/reg_u_mul.S b/arch/i386/math-emu/reg_u_mul.S new file mode 100644 index 000000000..8250666bd --- /dev/null +++ b/arch/i386/math-emu/reg_u_mul.S @@ -0,0 +1,163 @@ + .file "reg_u_mul.S" +/*---------------------------------------------------------------------------+ + | reg_u_mul.S | + | | + | Core multiplication routine | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Basic multiplication routine. | + | Does not check the resulting exponent for overflow/underflow | + | | + | reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); | + | | + | Internal working is at approx 128 bits. | + | Result is rounded to nearest 53 or 64 bits, using "nearest or even". | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" +#include "control_w.h" + + + +#ifndef NON_REENTRANT_FPU +/* Local storage on the stack: */ +#define FPU_accum_0 -4(%ebp) /* ms word */ +#define FPU_accum_1 -8(%ebp) + +#else +/* Local storage in a static area: */ +.data + .align 4,0 +FPU_accum_0: + .long 0 +FPU_accum_1: + .long 0 +#endif NON_REENTRANT_FPU + + +.text + .align 2,144 + +.globl _reg_u_mul +_reg_u_mul: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $8,%esp +#endif NON_REENTRANT_FPU + + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi + movl PARAM2,%edi + +#ifdef PARANOID + testl $0x80000000,SIGH(%esi) + jz L_bugged + testl $0x80000000,SIGH(%edi) + jz L_bugged +#endif PARANOID + +#ifdef DENORM_OPERAND + movl EXP(%esi),%eax + cmpl EXP_UNDER,%eax + jg xOp1_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xOp1_not_denorm: + movl EXP(%edi),%eax + cmpl EXP_UNDER,%eax + jg xOp2_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xOp2_not_denorm: +#endif DENORM_OPERAND + + xorl %ecx,%ecx + xorl %ebx,%ebx + + movl SIGL(%esi),%eax + mull SIGL(%edi) + movl %eax,FPU_accum_0 + movl %edx,FPU_accum_1 + + movl SIGL(%esi),%eax + mull SIGH(%edi) + addl %eax,FPU_accum_1 + adcl %edx,%ebx +/* adcl $0,%ecx // overflow here is not possible */ + + movl SIGH(%esi),%eax + mull SIGL(%edi) + addl %eax,FPU_accum_1 + adcl %edx,%ebx + adcl $0,%ecx + + movl SIGH(%esi),%eax + mull SIGH(%edi) + addl %eax,%ebx + adcl %edx,%ecx + + movl EXP(%esi),%eax /* Compute the exponent */ + addl EXP(%edi),%eax + subl EXP_BIAS-1,%eax + +/* Have now finished with the sources */ + movl PARAM3,%edi /* Point to the destination */ + movl %eax,EXP(%edi) + +/* Now make sure that the result is normalized */ + testl $0x80000000,%ecx + jnz LResult_Normalised + + /* Normalize by shifting left one bit */ + shll $1,FPU_accum_0 + rcll $1,FPU_accum_1 + rcll $1,%ebx + rcll $1,%ecx + decl EXP(%edi) + +LResult_Normalised: + movl FPU_accum_0,%eax + movl FPU_accum_1,%edx + orl %eax,%eax + jz L_extent_zero + + orl $1,%edx + +L_extent_zero: + movl %ecx,%eax + jmp fpu_reg_round + + +#ifdef PARANOID +L_bugged: + pushl EX_INTERNAL|0x205 + call EXCEPTION + pop %ebx + jmp L_exit + +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret +#endif PARANOID + diff --git a/arch/i386/math-emu/reg_u_sub.S b/arch/i386/math-emu/reg_u_sub.S new file mode 100644 index 000000000..fbec17dfb --- /dev/null +++ b/arch/i386/math-emu/reg_u_sub.S @@ -0,0 +1,292 @@ + .file "reg_u_sub.S" +/*---------------------------------------------------------------------------+ + | reg_u_sub.S | + | | + | Core floating point subtraction routine. | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | + | int control_w) | + | | + +---------------------------------------------------------------------------*/ + +/* + | Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ). + | Takes two valid reg f.p. numbers (TW_Valid), which are + | treated as unsigned numbers, + | and returns their difference as a TW_Valid or TW_Zero f.p. + | number. + | The first number (arg1) must be the larger. + | The returned number is normalized. + | Basic checks are performed if PARANOID is defined. + */ + +#include "exception.h" +#include "fpu_asm.h" +#include "control_w.h" + +.text + .align 2,144 +.globl _reg_u_sub +_reg_u_sub: + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi /* source 1 */ + movl PARAM2,%edi /* source 2 */ + +#ifdef DENORM_OPERAND + cmpl EXP_UNDER,EXP(%esi) + jg xOp1_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xOp1_not_denorm: + cmpl EXP_UNDER,EXP(%edi) + jg xOp2_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz fpu_Arith_exit + +xOp2_not_denorm: +#endif DENORM_OPERAND + + movl EXP(%esi),%ecx + subl EXP(%edi),%ecx /* exp1 - exp2 */ + +#ifdef PARANOID + /* source 2 is always smaller than source 1 */ + js L_bugged_1 + + testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */ + je L_bugged_2 + + testl $0x80000000,SIGH(%esi) + je L_bugged_2 +#endif PARANOID + +/*--------------------------------------+ + | Form a register holding the | + | smaller number | + +--------------------------------------*/ + movl SIGH(%edi),%eax /* register ms word */ + movl SIGL(%edi),%ebx /* register ls word */ + + movl PARAM3,%edi /* destination */ + movl EXP(%esi),%edx + movl %edx,EXP(%edi) /* Copy exponent to destination */ +/* movb SIGN(%esi),%dl + movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */ + + xorl %edx,%edx /* register extension */ + +/*--------------------------------------+ + | Shift the temporary register | + | right the required number of | + | places. | + +--------------------------------------*/ +L_shift_r: + cmpl $32,%ecx /* shrd only works for 0..31 bits */ + jnc L_more_than_31 + +/* less than 32 bits */ + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + jmp L_shift_done + +L_more_than_31: + cmpl $64,%ecx + jnc L_more_than_63 + + subb $32,%cl + jz L_exactly_32 + + shrd %cl,%eax,%edx + shr %cl,%eax + orl %ebx,%ebx + jz L_more_31_no_low /* none of the lowest bits is set */ + + orl $1,%edx /* record the fact in the extension */ + +L_more_31_no_low: + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_exactly_32: + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_more_than_63: + cmpw $65,%cx + jnc L_more_than_64 + + /* Shift right by 64 bits */ + movl %eax,%edx + orl %ebx,%ebx + jz L_more_63_no_low + + orl $1,%edx + jmp L_more_63_no_low + +L_more_than_64: + jne L_more_than_65 + + /* Shift right by 65 bits */ + /* Carry is clear if we get here */ + movl %eax,%edx + rcrl %edx + jnc L_shift_65_nc + + orl $1,%edx + jmp L_more_63_no_low + +L_shift_65_nc: + orl %ebx,%ebx + jz L_more_63_no_low + + orl $1,%edx + jmp L_more_63_no_low + +L_more_than_65: + movl $1,%edx /* The shifted nr always at least one '1' */ + +L_more_63_no_low: + xorl %ebx,%ebx + xorl %eax,%eax + +L_shift_done: +L_subtr: +/*------------------------------+ + | Do the subtraction | + +------------------------------*/ + xorl %ecx,%ecx + subl %edx,%ecx + movl %ecx,%edx + movl SIGL(%esi),%ecx + sbbl %ebx,%ecx + movl %ecx,%ebx + movl SIGH(%esi),%ecx + sbbl %eax,%ecx + movl %ecx,%eax + +#ifdef PARANOID + /* We can never get a borrow */ + jc L_bugged +#endif PARANOID + +/*--------------------------------------+ + | Normalize the result | + +--------------------------------------*/ + testl $0x80000000,%eax + jnz L_round /* no shifting needed */ + + orl %eax,%eax + jnz L_shift_1 /* shift left 1 - 31 bits */ + + orl %ebx,%ebx + jnz L_shift_32 /* shift left 32 - 63 bits */ + +/* + * A rare case, the only one which is non-zero if we got here + * is: 1000000 .... 0000 + * -0111111 .... 1111 1 + * -------------------- + * 0000000 .... 0000 1 + */ + + cmpl $0x80000000,%edx + jnz L_must_be_zero + + /* Shift left 64 bits */ + subl $64,EXP(%edi) + xchg %edx,%eax + jmp fpu_reg_round + +L_must_be_zero: +#ifdef PARANOID + orl %edx,%edx + jnz L_bugged_3 +#endif PARANOID + + /* The result is zero */ + movb TW_Zero,TAG(%edi) + movl $0,EXP(%edi) /* exponent */ + movl $0,SIGL(%edi) + movl $0,SIGH(%edi) + jmp L_exit /* %eax contains zero */ + +L_shift_32: + movl %ebx,%eax + movl %edx,%ebx + movl $0,%edx + subl $32,EXP(%edi) /* Can get underflow here */ + +/* We need to shift left by 1 - 31 bits */ +L_shift_1: + bsrl %eax,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + shld %cl,%ebx,%eax + shld %cl,%edx,%ebx + shl %cl,%edx + subl %ecx,EXP(%edi) /* Can get underflow here */ + +L_round: + jmp fpu_reg_round /* Round the result */ + + +#ifdef PARANOID +L_bugged_1: + pushl EX_INTERNAL|0x206 + call EXCEPTION + pop %ebx + jmp L_error_exit + +L_bugged_2: + pushl EX_INTERNAL|0x209 + call EXCEPTION + pop %ebx + jmp L_error_exit + +L_bugged_3: + pushl EX_INTERNAL|0x210 + call EXCEPTION + pop %ebx + jmp L_error_exit + +L_bugged_4: + pushl EX_INTERNAL|0x211 + call EXCEPTION + pop %ebx + jmp L_error_exit + +L_bugged: + pushl EX_INTERNAL|0x212 + call EXCEPTION + pop %ebx + jmp L_error_exit +#endif PARANOID + + +L_error_exit: + movl $1,%eax +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret diff --git a/arch/i386/math-emu/round_Xsig.S b/arch/i386/math-emu/round_Xsig.S new file mode 100644 index 000000000..163755878 --- /dev/null +++ b/arch/i386/math-emu/round_Xsig.S @@ -0,0 +1,148 @@ +/*---------------------------------------------------------------------------+ + | round_Xsig.S | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Normalize and round a 12 byte quantity. | + | Call from C as: | + | int round_Xsig(Xsig *n) | + | | + | Normalize a 12 byte quantity. | + | Call from C as: | + | int norm_Xsig(Xsig *n) | + | | + | Each function returns the size of the shift (nr of bits). | + | | + +---------------------------------------------------------------------------*/ + .file "round_Xsig.S" + +#include "fpu_asm.h" + + +.text + + .align 2,144 +.globl _round_Xsig + +_round_Xsig: + pushl %ebp + movl %esp,%ebp + pushl %ebx /* Reserve some space */ + pushl %ebx + pushl %esi + + movl PARAM1,%esi + + movl 8(%esi),%edx + movl 4(%esi),%ebx + movl (%esi),%eax + + movl $0,-4(%ebp) + + orl %edx,%edx /* ms bits */ + js L_round /* Already normalized */ + jnz L_shift_1 /* Shift left 1 - 31 bits */ + + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + movl $-32,-4(%ebp) + +/* We need to shift left by 1 - 31 bits */ +L_shift_1: + bsrl %edx,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + subl %ecx,-4(%ebp) + shld %cl,%ebx,%edx + shld %cl,%eax,%ebx + shl %cl,%eax + +L_round: + testl $0x80000000,%eax + jz L_exit + + addl $1,%ebx + adcl $0,%edx + jnz L_exit + + movl $0x80000000,%edx + incl -4(%ebp) + +L_exit: + movl %edx,8(%esi) + movl %ebx,4(%esi) + movl %eax,(%esi) + + movl -4(%ebp),%eax + + popl %esi + popl %ebx + leave + ret + + + + + .align 2,144 +.globl _norm_Xsig + +_norm_Xsig: + pushl %ebp + movl %esp,%ebp + pushl %ebx /* Reserve some space */ + pushl %ebx + pushl %esi + + movl PARAM1,%esi + + movl 8(%esi),%edx + movl 4(%esi),%ebx + movl (%esi),%eax + + movl $0,-4(%ebp) + + orl %edx,%edx /* ms bits */ + js L_n_exit /* Already normalized */ + jnz L_n_shift_1 /* Shift left 1 - 31 bits */ + + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + movl $-32,-4(%ebp) + + orl %edx,%edx /* ms bits */ + js L_n_exit /* Normalized now */ + jnz L_n_shift_1 /* Shift left 1 - 31 bits */ + + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + addl $-32,-4(%ebp) + jmp L_n_exit /* Might not be normalized, + but shift no more. */ + +/* We need to shift left by 1 - 31 bits */ +L_n_shift_1: + bsrl %edx,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + subl %ecx,-4(%ebp) + shld %cl,%ebx,%edx + shld %cl,%eax,%ebx + shl %cl,%eax + +L_n_exit: + movl %edx,8(%esi) + movl %ebx,4(%esi) + movl %eax,(%esi) + + movl -4(%ebp),%eax + + popl %esi + popl %ebx + leave + ret + diff --git a/arch/i386/math-emu/shr_Xsig.S b/arch/i386/math-emu/shr_Xsig.S new file mode 100644 index 000000000..d6724a204 --- /dev/null +++ b/arch/i386/math-emu/shr_Xsig.S @@ -0,0 +1,90 @@ + .file "shr_Xsig.S" +/*---------------------------------------------------------------------------+ + | shr_Xsig.S | + | | + | 12 byte right shift function | + | | + | Copyright (C) 1992,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void shr_Xsig(Xsig *arg, unsigned nr) | + | | + | Extended shift right function. | + | Fastest for small shifts. | + | Shifts the 12 byte quantity pointed to by the first arg (arg) | + | right by the number of bits specified by the second arg (nr). | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_asm.h" + +.text + .align 2,144 + + .globl _shr_Xsig +_shr_Xsig: + push %ebp + movl %esp,%ebp + pushl %esi + movl PARAM2,%ecx + movl PARAM1,%esi + cmpl $32,%ecx /* shrd only works for 0..31 bits */ + jnc L_more_than_31 + +/* less than 32 bits */ + pushl %ebx + movl (%esi),%eax /* lsl */ + movl 4(%esi),%ebx /* midl */ + movl 8(%esi),%edx /* msl */ + shrd %cl,%ebx,%eax + shrd %cl,%edx,%ebx + shr %cl,%edx + movl %eax,(%esi) + movl %ebx,4(%esi) + movl %edx,8(%esi) + popl %ebx + popl %esi + leave + ret + +L_more_than_31: + cmpl $64,%ecx + jnc L_more_than_63 + + subb $32,%cl + movl 4(%esi),%eax /* midl */ + movl 8(%esi),%edx /* msl */ + shrd %cl,%edx,%eax + shr %cl,%edx + movl %eax,(%esi) + movl %edx,4(%esi) + movl $0,8(%esi) + popl %esi + leave + ret + +L_more_than_63: + cmpl $96,%ecx + jnc L_more_than_95 + + subb $64,%cl + movl 8(%esi),%eax /* msl */ + shr %cl,%eax + xorl %edx,%edx + movl %eax,(%esi) + movl %edx,4(%esi) + movl %edx,8(%esi) + popl %esi + leave + ret + +L_more_than_95: + xorl %eax,%eax + movl %eax,(%esi) + movl %eax,4(%esi) + movl %eax,8(%esi) + popl %esi + leave + ret diff --git a/arch/i386/math-emu/status_w.h b/arch/i386/math-emu/status_w.h new file mode 100644 index 000000000..96607d0e1 --- /dev/null +++ b/arch/i386/math-emu/status_w.h @@ -0,0 +1,65 @@ +/*---------------------------------------------------------------------------+ + | status_w.h | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#include "fpu_emu.h" /* for definition of PECULIAR_486 */ + +#ifdef __ASSEMBLER__ +#define Const__(x) $##x +#else +#define Const__(x) x +#endif + +#define SW_Backward Const__(0x8000) /* backward compatibility */ +#define SW_C3 Const__(0x4000) /* condition bit 3 */ +#define SW_Top Const__(0x3800) /* top of stack */ +#define SW_Top_Shift Const__(11) /* shift for top of stack bits */ +#define SW_C2 Const__(0x0400) /* condition bit 2 */ +#define SW_C1 Const__(0x0200) /* condition bit 1 */ +#define SW_C0 Const__(0x0100) /* condition bit 0 */ +#define SW_Summary Const__(0x0080) /* exception summary */ +#define SW_Stack_Fault Const__(0x0040) /* stack fault */ +#define SW_Precision Const__(0x0020) /* loss of precision */ +#define SW_Underflow Const__(0x0010) /* underflow */ +#define SW_Overflow Const__(0x0008) /* overflow */ +#define SW_Zero_Div Const__(0x0004) /* divide by zero */ +#define SW_Denorm_Op Const__(0x0002) /* denormalized operand */ +#define SW_Invalid Const__(0x0001) /* invalid operation */ + +#define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */ + +#ifndef __ASSEMBLER__ + +#define COMP_A_gt_B 1 +#define COMP_A_eq_B 2 +#define COMP_A_lt_B 3 +#define COMP_No_Comp 4 +#define COMP_Denormal 0x20 +#define COMP_NaN 0x40 +#define COMP_SNaN 0x80 + +#define status_word() \ + ((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top)) +#define setcc(cc) ({ \ + partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \ + partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); }) + +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + /* Clear the SW_C1 bit, "other bits undefined". */ +# define clear_C1() { partial_status &= ~SW_C1; } +# else +# define clear_C1() +#endif PECULIAR_486 + +#endif __ASSEMBLER__ + +#endif _STATUS_H_ diff --git a/arch/i386/math-emu/version.h b/arch/i386/math-emu/version.h new file mode 100644 index 000000000..4c75a4792 --- /dev/null +++ b/arch/i386/math-emu/version.h @@ -0,0 +1,12 @@ +/*---------------------------------------------------------------------------+ + | version.h | + | | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#define FPU_VERSION "wm-FPU-emu version 1.20" diff --git a/arch/i386/math-emu/wm_shrx.S b/arch/i386/math-emu/wm_shrx.S new file mode 100644 index 000000000..bef0e1963 --- /dev/null +++ b/arch/i386/math-emu/wm_shrx.S @@ -0,0 +1,208 @@ + .file "wm_shrx.S" +/*---------------------------------------------------------------------------+ + | wm_shrx.S | + | | + | 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 | + | | + | Call from C as: | + | unsigned shrx(void *arg1, unsigned arg2) | + | and | + | unsigned shrxs(void *arg1, unsigned arg2) | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_asm.h" + +.text + .align 2,144 + +/*---------------------------------------------------------------------------+ + | unsigned shrx(void *arg1, unsigned arg2) | + | | + | Extended shift right function. | + | Fastest for small shifts. | + | Shifts the 64 bit quantity pointed to by the first arg (arg1) | + | right by the number of bits specified by the second arg (arg2). | + | Forms a 96 bit quantity from the 64 bit arg and eax: | + | [ 64 bit arg ][ eax ] | + | shift right ---------> | + | The eax register is initialized to 0 before the shifting. | + | Results returned in the 64 bit arg and eax. | + +---------------------------------------------------------------------------*/ + + .globl _shrx + +_shrx: + push %ebp + movl %esp,%ebp + pushl %esi + movl PARAM2,%ecx + movl PARAM1,%esi + cmpl $32,%ecx /* shrd only works for 0..31 bits */ + jnc L_more_than_31 + +/* less than 32 bits */ + pushl %ebx + movl (%esi),%ebx /* lsl */ + movl 4(%esi),%edx /* msl */ + xorl %eax,%eax /* extension */ + shrd %cl,%ebx,%eax + shrd %cl,%edx,%ebx + shr %cl,%edx + movl %ebx,(%esi) + movl %edx,4(%esi) + popl %ebx + popl %esi + leave + ret + +L_more_than_31: + cmpl $64,%ecx + jnc L_more_than_63 + + subb $32,%cl + movl (%esi),%eax /* lsl */ + movl 4(%esi),%edx /* msl */ + shrd %cl,%edx,%eax + shr %cl,%edx + movl %edx,(%esi) + movl $0,4(%esi) + popl %esi + leave + ret + +L_more_than_63: + cmpl $96,%ecx + jnc L_more_than_95 + + subb $64,%cl + movl 4(%esi),%eax /* msl */ + shr %cl,%eax + xorl %edx,%edx + movl %edx,(%esi) + movl %edx,4(%esi) + popl %esi + leave + ret + +L_more_than_95: + xorl %eax,%eax + movl %eax,(%esi) + movl %eax,4(%esi) + popl %esi + leave + ret + + +/*---------------------------------------------------------------------------+ + | unsigned shrxs(void *arg1, unsigned arg2) | + | | + | Extended shift right function (optimized for small floating point | + | integers). | + | Shifts the 64 bit quantity pointed to by the first arg (arg1) | + | right by the number of bits specified by the second arg (arg2). | + | Forms a 96 bit quantity from the 64 bit arg and eax: | + | [ 64 bit arg ][ eax ] | + | shift right ---------> | + | The eax register is initialized to 0 before the shifting. | + | The lower 8 bits of eax are lost and replaced by a flag which is | + | set (to 0x01) if any bit, apart from the first one, is set in the | + | part which has been shifted out of the arg. | + | Results returned in the 64 bit arg and eax. | + +---------------------------------------------------------------------------*/ + .globl _shrxs +_shrxs: + push %ebp + movl %esp,%ebp + pushl %esi + pushl %ebx + movl PARAM2,%ecx + movl PARAM1,%esi + cmpl $64,%ecx /* shrd only works for 0..31 bits */ + jnc Ls_more_than_63 + + cmpl $32,%ecx /* shrd only works for 0..31 bits */ + jc Ls_less_than_32 + +/* We got here without jumps by assuming that the most common requirement + is for small integers */ +/* Shift by [32..63] bits */ + subb $32,%cl + movl (%esi),%eax /* lsl */ + movl 4(%esi),%edx /* msl */ + xorl %ebx,%ebx + shrd %cl,%eax,%ebx + shrd %cl,%edx,%eax + shr %cl,%edx + orl %ebx,%ebx /* test these 32 bits */ + setne %bl + test $0x7fffffff,%eax /* and 31 bits here */ + setne %bh + orw %bx,%bx /* Any of the 63 bit set ? */ + setne %al + movl %edx,(%esi) + movl $0,4(%esi) + popl %ebx + popl %esi + leave + ret + +/* Shift by [0..31] bits */ +Ls_less_than_32: + movl (%esi),%ebx /* lsl */ + movl 4(%esi),%edx /* msl */ + xorl %eax,%eax /* extension */ + shrd %cl,%ebx,%eax + shrd %cl,%edx,%ebx + shr %cl,%edx + test $0x7fffffff,%eax /* only need to look at eax here */ + setne %al + movl %ebx,(%esi) + movl %edx,4(%esi) + popl %ebx + popl %esi + leave + ret + +/* Shift by [64..95] bits */ +Ls_more_than_63: + cmpl $96,%ecx + jnc Ls_more_than_95 + + subb $64,%cl + movl (%esi),%ebx /* lsl */ + movl 4(%esi),%eax /* msl */ + xorl %edx,%edx /* extension */ + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + orl %ebx,%edx + setne %bl + test $0x7fffffff,%eax /* only need to look at eax here */ + setne %bh + orw %bx,%bx + setne %al + xorl %edx,%edx + movl %edx,(%esi) /* set to zero */ + movl %edx,4(%esi) /* set to zero */ + popl %ebx + popl %esi + leave + ret + +Ls_more_than_95: +/* Shift by [96..inf) bits */ + xorl %eax,%eax + movl (%esi),%ebx + orl 4(%esi),%ebx + setne %al + xorl %ebx,%ebx + movl %ebx,(%esi) + movl %ebx,4(%esi) + popl %ebx + popl %esi + leave + ret diff --git a/arch/i386/math-emu/wm_sqrt.S b/arch/i386/math-emu/wm_sqrt.S new file mode 100644 index 000000000..4e028cb80 --- /dev/null +++ b/arch/i386/math-emu/wm_sqrt.S @@ -0,0 +1,474 @@ + .file "wm_sqrt.S" +/*---------------------------------------------------------------------------+ + | wm_sqrt.S | + | | + | Fixed point arithmetic square root evaluation. | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void wm_sqrt(FPU_REG *n, unsigned int control_word) | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | wm_sqrt(FPU_REG *n, unsigned int control_word) | + | returns the square root of n in n. | + | | + | Use Newton's method to compute the square root of a number, which must | + | be in the range [1.0 .. 4.0), to 64 bits accuracy. | + | Does not check the sign or tag of the argument. | + | Sets the exponent, but not the sign or tag of the result. | + | | + | The guess is kept in %esi:%edi | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" + + +#ifndef NON_REENTRANT_FPU +/* Local storage on the stack: */ +#define FPU_accum_3 -4(%ebp) /* ms word */ +#define FPU_accum_2 -8(%ebp) +#define FPU_accum_1 -12(%ebp) +#define FPU_accum_0 -16(%ebp) + +/* + * The de-normalised argument: + * sq_2 sq_1 sq_0 + * b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0 + * ^ binary point here + */ +#define FPU_fsqrt_arg_2 -20(%ebp) /* ms word */ +#define FPU_fsqrt_arg_1 -24(%ebp) +#define FPU_fsqrt_arg_0 -28(%ebp) /* ls word, at most the ms bit is set */ + +#else +/* Local storage in a static area: */ +.data + .align 4,0 +FPU_accum_3: + .long 0 /* ms word */ +FPU_accum_2: + .long 0 +FPU_accum_1: + .long 0 +FPU_accum_0: + .long 0 + +/* The de-normalised argument: + sq_2 sq_1 sq_0 + b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0 + ^ binary point here + */ +FPU_fsqrt_arg_2: + .long 0 /* ms word */ +FPU_fsqrt_arg_1: + .long 0 +FPU_fsqrt_arg_0: + .long 0 /* ls word, at most the ms bit is set */ +#endif NON_REENTRANT_FPU + + +.text + .align 2,144 + +.globl _wm_sqrt +_wm_sqrt: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $28,%esp +#endif NON_REENTRANT_FPU + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi + + movl SIGH(%esi),%eax + movl SIGL(%esi),%ecx + xorl %edx,%edx + +/* We use a rough linear estimate for the first guess.. */ + + cmpl EXP_BIAS,EXP(%esi) + jnz sqrt_arg_ge_2 + + shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */ + rcrl $1,%ecx + rcrl $1,%edx + +sqrt_arg_ge_2: +/* From here on, n is never accessed directly again until it is + replaced by the answer. */ + + movl %eax,FPU_fsqrt_arg_2 /* ms word of n */ + movl %ecx,FPU_fsqrt_arg_1 + movl %edx,FPU_fsqrt_arg_0 + +/* Make a linear first estimate */ + shrl $1,%eax + addl $0x40000000,%eax + movl $0xaaaaaaaa,%ecx + mull %ecx + shll %edx /* max result was 7fff... */ + testl $0x80000000,%edx /* but min was 3fff... */ + jnz sqrt_prelim_no_adjust + + movl $0x80000000,%edx /* round up */ + +sqrt_prelim_no_adjust: + movl %edx,%esi /* Our first guess */ + +/* We have now computed (approx) (2 + x) / 3, which forms the basis + for a few iterations of Newton's method */ + + movl FPU_fsqrt_arg_2,%ecx /* ms word */ + +/* + * From our initial estimate, three iterations are enough to get us + * to 30 bits or so. This will then allow two iterations at better + * precision to complete the process. + */ + +/* Compute (g + n/g)/2 at each iteration (g is the guess). */ + shrl %ecx /* Doing this first will prevent a divide */ + /* overflow later. */ + + movl %ecx,%edx /* msw of the arg / 2 */ + divl %esi /* current estimate */ + shrl %esi /* divide by 2 */ + addl %eax,%esi /* the new estimate */ + + movl %ecx,%edx + divl %esi + shrl %esi + addl %eax,%esi + + movl %ecx,%edx + divl %esi + shrl %esi + addl %eax,%esi + +/* + * Now that an estimate accurate to about 30 bits has been obtained (in %esi), + * we improve it to 60 bits or so. + * + * The strategy from now on is to compute new estimates from + * guess := guess + (n - guess^2) / (2 * guess) + */ + +/* First, find the square of the guess */ + movl %esi,%eax + mull %esi +/* guess^2 now in %edx:%eax */ + + movl FPU_fsqrt_arg_1,%ecx + subl %ecx,%eax + movl FPU_fsqrt_arg_2,%ecx /* ms word of normalized n */ + sbbl %ecx,%edx + jnc sqrt_stage_2_positive + +/* Subtraction gives a negative result, + negate the result before division. */ + notl %edx + notl %eax + addl $1,%eax + adcl $0,%edx + + divl %esi + movl %eax,%ecx + + movl %edx,%eax + divl %esi + jmp sqrt_stage_2_finish + +sqrt_stage_2_positive: + divl %esi + movl %eax,%ecx + + movl %edx,%eax + divl %esi + + notl %ecx + notl %eax + addl $1,%eax + adcl $0,%ecx + +sqrt_stage_2_finish: + sarl $1,%ecx /* divide by 2 */ + rcrl $1,%eax + + /* Form the new estimate in %esi:%edi */ + movl %eax,%edi + addl %ecx,%esi + + jnz sqrt_stage_2_done /* result should be [1..2) */ + +#ifdef PARANOID +/* It should be possible to get here only if the arg is ffff....ffff */ + cmp $0xffffffff,FPU_fsqrt_arg_1 + jnz sqrt_stage_2_error +#endif PARANOID + +/* The best rounded result. */ + xorl %eax,%eax + decl %eax + movl %eax,%edi + movl %eax,%esi + movl $0x7fffffff,%eax + jmp sqrt_round_result + +#ifdef PARANOID +sqrt_stage_2_error: + pushl EX_INTERNAL|0x213 + call EXCEPTION +#endif PARANOID + +sqrt_stage_2_done: + +/* Now the square root has been computed to better than 60 bits. */ + +/* Find the square of the guess. */ + movl %edi,%eax /* ls word of guess */ + mull %edi + movl %edx,FPU_accum_1 + + movl %esi,%eax + mull %esi + movl %edx,FPU_accum_3 + movl %eax,FPU_accum_2 + + movl %edi,%eax + mull %esi + addl %eax,FPU_accum_1 + adcl %edx,FPU_accum_2 + adcl $0,FPU_accum_3 + +/* movl %esi,%eax */ +/* mull %edi */ + addl %eax,FPU_accum_1 + adcl %edx,FPU_accum_2 + adcl $0,FPU_accum_3 + +/* guess^2 now in FPU_accum_3:FPU_accum_2:FPU_accum_1 */ + + movl FPU_fsqrt_arg_0,%eax /* get normalized n */ + subl %eax,FPU_accum_1 + movl FPU_fsqrt_arg_1,%eax + sbbl %eax,FPU_accum_2 + movl FPU_fsqrt_arg_2,%eax /* ms word of normalized n */ + sbbl %eax,FPU_accum_3 + jnc sqrt_stage_3_positive + +/* Subtraction gives a negative result, + negate the result before division */ + notl FPU_accum_1 + notl FPU_accum_2 + notl FPU_accum_3 + addl $1,FPU_accum_1 + adcl $0,FPU_accum_2 + +#ifdef PARANOID + adcl $0,FPU_accum_3 /* This must be zero */ + jz sqrt_stage_3_no_error + +sqrt_stage_3_error: + pushl EX_INTERNAL|0x207 + call EXCEPTION + +sqrt_stage_3_no_error: +#endif PARANOID + + movl FPU_accum_2,%edx + movl FPU_accum_1,%eax + divl %esi + movl %eax,%ecx + + movl %edx,%eax + divl %esi + + sarl $1,%ecx /* divide by 2 */ + rcrl $1,%eax + + /* prepare to round the result */ + + addl %ecx,%edi + adcl $0,%esi + + jmp sqrt_stage_3_finished + +sqrt_stage_3_positive: + movl FPU_accum_2,%edx + movl FPU_accum_1,%eax + divl %esi + movl %eax,%ecx + + movl %edx,%eax + divl %esi + + sarl $1,%ecx /* divide by 2 */ + rcrl $1,%eax + + /* prepare to round the result */ + + notl %eax /* Negate the correction term */ + notl %ecx + addl $1,%eax + adcl $0,%ecx /* carry here ==> correction == 0 */ + adcl $0xffffffff,%esi + + addl %ecx,%edi + adcl $0,%esi + +sqrt_stage_3_finished: + +/* + * The result in %esi:%edi:%esi should be good to about 90 bits here, + * and the rounding information here does not have sufficient accuracy + * in a few rare cases. + */ + cmpl $0xffffffe0,%eax + ja sqrt_near_exact_x + + cmpl $0x00000020,%eax + jb sqrt_near_exact + + cmpl $0x7fffffe0,%eax + jb sqrt_round_result + + cmpl $0x80000020,%eax + jb sqrt_get_more_precision + +sqrt_round_result: +/* Set up for rounding operations */ + movl %eax,%edx + movl %esi,%eax + movl %edi,%ebx + movl PARAM1,%edi + movl EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0) */ + movl PARAM2,%ecx + jmp fpu_reg_round_sqrt + + +sqrt_near_exact_x: +/* First, the estimate must be rounded up. */ + addl $1,%edi + adcl $0,%esi + +sqrt_near_exact: +/* + * This is an easy case because x^1/2 is monotonic. + * We need just find the square of our estimate, compare it + * with the argument, and deduce whether our estimate is + * above, below, or exact. We use the fact that the estimate + * is known to be accurate to about 90 bits. + */ + movl %edi,%eax /* ls word of guess */ + mull %edi + movl %edx,%ebx /* 2nd ls word of square */ + movl %eax,%ecx /* ls word of square */ + + movl %edi,%eax + mull %esi + addl %eax,%ebx + addl %eax,%ebx + +#ifdef PARANOID + cmp $0xffffffb0,%ebx + jb sqrt_near_exact_ok + + cmp $0x00000050,%ebx + ja sqrt_near_exact_ok + + pushl EX_INTERNAL|0x214 + call EXCEPTION + +sqrt_near_exact_ok: +#endif PARANOID + + or %ebx,%ebx + js sqrt_near_exact_small + + jnz sqrt_near_exact_large + + or %ebx,%edx + jnz sqrt_near_exact_large + +/* Our estimate is exactly the right answer */ + xorl %eax,%eax + jmp sqrt_round_result + +sqrt_near_exact_small: +/* Our estimate is too small */ + movl $0x000000ff,%eax + jmp sqrt_round_result + +sqrt_near_exact_large: +/* Our estimate is too large, we need to decrement it */ + subl $1,%edi + sbbl $0,%esi + movl $0xffffff00,%eax + jmp sqrt_round_result + + +sqrt_get_more_precision: +/* This case is almost the same as the above, except we start + with an extra bit of precision in the estimate. */ + stc /* The extra bit. */ + rcll $1,%edi /* Shift the estimate left one bit */ + rcll $1,%esi + + movl %edi,%eax /* ls word of guess */ + mull %edi + movl %edx,%ebx /* 2nd ls word of square */ + movl %eax,%ecx /* ls word of square */ + + movl %edi,%eax + mull %esi + addl %eax,%ebx + addl %eax,%ebx + +/* Put our estimate back to its original value */ + stc /* The ms bit. */ + rcrl $1,%esi /* Shift the estimate left one bit */ + rcrl $1,%edi + +#ifdef PARANOID + cmp $0xffffff60,%ebx + jb sqrt_more_prec_ok + + cmp $0x000000a0,%ebx + ja sqrt_more_prec_ok + + pushl EX_INTERNAL|0x215 + call EXCEPTION + +sqrt_more_prec_ok: +#endif PARANOID + + or %ebx,%ebx + js sqrt_more_prec_small + + jnz sqrt_more_prec_large + + or %ebx,%ecx + jnz sqrt_more_prec_large + +/* Our estimate is exactly the right answer */ + movl $0x80000000,%eax + jmp sqrt_round_result + +sqrt_more_prec_small: +/* Our estimate is too small */ + movl $0x800000ff,%eax + jmp sqrt_round_result + +sqrt_more_prec_large: +/* Our estimate is too large */ + movl $0x7fffff00,%eax + jmp sqrt_round_result diff --git a/arch/i386/mm/Makefile b/arch/i386/mm/Makefile index 5063d60c2..af75a20a4 100644 --- a/arch/i386/mm/Makefile +++ b/arch/i386/mm/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the linux memory manager. +# Makefile for the linux i386-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here @@ -14,11 +14,13 @@ .c.s: $(CC) $(CFLAGS) -S $< -OBJS = memory.o swap.o mmap.o mprotect.o kmalloc.o vmalloc.o +OBJS = init.o fault.o mm.o: $(OBJS) $(LD) -r -o mm.o $(OBJS) +modules: + dep: $(CPP) -M *.c > .depend diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c new file mode 100644 index 000000000..01c259f0f --- /dev/null +++ b/arch/i386/mm/fault.c @@ -0,0 +1,135 @@ +/* + * linux/arch/i386/mm/fault.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> + +extern void die_if_kernel(char *,struct pt_regs *,long); + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * error_code: + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + * bit 2 == 0 means kernel, 1 means user-mode + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + struct vm_area_struct * vma; + unsigned long address; + unsigned long page; + + /* get the address */ + __asm__("movl %%cr2,%0":"=r" (address)); + vma = find_vma(current, 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) + 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))) + goto bad_area; + } + /* + * 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; + } + do_no_page(vma, address, error_code & 2); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + if (error_code & 4) { + current->tss.cr2 = address; + current->tss.error_code = error_code; + current->tss.trap_no = 14; + send_sig(SIGSEGV, current, 1); + return; + } +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + * + * First we check if it was the bootup rw-test, though.. + */ + if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & 1)) { + wp_works_ok = 1; + pg0[0] = pte_val(mk_pte(0, PAGE_SHARED)); + invalidate(); + printk("This processor honours the WP bit even when in supervisor mode. Good.\n"); + return; + } + if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) { + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + pg0[0] = pte_val(mk_pte(0, PAGE_SHARED)); + } else + printk(KERN_ALERT "Unable to handle kernel paging request"); + 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]; + printk(KERN_ALERT "*pde = %08lx\n", page); + if (page & 1) { + page &= PAGE_MASK; + address &= 0x003ff000; + page = ((unsigned long *) page)[address >> PAGE_SHIFT]; + printk(KERN_ALERT "*pte = %08lx\n", page); + } + die_if_kernel("Oops", regs, error_code); + do_exit(SIGKILL); +} diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c new file mode 100644 index 000000000..296595644 --- /dev/null +++ b/arch/i386/mm/init.c @@ -0,0 +1,239 @@ +/* + * linux/arch/i386/mm/init.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.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); + +/* + * 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 + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +pte_t * __bad_pagetable(void) +{ + extern char empty_bad_page_table[PAGE_SIZE]; + + __asm__ __volatile__("cld ; rep ; stosl": + :"a" (pte_val(BAD_PAGE)), + "D" ((long) empty_bad_page_table), + "c" (PAGE_SIZE/4) + :"di","cx"); + return (pte_t *) empty_bad_page_table; +} + +pte_t __bad_page(void) +{ + extern char empty_bad_page[PAGE_SIZE]; + + __asm__ __volatile__("cld ; rep ; stosl": + :"a" (0), + "D" ((long) empty_bad_page), + "c" (PAGE_SIZE/4) + :"di","cx"); + return pte_mkdirty(mk_pte((unsigned long) empty_bad_page, PAGE_SHARED)); +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = high_memory >> PAGE_SHIFT; + while (i-- > 0) { + total++; + if (mem_map[i] & MAP_PAGE_RESERVED) + reserved++; + else if (!mem_map[i]) + free++; + else + shared += mem_map[i]-1; + } + printk("%d pages of RAM\n",total); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + show_buffers(); +#ifdef CONFIG_NET + show_net_buffers(); +#endif +} + +extern unsigned long free_area_init(unsigned long, unsigned long); + +/* + * paging_init() sets up the page tables - note that the first 4MB are + * already mapped by head.S. + * + * This routines also unmaps the page at virtual kernel address 0, so + * that we can trap those pesky NULL-reference errors in the kernel. + */ +unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) +{ + pgd_t * pg_dir; + pte_t * pg_table; + unsigned long tmp; + unsigned long address; + +/* + * Physical page 0 is special; it's not touched by Linux since BIOS + * 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. + */ +#if 0 + memset((void *) 0, 0, PAGE_SIZE); +#endif + start_mem = PAGE_ALIGN(start_mem); + address = 0; + pg_dir = swapper_pg_dir; + while (address < end_mem) { + /* map the memory at virtual addr 0xC0000000 */ + pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768])); + if (!pg_table) { + pg_table = (pte_t *) 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++; + 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); + address += PAGE_SIZE; + } + } + invalidate(); + return free_area_init(start_mem, end_mem); +} + +void mem_init(unsigned long start_mem, unsigned long end_mem) +{ + unsigned long start_low_mem = PAGE_SIZE; + int codepages = 0; + int reservedpages = 0; + int datapages = 0; + unsigned long tmp; + extern int etext; + + end_mem &= PAGE_MASK; + high_memory = 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_mem = PAGE_ALIGN(start_mem); + + /* + * IBM messed up *AGAIN* in their thinkpad: 0xA0000 -> 0x9F000. + * 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; + start_low_mem += PAGE_SIZE; + } + + while (start_mem < high_memory) { + mem_map[MAP_NR(start_mem)] = 0; + 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) + reservedpages++; + else if (tmp < (unsigned long) &etext) + codepages++; + else + datapages++; + continue; + } + mem_map[MAP_NR(tmp)] = 1; + 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, + 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 + return; +} + +void si_meminfo(struct sysinfo *val) +{ + int i; + + i = high_memory >> PAGE_SHIFT; + 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) + continue; + val->totalram++; + if (!mem_map[i]) + continue; + val->sharedram += mem_map[i]-1; + } + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + return; +} diff --git a/arch/i386/mm/kmalloc.c b/arch/i386/mm/kmalloc.c deleted file mode 100644 index 018f8db8f..000000000 --- a/arch/i386/mm/kmalloc.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * linux/mm/kmalloc.c - * - * Copyright (C) 1991, 1992 Linus Torvalds & Roger Wolff. - * - * Written by R.E. Wolff Sept/Oct '93. - * - */ - -/* - * Modified by Alex Bligh (alex@cconcepts.co.uk) 4 Apr 1994 to use multiple - * pages. So for 'page' throughout, read 'area'. - */ - -#include <linux/mm.h> -#include <asm/system.h> -#include <linux/delay.h> - -#define GFP_LEVEL_MASK 0xf - -/* I want this low enough for a while to catch errors. - I want this number to be increased in the near future: - loadable device drivers should use this function to get memory */ - -#define MAX_KMALLOC_K ((PAGE_SIZE<<(NUM_AREA_ORDERS-1))>>10) - - -/* This defines how many times we should try to allocate a free page before - giving up. Normally this shouldn't happen at all. */ -#define MAX_GET_FREE_PAGE_TRIES 4 - - -/* Private flags. */ - -#define MF_USED 0xffaa0055 -#define MF_FREE 0x0055ffaa - - -/* - * Much care has gone into making these routines in this file reentrant. - * - * The fancy bookkeeping of nbytesmalloced and the like are only used to - * report them to the user (oooohhhhh, aaaaahhhhh....) are not - * protected by cli(). (If that goes wrong. So what?) - * - * These routines restore the interrupt status to allow calling with ints - * off. - */ - -/* - * A block header. This is in front of every malloc-block, whether free or not. - */ -struct block_header { - unsigned long bh_flags; - union { - unsigned long ubh_length; - struct block_header *fbh_next; - } vp; -}; - - -#define bh_length vp.ubh_length -#define bh_next vp.fbh_next -#define BH(p) ((struct block_header *)(p)) - - -/* - * The page descriptor is at the front of every page that malloc has in use. - */ -struct page_descriptor { - struct page_descriptor *next; - struct block_header *firstfree; - int order; - int nfree; -}; - - -#define PAGE_DESC(p) ((struct page_descriptor *)(((unsigned long)(p)) & PAGE_MASK)) - - -/* - * A size descriptor describes a specific class of malloc sizes. - * Each class of sizes has its own freelist. - */ -struct size_descriptor { - struct page_descriptor *firstfree; - int size; - int nblocks; - - int nmallocs; - int nfrees; - int nbytesmalloced; - int npages; - unsigned long gfporder; /* number of pages in the area required */ -}; - -/* - * For now it is unsafe to allocate bucket sizes between n & n=16 where n is - * 4096 * any power of two - */ - -struct size_descriptor sizes[] = { - { NULL, 32,127, 0,0,0,0, 0}, - { NULL, 64, 63, 0,0,0,0, 0 }, - { NULL, 128, 31, 0,0,0,0, 0 }, - { NULL, 252, 16, 0,0,0,0, 0 }, - { NULL, 508, 8, 0,0,0,0, 0 }, - { NULL,1020, 4, 0,0,0,0, 0 }, - { NULL,2040, 2, 0,0,0,0, 0 }, - { NULL,4096-16, 1, 0,0,0,0, 0 }, - { NULL,8192-16, 1, 0,0,0,0, 1 }, - { NULL,16384-16, 1, 0,0,0,0, 2 }, - { NULL,32768-16, 1, 0,0,0,0, 3 }, - { NULL,65536-16, 1, 0,0,0,0, 4 }, - { NULL,131072-16, 1, 0,0,0,0, 5 }, - { NULL, 0, 0, 0,0,0,0, 0 } -}; - - -#define NBLOCKS(order) (sizes[order].nblocks) -#define BLOCKSIZE(order) (sizes[order].size) -#define AREASIZE(order) (PAGE_SIZE<<(sizes[order].gfporder)) - - -long kmalloc_init (long start_mem,long end_mem) -{ - int order; - -/* - * Check the static info array. Things will blow up terribly if it's - * incorrect. This is a late "compile time" check..... - */ -for (order = 0;BLOCKSIZE(order);order++) - { - if ((NBLOCKS (order)*BLOCKSIZE(order) + sizeof (struct page_descriptor)) > - AREASIZE(order)) - { - printk ("Cannot use %d bytes out of %d in order = %d block mallocs\n", - NBLOCKS (order) * BLOCKSIZE(order) + - sizeof (struct page_descriptor), - (int) AREASIZE(order), - BLOCKSIZE (order)); - panic ("This only happens if someone messes with kmalloc"); - } - } -return start_mem; -} - - - -int get_order (int size) -{ - int order; - - /* Add the size of the header */ - size += sizeof (struct block_header); - for (order = 0;BLOCKSIZE(order);order++) - if (size <= BLOCKSIZE (order)) - return order; - return -1; -} - -void * kmalloc (size_t size, int priority) -{ - unsigned long flags; - int order,tries,i,sz; - struct block_header *p; - struct page_descriptor *page; - -/* Sanity check... */ - if (intr_count && priority != GFP_ATOMIC) { - static int count = 0; - if (++count < 5) { - printk("kmalloc called nonatomically from interrupt %p\n", - __builtin_return_address(0)); - priority = GFP_ATOMIC; - } - } - -order = get_order (size); -if (order < 0) - { - printk ("kmalloc of too large a block (%d bytes).\n",size); - return (NULL); - } - -save_flags(flags); - -/* It seems VERY unlikely to me that it would be possible that this - loop will get executed more than once. */ -tries = MAX_GET_FREE_PAGE_TRIES; -while (tries --) - { - /* Try to allocate a "recently" freed memory block */ - cli (); - if ((page = sizes[order].firstfree) && - (p = page->firstfree)) - { - if (p->bh_flags == MF_FREE) - { - page->firstfree = p->bh_next; - page->nfree--; - if (!page->nfree) - { - sizes[order].firstfree = page->next; - page->next = NULL; - } - restore_flags(flags); - - sizes [order].nmallocs++; - sizes [order].nbytesmalloced += size; - p->bh_flags = MF_USED; /* As of now this block is officially in use */ - p->bh_length = size; - return p+1; /* Pointer arithmetic: increments past header */ - } - printk ("Problem: block on freelist at %08lx isn't free.\n",(long)p); - return (NULL); - } - restore_flags(flags); - - - /* Now we're in trouble: We need to get a new free page..... */ - - sz = BLOCKSIZE(order); /* sz is the size of the blocks we're dealing with */ - - /* This can be done with ints on: This is private to this invocation */ - page = (struct page_descriptor *) __get_free_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder); - if (!page) { - static unsigned long last = 0; - if (last + 10*HZ < jiffies) { - last = jiffies; - printk ("Couldn't get a free page.....\n"); - } - return NULL; - } -#if 0 - printk ("Got page %08x to use for %d byte mallocs....",(long)page,sz); -#endif - sizes[order].npages++; - - /* Loop for all but last block: */ - for (i=NBLOCKS(order),p=BH (page+1);i > 1;i--,p=p->bh_next) - { - p->bh_flags = MF_FREE; - p->bh_next = BH ( ((long)p)+sz); - } - /* Last block: */ - p->bh_flags = MF_FREE; - p->bh_next = NULL; - - page->order = order; - page->nfree = NBLOCKS(order); - page->firstfree = BH(page+1); -#if 0 - printk ("%d blocks per page\n",page->nfree); -#endif - /* Now we're going to muck with the "global" freelist for this size: - this should be uninterruptible */ - cli (); - /* - * sizes[order].firstfree used to be NULL, otherwise we wouldn't be - * here, but you never know.... - */ - page->next = sizes[order].firstfree; - sizes[order].firstfree = page; - restore_flags(flags); - } - -/* Pray that printk won't cause this to happen again :-) */ - -printk ("Hey. This is very funny. I tried %d times to allocate a whole\n" - "new page for an object only %d bytes long, but some other process\n" - "beat me to actually allocating it. Also note that this 'error'\n" - "message is soooo very long to catch your attention. I'd appreciate\n" - "it if you'd be so kind as to report what conditions caused this to\n" - "the author of this kmalloc: wolff@dutecai.et.tudelft.nl.\n" - "(Executive summary: This can't happen)\n", - MAX_GET_FREE_PAGE_TRIES, - size); -return NULL; -} - - -void kfree_s (void *ptr,int size) -{ -unsigned long flags; -int order; -register struct block_header *p=((struct block_header *)ptr) -1; -struct page_descriptor *page,*pg2; - -page = PAGE_DESC (p); -order = page->order; -if ((order < 0) || - (order > sizeof (sizes)/sizeof (sizes[0])) || - (((long)(page->next)) & ~PAGE_MASK) || - (p->bh_flags != MF_USED)) - { - printk ("kfree of non-kmalloced memory: %p, next= %p, order=%d\n", - p, page->next, page->order); - return; - } -if (size && - size != p->bh_length) - { - printk ("Trying to free pointer at %p with wrong size: %d instead of %lu.\n", - p,size,p->bh_length); - return; - } -size = p->bh_length; -p->bh_flags = MF_FREE; /* As of now this block is officially free */ -save_flags(flags); -cli (); -p->bh_next = page->firstfree; -page->firstfree = p; -page->nfree ++; - -if (page->nfree == 1) - { /* Page went from full to one free block: put it on the freelist */ - if (page->next) - { - printk ("Page %p already on freelist dazed and confused....\n", page); - } - else - { - page->next = sizes[order].firstfree; - sizes[order].firstfree = page; - } - } - -/* If page is completely free, free it */ -if (page->nfree == NBLOCKS (page->order)) - { -#if 0 - printk ("Freeing page %08x.\n", (long)page); -#endif - if (sizes[order].firstfree == page) - { - sizes[order].firstfree = page->next; - } - else - { - for (pg2=sizes[order].firstfree; - (pg2 != NULL) && (pg2->next != page); - pg2=pg2->next) - /* Nothing */; - if (pg2 != NULL) - pg2->next = page->next; - else - printk ("Ooops. page %p doesn't show on freelist.\n", page); - } -/* FIXME: I'm sure we should do something with npages here (like npages--) */ - free_pages ((long)page, sizes[order].gfporder); - } -restore_flags(flags); - -/* FIXME: ?? Are these increment & decrement operations guaranteed to be - * atomic? Could an IRQ not occur between the read & the write? - * Maybe yes on a x86 with GCC...?? - */ -sizes[order].nfrees++; /* Noncritical (monitoring) admin stuff */ -sizes[order].nbytesmalloced -= size; -} diff --git a/arch/i386/mm/memory.c b/arch/i386/mm/memory.c deleted file mode 100644 index 3e5a67041..000000000 --- a/arch/i386/mm/memory.c +++ /dev/null @@ -1,1320 +0,0 @@ -/* - * linux/mm/memory.c - * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - */ - -/* - * demand-loading started 01.12.91 - seems it is high on the list of - * things wanted, and it should be easy to implement. - Linus - */ - -/* - * Ok, demand-loading was easy, shared pages a little bit tricker. Shared - * pages started 02.12.91, seems to work. - Linus. - * - * Tested sharing by executing about 30 /bin/sh: under the old kernel it - * would have taken more than the 6M I have free, but it worked well as - * far as I could see. - * - * Also corrected some "invalidate()"s - I wasn't doing enough of them. - */ - -/* - * Real VM (paging to/from disk) started 18.12.91. Much more work and - * thought has to go into this. Oh, well.. - * 19.12.91 - works, somewhat. Sometimes I get faults, don't know why. - * Found it. Everything seems to work now. - * 20.12.91 - Ok, making the swap-device changeable like the root. - */ - -/* - * 05.04.94 - Multi-page memory management added for v1.1. - * Idea by Alex Bligh (alex@cconcepts.co.uk) - */ - -#include <linux/config.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/head.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/types.h> -#include <linux/ptrace.h> -#include <linux/mman.h> - -#include <asm/system.h> -#include <asm/segment.h> - -/* - * Define this if things work differently on a i386 and a i486: - * it will (on a i486) warn about kernel memory accesses that are - * done without a 'verify_area(VERIFY_WRITE,..)' - */ -#undef CONFIG_TEST_VERIFY_AREA - -unsigned long high_memory = 0; - -extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */ - -extern void sound_mem_init(void); -extern void die_if_kernel(char *,struct pt_regs *,long); -extern void show_net_buffers(void); - -/* - * The free_area_list arrays point to the queue heads of the free areas - * of different sizes - */ -int nr_swap_pages = 0; -int nr_free_pages = 0; -struct mem_list free_area_list[NR_MEM_LISTS]; -unsigned char * free_area_map[NR_MEM_LISTS]; - -#define copy_page(from,to) \ -__asm__("cld ; rep ; movsl": :"S" (from),"D" (to),"c" (1024):"cx","di","si") - -unsigned short * mem_map = NULL; - -#define CODE_SPACE(addr,p) ((addr) < (p)->end_code) - -/* - * oom() prints a message (so that the user knows why the process died), - * and gives the process an untrappable SIGKILL. - */ -void oom(struct task_struct * task) -{ - printk("\nOut of memory.\n"); - task->sigaction[SIGKILL-1].sa_handler = NULL; - task->blocked &= ~(1<<(SIGKILL-1)); - send_sig(SIGKILL,task,1); -} - -static void free_one_table(unsigned long * page_dir) -{ - int j; - unsigned long pg_table = *page_dir; - unsigned long * page_table; - - if (!pg_table) - return; - *page_dir = 0; - if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) { - printk("Bad page table: [%p]=%08lx\n",page_dir,pg_table); - return; - } - if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED) - return; - page_table = (unsigned long *) (pg_table & PAGE_MASK); - for (j = 0 ; j < PTRS_PER_PAGE ; j++,page_table++) { - unsigned long pg = *page_table; - - if (!pg) - continue; - *page_table = 0; - if (pg & PAGE_PRESENT) - free_page(PAGE_MASK & pg); - else - swap_free(pg); - } - free_page(PAGE_MASK & pg_table); -} - -/* - * This function clears all user-level page tables of a process - this - * is needed by execve(), so that old pages aren't in the way. Note that - * unlike 'free_page_tables()', this function still leaves a valid - * page-table-tree in memory: it just removes the user pages. The two - * functions are similar, but there is a fundamental difference. - */ -void clear_page_tables(struct task_struct * tsk) -{ - int i; - unsigned long pg_dir; - unsigned long * page_dir; - - if (!tsk) - return; - if (tsk == task[0]) - panic("task[0] (swapper) doesn't support exec()\n"); - pg_dir = tsk->tss.cr3; - page_dir = (unsigned long *) pg_dir; - if (!page_dir || page_dir == swapper_pg_dir) { - printk("Trying to clear kernel page-directory: not good\n"); - return; - } - if (mem_map[MAP_NR(pg_dir)] > 1) { - unsigned long * new_pg; - - if (!(new_pg = (unsigned long*) get_free_page(GFP_KERNEL))) { - oom(tsk); - return; - } - for (i = 768 ; i < 1024 ; i++) - new_pg[i] = page_dir[i]; - free_page(pg_dir); - tsk->tss.cr3 = (unsigned long) new_pg; - return; - } - for (i = 0 ; i < 768 ; i++,page_dir++) - free_one_table(page_dir); - invalidate(); - return; -} - -/* - * This function frees up all page tables of a process when it exits. - */ -void free_page_tables(struct task_struct * tsk) -{ - int i; - unsigned long pg_dir; - unsigned long * page_dir; - - if (!tsk) - return; - if (tsk == task[0]) { - printk("task[0] (swapper) killed: unable to recover\n"); - panic("Trying to free up swapper memory space"); - } - pg_dir = tsk->tss.cr3; - if (!pg_dir || pg_dir == (unsigned long) swapper_pg_dir) { - printk("Trying to free kernel page-directory: not good\n"); - return; - } - tsk->tss.cr3 = (unsigned long) swapper_pg_dir; - if (tsk == current) - __asm__ __volatile__("movl %0,%%cr3": :"a" (tsk->tss.cr3)); - if (mem_map[MAP_NR(pg_dir)] > 1) { - free_page(pg_dir); - return; - } - page_dir = (unsigned long *) pg_dir; - for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++) - free_one_table(page_dir); - free_page(pg_dir); - invalidate(); -} - -/* - * clone_page_tables() clones the page table for a process - both - * processes will have the exact same pages in memory. There are - * probably races in the memory management with cloning, but we'll - * see.. - */ -int clone_page_tables(struct task_struct * tsk) -{ - unsigned long pg_dir; - - pg_dir = current->tss.cr3; - mem_map[MAP_NR(pg_dir)]++; - tsk->tss.cr3 = pg_dir; - return 0; -} - -/* - * copy_page_tables() just copies the whole process memory range: - * note the special handling of RESERVED (ie kernel) pages, which - * means that they are always shared by all processes. - */ -int copy_page_tables(struct task_struct * tsk) -{ - int i; - unsigned long old_pg_dir, *old_page_dir; - unsigned long new_pg_dir, *new_page_dir; - - if (!(new_pg_dir = get_free_page(GFP_KERNEL))) - return -ENOMEM; - old_pg_dir = current->tss.cr3; - tsk->tss.cr3 = new_pg_dir; - old_page_dir = (unsigned long *) old_pg_dir; - new_page_dir = (unsigned long *) new_pg_dir; - for (i = 0 ; i < PTRS_PER_PAGE ; i++,old_page_dir++,new_page_dir++) { - int j; - unsigned long old_pg_table, *old_page_table; - unsigned long new_pg_table, *new_page_table; - - old_pg_table = *old_page_dir; - if (!old_pg_table) - continue; - if (old_pg_table >= high_memory || !(old_pg_table & PAGE_PRESENT)) { - printk("copy_page_tables: bad page table: " - "probable memory corruption\n"); - *old_page_dir = 0; - continue; - } - if (mem_map[MAP_NR(old_pg_table)] & MAP_PAGE_RESERVED) { - *new_page_dir = old_pg_table; - continue; - } - if (!(new_pg_table = get_free_page(GFP_KERNEL))) { - free_page_tables(tsk); - return -ENOMEM; - } - old_page_table = (unsigned long *) (PAGE_MASK & old_pg_table); - new_page_table = (unsigned long *) (PAGE_MASK & new_pg_table); - for (j = 0 ; j < PTRS_PER_PAGE ; j++,old_page_table++,new_page_table++) { - unsigned long pg; - pg = *old_page_table; - if (!pg) - continue; - if (!(pg & PAGE_PRESENT)) { - *new_page_table = swap_duplicate(pg); - continue; - } - if (pg > high_memory || (mem_map[MAP_NR(pg)] & MAP_PAGE_RESERVED)) { - *new_page_table = pg; - continue; - } - if (pg & PAGE_COW) - pg &= ~PAGE_RW; - if (delete_from_swap_cache(pg)) - pg |= PAGE_DIRTY; - *new_page_table = pg; - *old_page_table = pg; - mem_map[MAP_NR(pg)]++; - } - *new_page_dir = new_pg_table | PAGE_TABLE; - } - invalidate(); - return 0; -} - -/* - * a more complete version of free_page_tables which performs with page - * granularity. - */ -int unmap_page_range(unsigned long from, unsigned long size) -{ - unsigned long page, page_dir; - unsigned long *page_table, *dir; - unsigned long poff, pcnt, pc; - - if (from & ~PAGE_MASK) { - printk("unmap_page_range called with wrong alignment\n"); - return -EINVAL; - } - size = (size + ~PAGE_MASK) >> PAGE_SHIFT; - dir = PAGE_DIR_OFFSET(current->tss.cr3,from); - poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if ((pcnt = PTRS_PER_PAGE - poff) > size) - pcnt = size; - - for ( ; size > 0; ++dir, size -= pcnt, - pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size)) { - if (!(page_dir = *dir)) { - poff = 0; - continue; - } - if (!(page_dir & PAGE_PRESENT)) { - printk("unmap_page_range: bad page directory."); - continue; - } - page_table = (unsigned long *)(PAGE_MASK & page_dir); - if (poff) { - page_table += poff; - poff = 0; - } - for (pc = pcnt; pc--; page_table++) { - if ((page = *page_table) != 0) { - *page_table = 0; - if (PAGE_PRESENT & page) { - if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) - if (current->mm->rss > 0) - --current->mm->rss; - free_page(PAGE_MASK & page); - } else - swap_free(page); - } - } - if (pcnt == PTRS_PER_PAGE) { - *dir = 0; - free_page(PAGE_MASK & page_dir); - } - } - invalidate(); - return 0; -} - -int zeromap_page_range(unsigned long from, unsigned long size, int mask) -{ - unsigned long *page_table, *dir; - unsigned long poff, pcnt; - unsigned long page; - - if (mask) { - if ((mask & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) { - printk("zeromap_page_range: mask = %08x\n",mask); - return -EINVAL; - } - mask |= ZERO_PAGE; - } - if (from & ~PAGE_MASK) { - printk("zeromap_page_range: from = %08lx\n",from); - return -EINVAL; - } - dir = PAGE_DIR_OFFSET(current->tss.cr3,from); - size = (size + ~PAGE_MASK) >> PAGE_SHIFT; - poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if ((pcnt = PTRS_PER_PAGE - poff) > size) - pcnt = size; - - while (size > 0) { - if (!(PAGE_PRESENT & *dir)) { - /* clear page needed here? SRB. */ - if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) { - invalidate(); - return -ENOMEM; - } - if (PAGE_PRESENT & *dir) { - free_page((unsigned long) page_table); - page_table = (unsigned long *)(PAGE_MASK & *dir++); - } else - *dir++ = ((unsigned long) page_table) | PAGE_TABLE; - } else - page_table = (unsigned long *)(PAGE_MASK & *dir++); - page_table += poff; - poff = 0; - for (size -= pcnt; pcnt-- ;) { - if ((page = *page_table) != 0) { - *page_table = 0; - if (page & PAGE_PRESENT) { - if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) - if (current->mm->rss > 0) - --current->mm->rss; - free_page(PAGE_MASK & page); - } else - swap_free(page); - } - *page_table++ = mask; - } - pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size); - } - invalidate(); - return 0; -} - -/* - * maps a range of physical memory into the requested pages. the old - * mappings are removed. any references to nonexistent pages results - * in null mappings (currently treated as "copy-on-access") - */ -int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask) -{ - unsigned long *page_table, *dir; - unsigned long poff, pcnt; - unsigned long page; - - if (mask) { - if ((mask & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) { - printk("remap_page_range: mask = %08x\n",mask); - return -EINVAL; - } - } - if ((from & ~PAGE_MASK) || (to & ~PAGE_MASK)) { - printk("remap_page_range: from = %08lx, to=%08lx\n",from,to); - return -EINVAL; - } - dir = PAGE_DIR_OFFSET(current->tss.cr3,from); - size = (size + ~PAGE_MASK) >> PAGE_SHIFT; - poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if ((pcnt = PTRS_PER_PAGE - poff) > size) - pcnt = size; - - while (size > 0) { - if (!(PAGE_PRESENT & *dir)) { - /* clearing page here, needed? SRB. */ - if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) { - invalidate(); - return -1; - } - *dir++ = ((unsigned long) page_table) | PAGE_TABLE; - } - else - page_table = (unsigned long *)(PAGE_MASK & *dir++); - if (poff) { - page_table += poff; - poff = 0; - } - - for (size -= pcnt; pcnt-- ;) { - if ((page = *page_table) != 0) { - *page_table = 0; - if (PAGE_PRESENT & page) { - if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) - if (current->mm->rss > 0) - --current->mm->rss; - free_page(PAGE_MASK & page); - } else - swap_free(page); - } - - /* - * the first condition should return an invalid access - * when the page is referenced. current assumptions - * cause it to be treated as demand allocation in some - * cases. - */ - if (!mask) - *page_table++ = 0; /* not present */ - else if (to >= high_memory) - *page_table++ = (to | mask); - else if (!mem_map[MAP_NR(to)]) - *page_table++ = 0; /* not present */ - else { - *page_table++ = (to | mask); - if (!(mem_map[MAP_NR(to)] & MAP_PAGE_RESERVED)) { - ++current->mm->rss; - mem_map[MAP_NR(to)]++; - } - } - to += PAGE_SIZE; - } - pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size); - } - invalidate(); - return 0; -} - -/* - * This function puts a page in memory at the wanted address. - * It returns the physical address of the page gotten, 0 if - * out of memory (either when trying to access page-table or - * page.) - */ -unsigned long put_page(struct task_struct * tsk,unsigned long page, - unsigned long address,int prot) -{ - unsigned long *page_table; - - if ((prot & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) - printk("put_page: prot = %08x\n",prot); - if (page >= high_memory) { - printk("put_page: trying to put page %08lx at %08lx\n",page,address); - return 0; - } - page_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address); - if ((*page_table) & PAGE_PRESENT) - page_table = (unsigned long *) (PAGE_MASK & *page_table); - else { - printk("put_page: bad page directory entry\n"); - oom(tsk); - *page_table = BAD_PAGETABLE | PAGE_TABLE; - return 0; - } - page_table += (address >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if (*page_table) { - printk("put_page: page already exists\n"); - *page_table = 0; - invalidate(); - } - *page_table = page | prot; -/* no need for invalidate */ - return page; -} - -/* - * The previous function doesn't work very well if you also want to mark - * the page dirty: exec.c wants this, as it has earlier changed the page, - * and we want the dirty-status to be correct (for VM). Thus the same - * routine, but this time we mark it dirty too. - */ -unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsigned long address) -{ - unsigned long tmp, *page_table; - - if (page >= high_memory) - printk("put_dirty_page: trying to put page %08lx at %08lx\n",page,address); - if (mem_map[MAP_NR(page)] != 1) - printk("mem_map disagrees with %08lx at %08lx\n",page,address); - page_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address); - if (PAGE_PRESENT & *page_table) - page_table = (unsigned long *) (PAGE_MASK & *page_table); - else { - if (!(tmp = get_free_page(GFP_KERNEL))) - return 0; - if (PAGE_PRESENT & *page_table) { - free_page(tmp); - page_table = (unsigned long *) (PAGE_MASK & *page_table); - } else { - *page_table = tmp | PAGE_TABLE; - page_table = (unsigned long *) tmp; - } - } - page_table += (address >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if (*page_table) { - printk("put_dirty_page: page already exists\n"); - *page_table = 0; - invalidate(); - } - *page_table = page | (PAGE_DIRTY | PAGE_PRIVATE); -/* no need for invalidate */ - return page; -} - -/* - * This routine handles present pages, when users try to write - * to a shared page. It is done by copying the page to a new address - * and decrementing the shared-page counter for the old page. - * - * Goto-purists beware: the only reason for goto's here is that it results - * in better assembly code.. The "default" path will see no jumps at all. - */ -void do_wp_page(struct vm_area_struct * vma, unsigned long address, - unsigned long error_code) -{ - unsigned long *pde, pte, old_page, prot; - unsigned long new_page; - - new_page = __get_free_page(GFP_KERNEL); - pde = PAGE_DIR_OFFSET(vma->vm_task->tss.cr3,address); - pte = *pde; - if (!(pte & PAGE_PRESENT)) - goto end_wp_page; - if ((pte & PAGE_TABLE) != PAGE_TABLE || pte >= high_memory) - goto bad_wp_pagetable; - pte &= PAGE_MASK; - pte += PAGE_PTR(address); - old_page = *(unsigned long *) pte; - if (!(old_page & PAGE_PRESENT)) - goto end_wp_page; - if (old_page >= high_memory) - goto bad_wp_page; - if (old_page & PAGE_RW) - goto end_wp_page; - vma->vm_task->mm->min_flt++; - prot = (old_page & ~PAGE_MASK) | PAGE_RW | PAGE_DIRTY; - old_page &= PAGE_MASK; - if (mem_map[MAP_NR(old_page)] != 1) { - if (new_page) { - if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED) - ++vma->vm_task->mm->rss; - copy_page(old_page,new_page); - *(unsigned long *) pte = new_page | prot; - free_page(old_page); - invalidate(); - return; - } - free_page(old_page); - oom(vma->vm_task); - *(unsigned long *) pte = BAD_PAGE | prot; - invalidate(); - return; - } - *(unsigned long *) pte |= PAGE_RW | PAGE_DIRTY; - invalidate(); - if (new_page) - free_page(new_page); - return; -bad_wp_page: - printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page); - *(unsigned long *) pte = BAD_PAGE | PAGE_SHARED; - send_sig(SIGKILL, vma->vm_task, 1); - goto end_wp_page; -bad_wp_pagetable: - printk("do_wp_page: bogus page-table at address %08lx (%08lx)\n",address,pte); - *pde = BAD_PAGETABLE | PAGE_TABLE; - send_sig(SIGKILL, vma->vm_task, 1); -end_wp_page: - if (new_page) - free_page(new_page); - return; -} - -/* - * Ugly, ugly, but the goto's result in better assembly.. - */ -int verify_area(int type, const void * addr, unsigned long size) -{ - struct vm_area_struct * vma; - unsigned long start = (unsigned long) addr; - - /* If the current user space is mapped to kernel space (for the - * case where we use a fake user buffer with get_fs/set_fs()) we - * don't expect to find the address in the user vm map. - */ - if (get_fs() == get_ds()) - return 0; - - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > start) - break; - } - if (vma->vm_start <= start) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (vma->vm_end - start > current->rlim[RLIMIT_STACK].rlim_cur) - goto bad_area; - -good_area: - if (!wp_works_ok && type == VERIFY_WRITE) - goto check_wp_fault_by_hand; - for (;;) { - struct vm_area_struct * next; - if (!(vma->vm_page_prot & PAGE_USER)) - goto bad_area; - if (type != VERIFY_READ && !(vma->vm_page_prot & (PAGE_COW | PAGE_RW))) - goto bad_area; - if (vma->vm_end - start >= size) - return 0; - next = vma->vm_next; - if (!next || vma->vm_end != next->vm_start) - goto bad_area; - vma = next; - } - -check_wp_fault_by_hand: - size--; - size += start & ~PAGE_MASK; - size >>= PAGE_SHIFT; - start &= PAGE_MASK; - - for (;;) { - if (!(vma->vm_page_prot & (PAGE_COW | PAGE_RW))) - goto bad_area; - do_wp_page(vma, start, PAGE_PRESENT); - if (!size) - return 0; - size--; - start += PAGE_SIZE; - if (start < vma->vm_end) - continue; - vma = vma->vm_next; - if (!vma || vma->vm_start != start) - break; - } - -bad_area: - return -EFAULT; -} - -static inline void get_empty_page(struct task_struct * tsk, unsigned long address) -{ - unsigned long tmp; - - if (!(tmp = get_free_page(GFP_KERNEL))) { - oom(tsk); - tmp = BAD_PAGE; - } - if (!put_page(tsk,tmp,address,PAGE_PRIVATE)) - free_page(tmp); -} - -/* - * try_to_share() checks the page at address "address" in the task "p", - * to see if it exists, and if it is clean. If so, share it with the current - * task. - * - * NOTE! This assumes we have checked that p != current, and that they - * share the same inode and can generally otherwise be shared. - */ -static int try_to_share(unsigned long to_address, struct vm_area_struct * to_area, - unsigned long from_address, struct vm_area_struct * from_area, - unsigned long newpage) -{ - unsigned long from; - unsigned long to; - unsigned long from_page; - unsigned long to_page; - - from_page = (unsigned long)PAGE_DIR_OFFSET(from_area->vm_task->tss.cr3,from_address); - to_page = (unsigned long)PAGE_DIR_OFFSET(to_area->vm_task->tss.cr3,to_address); -/* is there a page-directory at from? */ - from = *(unsigned long *) from_page; - if (!(from & PAGE_PRESENT)) - return 0; - from &= PAGE_MASK; - from_page = from + PAGE_PTR(from_address); - from = *(unsigned long *) from_page; -/* is the page present? */ - if (!(from & PAGE_PRESENT)) - return 0; -/* if it is private, it must be clean to be shared */ - if (from & PAGE_DIRTY) { - if (from_area->vm_page_prot & PAGE_COW) - return 0; - if (!(from_area->vm_page_prot & PAGE_RW)) - return 0; - } -/* is the page reasonable at all? */ - if (from >= high_memory) - return 0; - if (mem_map[MAP_NR(from)] & MAP_PAGE_RESERVED) - return 0; -/* is the destination ok? */ - to = *(unsigned long *) to_page; - if (!(to & PAGE_PRESENT)) - return 0; - to &= PAGE_MASK; - to_page = to + PAGE_PTR(to_address); - if (*(unsigned long *) to_page) - return 0; -/* do we copy? */ - if (newpage) { - if (in_swap_cache(from)) { /* implies PAGE_DIRTY */ - if (from_area->vm_page_prot & PAGE_COW) - return 0; - if (!(from_area->vm_page_prot & PAGE_RW)) - return 0; - } - copy_page((from & PAGE_MASK), newpage); - *(unsigned long *) to_page = newpage | to_area->vm_page_prot; - return 1; - } -/* do a final swap-cache test before sharing them.. */ - if (in_swap_cache(from)) { - if (from_area->vm_page_prot & PAGE_COW) - return 0; - if (!(from_area->vm_page_prot & PAGE_RW)) - return 0; - from |= PAGE_DIRTY; - *(unsigned long *) from_page = from; - delete_from_swap_cache(from); - invalidate(); - } - mem_map[MAP_NR(from)]++; -/* fill in the 'to' field, checking for COW-stuff */ - to = (from & (PAGE_MASK | PAGE_DIRTY)) | to_area->vm_page_prot; - if (to & PAGE_COW) - to &= ~PAGE_RW; - *(unsigned long *) to_page = to; -/* Check if we need to do anything at all to the 'from' field */ - if (!(from & PAGE_RW)) - return 1; - if (!(from_area->vm_page_prot & PAGE_COW)) - return 1; -/* ok, need to mark it read-only, so invalidate any possible old TB entry */ - from &= ~PAGE_RW; - *(unsigned long *) from_page = from; - invalidate(); - return 1; -} - -/* - * share_page() tries to find a process that could share a page with - * the current one. - * - * We first check if it is at all feasible by checking inode->i_count. - * It should be >1 if there are other tasks sharing this inode. - */ -static int share_page(struct vm_area_struct * area, unsigned long address, - unsigned long error_code, unsigned long newpage) -{ - struct inode * inode; - struct task_struct ** p; - unsigned long offset; - unsigned long from_address; - unsigned long give_page; - - if (!area || !(inode = area->vm_inode) || inode->i_count < 2) - return 0; - /* do we need to copy or can we just share? */ - give_page = 0; - if ((area->vm_page_prot & PAGE_COW) && (error_code & PAGE_RW)) { - if (!newpage) - return 0; - give_page = newpage; - } - offset = address - area->vm_start + area->vm_offset; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - struct vm_area_struct * mpnt; - if (!*p) - continue; - if (area->vm_task == *p) - continue; - /* Now see if there is something in the VMM that - we can share pages with */ - for (mpnt = (*p)->mm->mmap; mpnt; mpnt = mpnt->vm_next) { - /* must be same inode */ - if (mpnt->vm_inode != inode) - continue; - /* offsets must be mutually page-aligned */ - if ((mpnt->vm_offset ^ area->vm_offset) & ~PAGE_MASK) - continue; - /* the other area must actually cover the wanted page.. */ - from_address = offset + mpnt->vm_start - mpnt->vm_offset; - if (from_address < mpnt->vm_start || from_address >= mpnt->vm_end) - continue; - /* .. NOW we can actually try to use the same physical page */ - if (!try_to_share(address, area, from_address, mpnt, give_page)) - continue; - /* free newpage if we never used it.. */ - if (give_page || !newpage) - return 1; - free_page(newpage); - return 1; - } - } - return 0; -} - -/* - * fill in an empty page-table if none exists. - */ -static inline unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned long address) -{ - unsigned long page; - unsigned long *p; - - p = PAGE_DIR_OFFSET(tsk->tss.cr3,address); - if (PAGE_PRESENT & *p) - return *p; - if (*p) { - printk("get_empty_pgtable: bad page-directory entry \n"); - *p = 0; - } - page = get_free_page(GFP_KERNEL); - p = PAGE_DIR_OFFSET(tsk->tss.cr3,address); - if (PAGE_PRESENT & *p) { - free_page(page); - return *p; - } - if (*p) { - printk("get_empty_pgtable: bad page-directory entry \n"); - *p = 0; - } - if (page) { - *p = page | PAGE_TABLE; - return *p; - } - oom(current); - *p = BAD_PAGETABLE | PAGE_TABLE; - return 0; -} - -static inline void do_swap_page(struct vm_area_struct * vma, - unsigned long address, unsigned long * pge, unsigned long entry) -{ - unsigned long page; - - if (vma->vm_ops && vma->vm_ops->swapin) - page = vma->vm_ops->swapin(vma, entry); - else - page = swap_in(entry); - if (*pge != entry) { - free_page(page); - return; - } - page = page | vma->vm_page_prot; - if (mem_map[MAP_NR(page)] > 1 && (page & PAGE_COW)) - page &= ~PAGE_RW; - ++vma->vm_task->mm->rss; - ++vma->vm_task->mm->maj_flt; - *pge = page; - return; -} - -void do_no_page(struct vm_area_struct * vma, unsigned long address, - unsigned long error_code) -{ - unsigned long page, entry, prot; - - page = get_empty_pgtable(vma->vm_task,address); - if (!page) - return; - page &= PAGE_MASK; - page += PAGE_PTR(address); - entry = *(unsigned long *) page; - if (entry & PAGE_PRESENT) - return; - if (entry) { - do_swap_page(vma, address, (unsigned long *) page, entry); - return; - } - address &= PAGE_MASK; - - if (!vma->vm_ops || !vma->vm_ops->nopage) { - ++vma->vm_task->mm->rss; - ++vma->vm_task->mm->min_flt; - get_empty_page(vma->vm_task,address); - return; - } - page = get_free_page(GFP_KERNEL); - if (share_page(vma, address, error_code, page)) { - ++vma->vm_task->mm->min_flt; - ++vma->vm_task->mm->rss; - return; - } - if (!page) { - oom(current); - put_page(vma->vm_task, BAD_PAGE, address, PAGE_PRIVATE); - return; - } - ++vma->vm_task->mm->maj_flt; - ++vma->vm_task->mm->rss; - prot = vma->vm_page_prot; - /* - * The fourth argument is "no_share", which tells the low-level code - * to copy, not share the page even if sharing is possible. It's - * essentially an early COW detection ("moo at 5 AM"). - */ - page = vma->vm_ops->nopage(vma, address, page, (error_code & PAGE_RW) && (prot & PAGE_COW)); - if (share_page(vma, address, error_code, 0)) { - free_page(page); - return; - } - /* - * This silly early PAGE_DIRTY setting removes a race - * due to the bad i386 page protection. - */ - if (error_code & PAGE_RW) { - prot |= PAGE_DIRTY; /* can't be COW-shared: see "no_share" above */ - } else if ((prot & PAGE_COW) && mem_map[MAP_NR(page)] > 1) - prot &= ~PAGE_RW; - if (put_page(vma->vm_task, page, address, prot)) - return; - free_page(page); - oom(current); -} - -/* - * This routine handles page faults. It determines the address, - * and the problem, and then passes it off to one of the appropriate - * routines. - */ -asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) -{ - struct vm_area_struct * vma; - unsigned long address; - unsigned long page; - - /* get the address */ - __asm__("movl %%cr2,%0":"=r" (address)); - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > address) - break; - } - 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) - 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: - if (regs->eflags & VM_MASK) { - unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT; - if (bit < 32) - current->screen_bitmap |= 1 << bit; - } - if (!(vma->vm_page_prot & PAGE_USER)) - goto bad_area; - if (error_code & PAGE_PRESENT) { - if (!(vma->vm_page_prot & (PAGE_RW | PAGE_COW))) - goto bad_area; -#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); - return; - } - do_no_page(vma, address, error_code); - return; - -/* - * Something tried to access memory that isn't in our memory map.. - * Fix it, but check if it's kernel or user first.. - */ -bad_area: - if (error_code & PAGE_USER) { - current->tss.cr2 = address; - current->tss.error_code = error_code; - current->tss.trap_no = 14; - send_sig(SIGSEGV, current, 1); - return; - } -/* - * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice. - */ - if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & PAGE_PRESENT)) { - wp_works_ok = 1; - pg0[0] = PAGE_SHARED; - invalidate(); - printk("This processor honours the WP bit even when in supervisor mode. Good.\n"); - return; - } - if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) { - printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - pg0[0] = PAGE_SHARED; - } else - printk(KERN_ALERT "Unable to handle kernel paging request"); - 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]; - printk(KERN_ALERT "*pde = %08lx\n", page); - if (page & PAGE_PRESENT) { - page &= PAGE_MASK; - address &= 0x003ff000; - page = ((unsigned long *) page)[address >> PAGE_SHIFT]; - printk(KERN_ALERT "*pte = %08lx\n", page); - } - die_if_kernel("Oops", regs, error_code); - do_exit(SIGKILL); -} - -/* - * 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 - * unused etc.. - * - * BAD_PAGETABLE is the accompanying page-table: it is initialized - * to point to BAD_PAGE entries. - * - * ZERO_PAGE is a special page that is used for zero-initialized - * data and COW. - */ -unsigned long __bad_pagetable(void) -{ - extern char empty_bad_page_table[PAGE_SIZE]; - - __asm__ __volatile__("cld ; rep ; stosl": - :"a" (BAD_PAGE + PAGE_TABLE), - "D" ((long) empty_bad_page_table), - "c" (PTRS_PER_PAGE) - :"di","cx"); - return (unsigned long) empty_bad_page_table; -} - -unsigned long __bad_page(void) -{ - extern char empty_bad_page[PAGE_SIZE]; - - __asm__ __volatile__("cld ; rep ; stosl": - :"a" (0), - "D" ((long) empty_bad_page), - "c" (PTRS_PER_PAGE) - :"di","cx"); - return (unsigned long) empty_bad_page; -} - -unsigned long __zero_page(void) -{ - extern char empty_zero_page[PAGE_SIZE]; - - __asm__ __volatile__("cld ; rep ; stosl": - :"a" (0), - "D" ((long) empty_zero_page), - "c" (PTRS_PER_PAGE) - :"di","cx"); - return (unsigned long) empty_zero_page; -} - -void show_mem(void) -{ - int i,free = 0,total = 0,reserved = 0; - int shared = 0; - - printk("Mem-info:\n"); - show_free_areas(); - printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = high_memory >> PAGE_SHIFT; - while (i-- > 0) { - total++; - if (mem_map[i] & MAP_PAGE_RESERVED) - reserved++; - else if (!mem_map[i]) - free++; - else - shared += mem_map[i]-1; - } - printk("%d pages of RAM\n",total); - printk("%d free pages\n",free); - printk("%d reserved pages\n",reserved); - printk("%d pages shared\n",shared); - show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif -} - -extern unsigned long free_area_init(unsigned long, unsigned long); - -/* - * paging_init() sets up the page tables - note that the first 4MB are - * already mapped by head.S. - * - * This routines also unmaps the page at virtual kernel address 0, so - * that we can trap those pesky NULL-reference errors in the kernel. - */ -unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) -{ - unsigned long * pg_dir; - unsigned long * pg_table; - unsigned long tmp; - unsigned long address; - -/* - * Physical page 0 is special; it's not touched by Linux since BIOS - * 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. - */ -#if 0 - memset((void *) 0, 0, PAGE_SIZE); -#endif - start_mem = PAGE_ALIGN(start_mem); - address = 0; - pg_dir = swapper_pg_dir; - while (address < end_mem) { - tmp = *(pg_dir + 768); /* at virtual addr 0xC0000000 */ - if (!tmp) { - tmp = start_mem | PAGE_TABLE; - *(pg_dir + 768) = tmp; - start_mem += PAGE_SIZE; - } - *pg_dir = tmp; /* also map it in at 0x0000000 for init */ - pg_dir++; - pg_table = (unsigned long *) (tmp & PAGE_MASK); - for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) { - if (address < end_mem) - *pg_table = address | PAGE_SHARED; - else - *pg_table = 0; - address += PAGE_SIZE; - } - } - invalidate(); - return free_area_init(start_mem, end_mem); -} - -void mem_init(unsigned long start_low_mem, - unsigned long start_mem, unsigned long end_mem) -{ - int codepages = 0; - int reservedpages = 0; - int datapages = 0; - unsigned long tmp; - extern int etext; - - cli(); - end_mem &= PAGE_MASK; - high_memory = end_mem; - - /* mark usable pages in the mem_map[] */ - start_low_mem = PAGE_ALIGN(start_low_mem); - start_mem = PAGE_ALIGN(start_mem); - - /* - * IBM messed up *AGAIN* in their thinkpad: 0xA0000 -> 0x9F000. - * 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; - start_low_mem += PAGE_SIZE; - } - - while (start_mem < high_memory) { - mem_map[MAP_NR(start_mem)] = 0; - start_mem += PAGE_SIZE; - } -#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) - reservedpages++; - else if (tmp < (unsigned long) &etext) - codepages++; - else - datapages++; - continue; - } - mem_map[MAP_NR(tmp)] = 1; - 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, - 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] = 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 - return; -} - -void si_meminfo(struct sysinfo *val) -{ - int i; - - i = high_memory >> PAGE_SHIFT; - 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) - continue; - val->totalram++; - if (!mem_map[i]) - continue; - val->sharedram += mem_map[i]-1; - } - val->totalram <<= PAGE_SHIFT; - val->sharedram <<= PAGE_SHIFT; - return; -} - - -/* - * This handles a generic mmap of a disk file. - */ -static unsigned long file_mmap_nopage(struct vm_area_struct * area, unsigned long address, - unsigned long page, int no_share) -{ - struct inode * inode = area->vm_inode; - unsigned int block; - int nr[8]; - int i, *p; - - address &= PAGE_MASK; - block = address - area->vm_start + area->vm_offset; - block >>= inode->i_sb->s_blocksize_bits; - i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; - p = nr; - do { - *p = bmap(inode,block); - i--; - block++; - p++; - } while (i > 0); - return bread_page(page, inode->i_dev, nr, inode->i_sb->s_blocksize, no_share); -} - -struct vm_operations_struct file_mmap = { - NULL, /* open */ - NULL, /* close */ - file_mmap_nopage, /* nopage */ - NULL, /* wppage */ - NULL, /* share */ - NULL, /* unmap */ -}; diff --git a/arch/i386/mm/mmap.c b/arch/i386/mm/mmap.c deleted file mode 100644 index fbbea985c..000000000 --- a/arch/i386/mm/mmap.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * linux/mm/mmap.c - * - * Written by obz. - */ -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/shm.h> -#include <linux/errno.h> -#include <linux/mman.h> -#include <linux/string.h> -#include <linux/malloc.h> - -#include <asm/segment.h> -#include <asm/system.h> - -static int anon_map(struct inode *, struct file *, struct vm_area_struct *); - -/* - * description of effects of mapping type and prot in current implementation. - * this is due to the limited x86 page protection hardware. The expected - * behavior is in parens: - * - * map_type prot - * PROT_NONE PROT_READ PROT_WRITE PROT_EXEC - * MAP_SHARED r: (no) no r: (yes) yes r: (no) yes r: (no) yes - * w: (no) no w: (no) no w: (yes) yes w: (no) no - * x: (no) no x: (no) yes x: (no) yes x: (yes) yes - * - * MAP_PRIVATE r: (no) no r: (yes) yes r: (no) yes r: (no) yes - * w: (no) no w: (no) no w: (copy) copy w: (no) no - * x: (no) no x: (no) yes x: (no) yes x: (yes) yes - * - */ - -int do_mmap(struct file * file, unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, unsigned long off) -{ - int mask, error; - struct vm_area_struct * vma; - - if ((len = PAGE_ALIGN(len)) == 0) - return addr; - - if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len) - return -EINVAL; - - /* offset overflow? */ - if (off + len < off) - return -EINVAL; - - /* - * do simple checking here so the lower-level routines won't have - * to. we assume access permissions have been handled by the open - * of the memory object, so we don't do any here. - */ - - if (file != NULL) { - switch (flags & MAP_TYPE) { - case MAP_SHARED: - if ((prot & PROT_WRITE) && !(file->f_mode & 2)) - return -EACCES; - /* fall through */ - case MAP_PRIVATE: - if (!(file->f_mode & 1)) - return -EACCES; - break; - - default: - return -EINVAL; - } - if ((flags & MAP_DENYWRITE) && (file->f_inode->i_wcount > 0)) - return -ETXTBSY; - } else if ((flags & MAP_TYPE) == MAP_SHARED) - return -EINVAL; - - /* - * obtain the address to map to. we verify (or select) it and ensure - * that it represents a valid section of the address space. - */ - - if (flags & MAP_FIXED) { - if (addr & ~PAGE_MASK) - return -EINVAL; - if (len > TASK_SIZE || addr > TASK_SIZE - len) - return -EINVAL; - } else { - addr = get_unmapped_area(len); - if (!addr) - return -ENOMEM; - } - - /* - * determine the object being mapped and call the appropriate - * specific mapper. the address has already been validated, but - * not unmapped, but the maps are removed from the list. - */ - if (file && (!file->f_op || !file->f_op->mmap)) - return -ENODEV; - mask = PAGE_PRESENT; - if (prot & (PROT_READ | PROT_EXEC)) - mask |= PAGE_READONLY; - if (prot & PROT_WRITE) - if ((flags & MAP_TYPE) == MAP_PRIVATE) - mask |= PAGE_COPY; - else - mask |= PAGE_SHARED; - - vma = (struct vm_area_struct *)kmalloc(sizeof(struct vm_area_struct), - GFP_KERNEL); - if (!vma) - return -ENOMEM; - - vma->vm_task = current; - vma->vm_start = addr; - vma->vm_end = addr + len; - vma->vm_page_prot = mask; - vma->vm_flags = prot & (VM_READ | VM_WRITE | VM_EXEC); - vma->vm_flags |= flags & (VM_GROWSDOWN | VM_DENYWRITE | VM_EXECUTABLE); - - if (file) { - if (file->f_mode & 1) - vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; - if (flags & MAP_SHARED) { - vma->vm_flags |= VM_SHARED | VM_MAYSHARE; - if (!(file->f_mode & 2)) - vma->vm_flags &= ~VM_MAYWRITE; - } - } else - vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; - vma->vm_ops = NULL; - vma->vm_offset = off; - vma->vm_inode = NULL; - vma->vm_pte = 0; - - do_munmap(addr, len); /* Clear old maps */ - - if (file) - error = file->f_op->mmap(file->f_inode, file, vma); - else - error = anon_map(NULL, NULL, vma); - - if (error) { - kfree(vma); - return error; - } - insert_vm_struct(current, vma); - merge_segments(current->mm->mmap); - return addr; -} - -/* - * Get an address range which is currently unmapped. - * For mmap() without MAP_FIXED and shmat() with addr=0. - * Return value 0 means ENOMEM. - */ -unsigned long get_unmapped_area(unsigned long len) -{ - struct vm_area_struct * vmm; - unsigned long gap_start = 0, gap_end; - - for (vmm = current->mm->mmap; ; vmm = vmm->vm_next) { - if (gap_start < SHM_RANGE_START) - gap_start = SHM_RANGE_START; - if (!vmm || ((gap_end = vmm->vm_start) > SHM_RANGE_END)) - gap_end = SHM_RANGE_END; - gap_start = PAGE_ALIGN(gap_start); - gap_end &= PAGE_MASK; - if ((gap_start <= gap_end) && (gap_end - gap_start >= len)) - return gap_start; - if (!vmm) - return 0; - gap_start = vmm->vm_end; - } -} - -asmlinkage int sys_mmap(unsigned long *buffer) -{ - int error; - unsigned long flags; - struct file * file = NULL; - - error = verify_area(VERIFY_READ, buffer, 6*sizeof(long)); - if (error) - return error; - flags = get_fs_long(buffer+3); - if (!(flags & MAP_ANONYMOUS)) { - unsigned long fd = get_fs_long(buffer+4); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - return -EBADF; - } - return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1), - get_fs_long(buffer+2), flags, get_fs_long(buffer+5)); -} - -/* - * Normal function to fix up a mapping - * This function is the default for when an area has no specific - * function. This may be used as part of a more specific routine. - * This function works out what part of an area is affected and - * adjusts the mapping information. Since the actual page - * manipulation is done in do_mmap(), none need be done here, - * though it would probably be more appropriate. - * - * By the time this function is called, the area struct has been - * removed from the process mapping list, so it needs to be - * reinserted if necessary. - * - * The 4 main cases are: - * Unmapping the whole area - * Unmapping from the start of the segment to a point in it - * Unmapping from an intermediate point to the end - * Unmapping between to intermediate points, making a hole. - * - * Case 4 involves the creation of 2 new areas, for each side of - * the hole. - */ -void unmap_fixup(struct vm_area_struct *area, - unsigned long addr, size_t len) -{ - struct vm_area_struct *mpnt; - unsigned long end = addr + len; - - if (addr < area->vm_start || addr >= area->vm_end || - end <= area->vm_start || end > area->vm_end || - end < addr) - { - printk("unmap_fixup: area=%lx-%lx, unmap %lx-%lx!!\n", - area->vm_start, area->vm_end, addr, end); - return; - } - - /* Unmapping the whole area */ - if (addr == area->vm_start && end == area->vm_end) { - if (area->vm_ops && area->vm_ops->close) - area->vm_ops->close(area); - if (area->vm_inode) - iput(area->vm_inode); - return; - } - - /* Work out to one of the ends */ - if (addr >= area->vm_start && end == area->vm_end) - area->vm_end = addr; - if (addr == area->vm_start && end <= area->vm_end) { - area->vm_offset += (end - area->vm_start); - area->vm_start = end; - } - - /* Unmapping a hole */ - if (addr > area->vm_start && end < area->vm_end) - { - /* Add end mapping -- leave beginning for below */ - mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); - - if (!mpnt) - return; - *mpnt = *area; - mpnt->vm_offset += (end - area->vm_start); - mpnt->vm_start = end; - if (mpnt->vm_inode) - mpnt->vm_inode->i_count++; - if (mpnt->vm_ops && mpnt->vm_ops->open) - mpnt->vm_ops->open(mpnt); - area->vm_end = addr; /* Truncate area */ - insert_vm_struct(current, mpnt); - } - - /* construct whatever mapping is needed */ - mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); - if (!mpnt) - return; - *mpnt = *area; - if (mpnt->vm_ops && mpnt->vm_ops->open) - mpnt->vm_ops->open(mpnt); - if (area->vm_ops && area->vm_ops->close) { - area->vm_end = area->vm_start; - area->vm_ops->close(area); - } - insert_vm_struct(current, mpnt); -} - -asmlinkage int sys_munmap(unsigned long addr, size_t len) -{ - return do_munmap(addr, len); -} - -/* - * Munmap is split into 2 main parts -- this part which finds - * what needs doing, and the areas themselves, which do the - * work. This now handles partial unmappings. - * Jeremy Fitzhardine <jeremy@sw.oz.au> - */ -int do_munmap(unsigned long addr, size_t len) -{ - struct vm_area_struct *mpnt, **npp, *free; - - if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr) - return -EINVAL; - - if ((len = PAGE_ALIGN(len)) == 0) - return 0; - - /* - * Check if this memory area is ok - put it on the temporary - * list if so.. The checks here are pretty simple -- - * every area affected in some way (by any overlap) is put - * on the list. If nothing is put on, nothing is affected. - */ - npp = ¤t->mm->mmap; - free = NULL; - for (mpnt = *npp; mpnt != NULL; mpnt = *npp) { - unsigned long end = addr+len; - - if ((addr < mpnt->vm_start && end <= mpnt->vm_start) || - (addr >= mpnt->vm_end && end > mpnt->vm_end)) - { - npp = &mpnt->vm_next; - continue; - } - - *npp = mpnt->vm_next; - mpnt->vm_next = free; - free = mpnt; - } - - if (free == NULL) - return 0; - - /* - * Ok - we have the memory areas we should free on the 'free' list, - * so release them, and unmap the page range.. - * If the one of the segments is only being partially unmapped, - * it will put new vm_area_struct(s) into the address space. - */ - while (free) { - unsigned long st, end; - - mpnt = free; - free = free->vm_next; - - st = addr < mpnt->vm_start ? mpnt->vm_start : addr; - end = addr+len; - end = end > mpnt->vm_end ? mpnt->vm_end : end; - - if (mpnt->vm_ops && mpnt->vm_ops->unmap) - mpnt->vm_ops->unmap(mpnt, st, end-st); - else - unmap_fixup(mpnt, st, end-st); - - kfree(mpnt); - } - - unmap_page_range(addr, len); - return 0; -} - -/* This is used for a general mmap of a disk file */ -int generic_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) -{ - extern struct vm_operations_struct file_mmap; - - if (vma->vm_page_prot & PAGE_RW) /* only PAGE_COW or read-only supported right now */ - return -EINVAL; - if (vma->vm_offset & (inode->i_sb->s_blocksize - 1)) - return -EINVAL; - if (!inode->i_sb || !S_ISREG(inode->i_mode)) - return -EACCES; - if (!inode->i_op || !inode->i_op->bmap) - return -ENOEXEC; - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - vma->vm_inode = inode; - inode->i_count++; - vma->vm_ops = &file_mmap; - return 0; -} - -/* - * Insert vm structure into process list sorted by address. - */ -void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp) -{ - struct vm_area_struct **p, *mpnt; - - p = &t->mm->mmap; - while ((mpnt = *p) != NULL) { - if (mpnt->vm_start > vmp->vm_start) - break; - if (mpnt->vm_end > vmp->vm_start) - printk("insert_vm_struct: overlapping memory areas\n"); - p = &mpnt->vm_next; - } - vmp->vm_next = mpnt; - *p = vmp; -} - -/* - * Merge a list of memory segments if possible. - * Redundant vm_area_structs are freed. - * This assumes that the list is ordered by address. - */ -void merge_segments(struct vm_area_struct *mpnt) -{ - struct vm_area_struct *prev, *next; - - if (mpnt == NULL) - return; - - for(prev = mpnt, mpnt = mpnt->vm_next; - mpnt != NULL; - prev = mpnt, mpnt = next) - { - next = mpnt->vm_next; - - /* - * To share, we must have the same inode, operations.. - */ - if (mpnt->vm_inode != prev->vm_inode) - continue; - if (mpnt->vm_pte != prev->vm_pte) - continue; - if (mpnt->vm_ops != prev->vm_ops) - continue; - if (mpnt->vm_page_prot != prev->vm_page_prot || - mpnt->vm_flags != prev->vm_flags) - continue; - if (prev->vm_end != mpnt->vm_start) - continue; - /* - * and if we have an inode, the offsets must be contiguous.. - */ - if ((mpnt->vm_inode != NULL) || (mpnt->vm_flags & VM_SHM)) { - if (prev->vm_offset + prev->vm_end - prev->vm_start != mpnt->vm_offset) - continue; - } - - /* - * merge prev with mpnt and set up pointers so the new - * big segment can possibly merge with the next one. - * The old unused mpnt is freed. - */ - prev->vm_end = mpnt->vm_end; - prev->vm_next = mpnt->vm_next; - if (mpnt->vm_ops && mpnt->vm_ops->close) { - mpnt->vm_offset += mpnt->vm_end - mpnt->vm_start; - mpnt->vm_start = mpnt->vm_end; - mpnt->vm_ops->close(mpnt); - } - if (mpnt->vm_inode) - mpnt->vm_inode->i_count--; - kfree_s(mpnt, sizeof(*mpnt)); - mpnt = prev; - } -} - -/* - * Map memory not associated with any file into a process - * address space. Adjacent memory is merged. - */ -static int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma) -{ - if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -ENOMEM; - return 0; -} diff --git a/arch/i386/mm/mprotect.c b/arch/i386/mm/mprotect.c deleted file mode 100644 index 99252183b..000000000 --- a/arch/i386/mm/mprotect.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * linux/mm/mprotect.c - * - * (C) Copyright 1994 Linus Torvalds - */ -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/shm.h> -#include <linux/errno.h> -#include <linux/mman.h> -#include <linux/string.h> -#include <linux/malloc.h> - -#include <asm/segment.h> -#include <asm/system.h> - -#define CHG_MASK (PAGE_MASK | PAGE_ACCESSED | PAGE_DIRTY | PAGE_PWT | PAGE_PCD) - -static void change_protection(unsigned long start, unsigned long end, int prot) -{ - unsigned long *page_table, *dir; - unsigned long page, offset; - int nr; - - dir = PAGE_DIR_OFFSET(current->tss.cr3, start); - offset = (start >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - nr = (end - start) >> PAGE_SHIFT; - while (nr > 0) { - page = *dir; - dir++; - if (!(page & PAGE_PRESENT)) { - nr = nr - PTRS_PER_PAGE + offset; - offset = 0; - continue; - } - page_table = offset + (unsigned long *) (page & PAGE_MASK); - offset = PTRS_PER_PAGE - offset; - if (offset > nr) - offset = nr; - nr = nr - offset; - do { - page = *page_table; - if (page & PAGE_PRESENT) - *page_table = (page & CHG_MASK) | prot; - ++page_table; - } while (--offset); - } - return; -} - -static inline int mprotect_fixup_all(struct vm_area_struct * vma, - int newflags, int prot) -{ - vma->vm_flags = newflags; - vma->vm_page_prot = prot; - return 0; -} - -static inline int mprotect_fixup_start(struct vm_area_struct * vma, - unsigned long end, - int newflags, int prot) -{ - struct vm_area_struct * n; - - n = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!n) - return -ENOMEM; - *n = *vma; - vma->vm_start = end; - n->vm_end = end; - vma->vm_offset += vma->vm_start - n->vm_start; - n->vm_flags = newflags; - n->vm_page_prot = prot; - if (n->vm_inode) - n->vm_inode->i_count++; - if (n->vm_ops && n->vm_ops->open) - n->vm_ops->open(n); - insert_vm_struct(current, n); - return 0; -} - -static inline int mprotect_fixup_end(struct vm_area_struct * vma, - unsigned long start, - int newflags, int prot) -{ - struct vm_area_struct * n; - - n = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!n) - return -ENOMEM; - *n = *vma; - vma->vm_end = start; - n->vm_start = start; - n->vm_offset += n->vm_start - vma->vm_start; - n->vm_flags = newflags; - n->vm_page_prot = prot; - if (n->vm_inode) - n->vm_inode->i_count++; - if (n->vm_ops && n->vm_ops->open) - n->vm_ops->open(n); - insert_vm_struct(current, n); - return 0; -} - -static inline int mprotect_fixup_middle(struct vm_area_struct * vma, - unsigned long start, unsigned long end, - int newflags, int prot) -{ - struct vm_area_struct * left, * right; - - left = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!left) - return -ENOMEM; - right = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!right) { - kfree(left); - return -ENOMEM; - } - *left = *vma; - *right = *vma; - left->vm_end = start; - vma->vm_start = start; - vma->vm_end = end; - right->vm_start = end; - vma->vm_offset += vma->vm_start - left->vm_start; - right->vm_offset += right->vm_start - left->vm_start; - vma->vm_flags = newflags; - vma->vm_page_prot = prot; - if (vma->vm_inode) - vma->vm_inode->i_count += 2; - if (vma->vm_ops && vma->vm_ops->open) { - vma->vm_ops->open(left); - vma->vm_ops->open(right); - } - insert_vm_struct(current, left); - insert_vm_struct(current, right); - return 0; -} - -static int mprotect_fixup(struct vm_area_struct * vma, - unsigned long start, unsigned long end, unsigned int newflags) -{ - int prot, error; - - if (newflags == vma->vm_flags) - return 0; - prot = PAGE_PRESENT; - if (newflags & (VM_READ | VM_EXEC)) - prot |= PAGE_READONLY; - if (newflags & VM_WRITE) - if (newflags & VM_SHARED) - prot |= PAGE_SHARED; - else - prot |= PAGE_COPY; - - if (start == vma->vm_start) - if (end == vma->vm_end) - error = mprotect_fixup_all(vma, newflags, prot); - else - error = mprotect_fixup_start(vma, end, newflags, prot); - else if (end == vma->vm_end) - error = mprotect_fixup_end(vma, start, newflags, prot); - else - error = mprotect_fixup_middle(vma, start, end, newflags, prot); - - if (error) - return error; - - change_protection(start, end, prot); - return 0; -} - -asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot) -{ - unsigned long end, tmp; - struct vm_area_struct * vma, * next; - int error; - - if (start & ~PAGE_MASK) - return -EINVAL; - len = (len + ~PAGE_MASK) & PAGE_MASK; - end = start + len; - if (end < start) - return -EINVAL; - if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) - return -EINVAL; - if (end == start) - return 0; - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return -EFAULT; - if (vma->vm_end > start) - break; - } - if (vma->vm_start > start) - return -EFAULT; - - for ( ; ; ) { - unsigned int newflags; - - /* Here we know that vma->vm_start <= start < vma->vm_end. */ - - newflags = prot | (vma->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC)); - if ((newflags & ~(newflags >> 4)) & 0xf) { - error = -EACCES; - break; - } - - if (vma->vm_end >= end) { - error = mprotect_fixup(vma, start, end, newflags); - break; - } - - tmp = vma->vm_end; - next = vma->vm_next; - error = mprotect_fixup(vma, start, tmp, newflags); - if (error) - break; - start = tmp; - vma = next; - if (!vma || vma->vm_start != start) { - error = -EFAULT; - break; - } - } - merge_segments(current->mm->mmap); - return error; -} diff --git a/arch/i386/mm/swap.c b/arch/i386/mm/swap.c deleted file mode 100644 index f7a1f54b3..000000000 --- a/arch/i386/mm/swap.c +++ /dev/null @@ -1,1017 +0,0 @@ -/* - * linux/mm/swap.c - * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - */ - -/* - * This file should contain most things doing the swapping from/to disk. - * Started 18.12.91 - */ - -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/head.h> -#include <linux/kernel.h> -#include <linux/kernel_stat.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/fs.h> - -#include <asm/system.h> /* for cli()/sti() */ -#include <asm/bitops.h> - -#define MAX_SWAPFILES 8 - -#define SWP_USED 1 -#define SWP_WRITEOK 3 - -#define SWP_TYPE(entry) (((entry) & 0xfe) >> 1) -#define SWP_OFFSET(entry) ((entry) >> PAGE_SHIFT) -#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << PAGE_SHIFT)) - -int min_free_pages = 20; - -static int nr_swapfiles = 0; -static struct wait_queue * lock_queue = NULL; - -static struct swap_info_struct { - unsigned long flags; - struct inode * swap_file; - unsigned int swap_device; - unsigned char * swap_map; - unsigned char * swap_lockmap; - int pages; - int lowest_bit; - int highest_bit; - unsigned long max; -} swap_info[MAX_SWAPFILES]; - -extern int shm_swap (int); - -unsigned long *swap_cache; - -#ifdef SWAP_CACHE_INFO -unsigned long swap_cache_add_total = 0; -unsigned long swap_cache_add_success = 0; -unsigned long swap_cache_del_total = 0; -unsigned long swap_cache_del_success = 0; -unsigned long swap_cache_find_total = 0; -unsigned long swap_cache_find_success = 0; - -extern inline void show_swap_cache_info(void) -{ - printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n", - swap_cache_add_total, swap_cache_add_success, - swap_cache_del_total, swap_cache_del_success, - swap_cache_find_total, swap_cache_find_success); -} -#endif - -extern inline int add_to_swap_cache(unsigned long addr, unsigned long entry) -{ - struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)]; - -#ifdef SWAP_CACHE_INFO - swap_cache_add_total++; -#endif - if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { - __asm__ __volatile__ ( - "xchgl %0,%1\n" - : "=m" (swap_cache[addr >> PAGE_SHIFT]), - "=r" (entry) - : "0" (swap_cache[addr >> PAGE_SHIFT]), - "1" (entry)); - if (entry) { - printk("swap_cache: replacing non-NULL entry\n"); - } -#ifdef SWAP_CACHE_INFO - swap_cache_add_success++; -#endif - return 1; - } - return 0; -} - -extern inline int add_to_swap_cache(unsigned long addr, unsigned long entry) -{ - struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)]; - -#ifdef SWAP_CACHE_INFO - swap_cache_add_total++; -#endif - if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { - __asm__ __volatile__ ( - "xchgl %0,%1\n" - : "=m" (swap_cache[addr >> PAGE_SHIFT]), - "=r" (entry) - : "0" (swap_cache[addr >> PAGE_SHIFT]), - "1" (entry) - ); - if (entry) { - printk("swap_cache: replacing non-NULL entry\n"); - } -#ifdef SWAP_CACHE_INFO - swap_cache_add_success++; -#endif - return 1; - } - return 0; -} - -static unsigned long init_swap_cache(unsigned long mem_start, - unsigned long mem_end) -{ - unsigned long swap_cache_size; - - mem_start = (mem_start + 15) & ~15; - swap_cache = (unsigned long *) mem_start; - swap_cache_size = mem_end >> PAGE_SHIFT; - memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long)); - return (unsigned long) (swap_cache + swap_cache_size); -} - -void rw_swap_page(int rw, unsigned long entry, char * buf) -{ - unsigned long type, offset; - struct swap_info_struct * p; - - type = SWP_TYPE(entry); - if (type >= nr_swapfiles) { - printk("Internal error: bad swap-device\n"); - return; - } - p = &swap_info[type]; - offset = SWP_OFFSET(entry); - if (offset >= p->max) { - printk("rw_swap_page: weirdness\n"); - return; - } - if (!(p->flags & SWP_USED)) { - printk("Trying to swap to unused swap-device\n"); - return; - } - while (set_bit(offset,p->swap_lockmap)) - sleep_on(&lock_queue); - if (rw == READ) - kstat.pswpin++; - else - kstat.pswpout++; - if (p->swap_device) { - ll_rw_page(rw,p->swap_device,offset,buf); - } else if (p->swap_file) { - struct inode *swapf = p->swap_file; - unsigned int zones[8]; - int i; - if (swapf->i_op->bmap == NULL - && swapf->i_op->smap != NULL){ - /* - With MsDOS, we use msdos_smap which return - a sector number (not a cluster or block number). - It is a patch to enable the UMSDOS project. - Other people are working on better solution. - - It sounds like ll_rw_swap_file defined - it operation size (sector size) based on - PAGE_SIZE and the number of block to read. - So using bmap or smap should work even if - smap will require more blocks. - */ - int j; - unsigned int block = offset << 3; - - for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){ - if (!(zones[i] = swapf->i_op->smap(swapf,block++))) { - printk("rw_swap_page: bad swap file\n"); - return; - } - } - }else{ - int j; - unsigned int block = offset - << (12 - swapf->i_sb->s_blocksize_bits); - - for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize) - if (!(zones[i] = bmap(swapf,block++))) { - printk("rw_swap_page: bad swap file\n"); - return; - } - } - ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf); - } else - printk("re_swap_page: no swap file or device\n"); - if (offset && !clear_bit(offset,p->swap_lockmap)) - printk("rw_swap_page: lock already cleared\n"); - wake_up(&lock_queue); -} - -unsigned int get_swap_page(void) -{ - struct swap_info_struct * p; - unsigned int offset, type; - - p = swap_info; - for (type = 0 ; type < nr_swapfiles ; type++,p++) { - if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK) - continue; - for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) { - if (p->swap_map[offset]) - continue; - p->swap_map[offset] = 1; - nr_swap_pages--; - if (offset == p->highest_bit) - p->highest_bit--; - p->lowest_bit = offset; - return SWP_ENTRY(type,offset); - } - } - return 0; -} - -unsigned long swap_duplicate(unsigned long entry) -{ - struct swap_info_struct * p; - unsigned long offset, type; - - if (!entry) - return 0; - offset = SWP_OFFSET(entry); - type = SWP_TYPE(entry); - if (type == SHM_SWP_TYPE) - return entry; - if (type >= nr_swapfiles) { - printk("Trying to duplicate nonexistent swap-page\n"); - return 0; - } - p = type + swap_info; - if (offset >= p->max) { - printk("swap_duplicate: weirdness\n"); - return 0; - } - if (!p->swap_map[offset]) { - printk("swap_duplicate: trying to duplicate unused page\n"); - return 0; - } - p->swap_map[offset]++; - return entry; -} - -void swap_free(unsigned long entry) -{ - struct swap_info_struct * p; - unsigned long offset, type; - - if (!entry) - return; - type = SWP_TYPE(entry); - if (type == SHM_SWP_TYPE) - return; - if (type >= nr_swapfiles) { - printk("Trying to free nonexistent swap-page\n"); - return; - } - p = & swap_info[type]; - offset = SWP_OFFSET(entry); - if (offset >= p->max) { - printk("swap_free: weirdness\n"); - return; - } - if (!(p->flags & SWP_USED)) { - printk("Trying to free swap from unused swap-device\n"); - return; - } - while (set_bit(offset,p->swap_lockmap)) - sleep_on(&lock_queue); - if (offset < p->lowest_bit) - p->lowest_bit = offset; - if (offset > p->highest_bit) - p->highest_bit = offset; - if (!p->swap_map[offset]) - printk("swap_free: swap-space map bad (entry %08lx)\n",entry); - else - if (!--p->swap_map[offset]) - nr_swap_pages++; - if (!clear_bit(offset,p->swap_lockmap)) - printk("swap_free: lock already cleared\n"); - wake_up(&lock_queue); -} - -unsigned long swap_in(unsigned long entry) -{ - unsigned long page; - - if (!(page = get_free_page(GFP_KERNEL))) { - oom(current); - return BAD_PAGE; - } - read_swap_page(entry, (char *) page); - if (add_to_swap_cache(page, entry)) - return page | PAGE_PRESENT; - swap_free(entry); - return page | PAGE_DIRTY | PAGE_PRESENT; -} - -static inline int try_to_swap_out(unsigned long * table_ptr) -{ - unsigned long page, entry; - - page = *table_ptr; - if (!(PAGE_PRESENT & page)) - return 0; - if (page >= high_memory) - return 0; - if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED) - return 0; - - if ((PAGE_DIRTY & page) && delete_from_swap_cache(page)) { - *table_ptr &= ~PAGE_ACCESSED; - return 0; - } - if (PAGE_ACCESSED & page) { - *table_ptr &= ~PAGE_ACCESSED; - return 0; - } - if (PAGE_DIRTY & page) { - page &= PAGE_MASK; - if (mem_map[MAP_NR(page)] != 1) - return 0; - if (!(entry = get_swap_page())) - return 0; - *table_ptr = entry; - invalidate(); - write_swap_page(entry, (char *) page); - free_page(page); - return 1; - } - if ((entry = find_in_swap_cache(page))) { - if (mem_map[MAP_NR(page)] != 1) { - *table_ptr |= PAGE_DIRTY; - printk("Aiee.. duplicated cached swap-cache entry\n"); - return 0; - } - *table_ptr = entry; - invalidate(); - free_page(page & PAGE_MASK); - return 1; - } - page &= PAGE_MASK; - *table_ptr = 0; - invalidate(); - free_page(page); - return 1 + mem_map[MAP_NR(page)]; -} - -/* - * A new implementation of swap_out(). We do not swap complete processes, - * but only a small number of blocks, before we continue with the next - * process. The number of blocks actually swapped is determined on the - * number of page faults, that this process actually had in the last time, - * so we won't swap heavily used processes all the time ... - * - * Note: the priority argument is a hint on much CPU to waste with the - * swap block search, not a hint, of how much blocks to swap with - * each process. - * - * (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de - */ - -/* - * These are the minimum and maximum number of pages to swap from one process, - * before proceeding to the next: - */ -#define SWAP_MIN 4 -#define SWAP_MAX 32 - -/* - * The actual number of pages to swap is determined as: - * SWAP_RATIO / (number of recent major page faults) - */ -#define SWAP_RATIO 128 - -static int swap_out_process(struct task_struct * p) -{ - unsigned long address; - unsigned long offset; - unsigned long *pgdir; - unsigned long pg_table; - - /* - * Go through process' page directory. - */ - address = p->mm->swap_address; - pgdir = (address >> PGDIR_SHIFT) + (unsigned long *) p->tss.cr3; - offset = address & ~PGDIR_MASK; - address &= PGDIR_MASK; - for ( ; address < TASK_SIZE ; - pgdir++, address = address + PGDIR_SIZE, offset = 0) { - pg_table = *pgdir; - if (pg_table >= high_memory) - continue; - if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED) - continue; - if (!(PAGE_PRESENT & pg_table)) { - printk("swap_out_process (%s): bad page-table at vm %08lx: %08lx\n", - p->comm, address + offset, pg_table); - *pgdir = 0; - continue; - } - pg_table &= 0xfffff000; - - /* - * Go through this page table. - */ - for( ; offset < ~PGDIR_MASK ; offset += PAGE_SIZE) { - switch(try_to_swap_out((unsigned long *) (pg_table + (offset >> 10)))) { - case 0: - break; - - case 1: - p->mm->rss--; - /* continue with the following page the next time */ - p->mm->swap_address = address + offset + PAGE_SIZE; - return 1; - - default: - p->mm->rss--; - break; - } - } - } - /* - * Finish work with this process, if we reached the end of the page - * directory. Mark restart from the beginning the next time. - */ - p->mm->swap_address = 0; - return 0; -} - -static int swap_out(unsigned int priority) -{ - static int swap_task; - int loop; - int counter = NR_TASKS * 2 >> priority; - struct task_struct *p; - - counter = NR_TASKS * 2 >> priority; - for(; counter >= 0; counter--, swap_task++) { - /* - * Check that swap_task is suitable for swapping. If not, look for - * the next suitable process. - */ - loop = 0; - while(1) { - if (swap_task >= NR_TASKS) { - swap_task = 1; - if (loop) - /* all processes are unswappable or already swapped out */ - return 0; - loop = 1; - } - - p = task[swap_task]; - if (p && p->mm->swappable && p->mm->rss) - break; - - swap_task++; - } - - /* - * Determine the number of pages to swap from this process. - */ - if (!p->mm->swap_cnt) { - p->mm->dec_flt = (p->mm->dec_flt * 3) / 4 + p->mm->maj_flt - p->mm->old_maj_flt; - p->mm->old_maj_flt = p->mm->maj_flt; - - if (p->mm->dec_flt >= SWAP_RATIO / SWAP_MIN) { - p->mm->dec_flt = SWAP_RATIO / SWAP_MIN; - p->mm->swap_cnt = SWAP_MIN; - } else if (p->mm->dec_flt <= SWAP_RATIO / SWAP_MAX) - p->mm->swap_cnt = SWAP_MAX; - else - p->mm->swap_cnt = SWAP_RATIO / p->mm->dec_flt; - } - if (swap_out_process(p)) { - if ((--p->mm->swap_cnt) == 0) - swap_task++; - return 1; - } - } - return 0; -} - -static int try_to_free_page(int priority) -{ - int i=6; - - while (i--) { - if (priority != GFP_NOBUFFER && shrink_buffers(i)) - return 1; - if (shm_swap(i)) - return 1; - if (swap_out(i)) - return 1; - } - return 0; -} - -static inline void add_mem_queue(struct mem_list * head, struct mem_list * entry) -{ - entry->prev = head; - entry->next = head->next; - entry->next->prev = entry; - head->next = entry; -} - -static inline void remove_mem_queue(struct mem_list * head, struct mem_list * entry) -{ - entry->next->prev = entry->prev; - entry->prev->next = entry->next; -} - -/* - * Free_page() adds the page to the free lists. This is optimized for - * fast normal cases (no error jumps taken normally). - * - * The way to optimize jumps for gcc-2.2.2 is to: - * - select the "normal" case and put it inside the if () { XXX } - * - no else-statements if you can avoid them - * - * With the above two rules, you get a straight-line execution path - * for the normal case, giving better asm-code. - */ - -/* - * Buddy system. Hairy. You really aren't expected to understand this - */ -static inline void free_pages_ok(unsigned long addr, unsigned long order) -{ - unsigned long index = addr >> (PAGE_SHIFT + 1 + order); - unsigned long mask = PAGE_MASK << order; - - addr &= mask; - nr_free_pages += 1 << order; - while (order < NR_MEM_LISTS-1) { - if (!change_bit(index, free_area_map[order])) - break; - remove_mem_queue(free_area_list+order, (struct mem_list *) (addr ^ (1+~mask))); - order++; - index >>= 1; - mask <<= 1; - addr &= mask; - } - add_mem_queue(free_area_list+order, (struct mem_list *) addr); -} - -static inline void check_free_buffers(unsigned long addr) -{ - struct buffer_head * bh; - - bh = buffer_pages[MAP_NR(addr)]; - if (bh) { - struct buffer_head *tmp = bh; - do { - if (tmp->b_list == BUF_SHARED && tmp->b_dev != 0xffff) - refile_buffer(tmp); - tmp = tmp->b_this_page; - } while (tmp != bh); - } -} - -void free_pages(unsigned long addr, unsigned long order) -{ - if (addr < high_memory) { - unsigned long flag; - unsigned short * map = mem_map + MAP_NR(addr); - if (*map) { - if (!(*map & MAP_PAGE_RESERVED)) { - save_flags(flag); - cli(); - if (!--*map) { - free_pages_ok(addr, order); - delete_from_swap_cache(addr); - } - restore_flags(flag); - if (*map == 1) - check_free_buffers(addr); - } - return; - } - printk("Trying to free free memory (%08lx): memory probably corrupted\n",addr); - printk("PC = %08lx\n",*(((unsigned long *)&addr)-1)); - return; - } -} - -/* - * Some ugly macros to speed up __get_free_pages().. - */ -#define RMQUEUE(order) \ -do { struct mem_list * queue = free_area_list+order; \ - unsigned long new_order = order; \ - do { struct mem_list *next = queue->next; \ - if (queue != next) { \ - (queue->next = next->next)->prev = queue; \ - mark_used((unsigned long) next, new_order); \ - nr_free_pages -= 1 << order; \ - restore_flags(flags); \ - EXPAND(next, order, new_order); \ - return (unsigned long) next; \ - } new_order++; queue++; \ - } while (new_order < NR_MEM_LISTS); \ -} while (0) - -static inline int mark_used(unsigned long addr, unsigned long order) -{ - return change_bit(addr >> (PAGE_SHIFT+1+order), free_area_map[order]); -} - -#define EXPAND(addr,low,high) \ -do { unsigned long size = PAGE_SIZE << high; \ - while (high > low) { \ - high--; size >>= 1; cli(); \ - add_mem_queue(free_area_list+high, addr); \ - mark_used((unsigned long) addr, high); \ - restore_flags(flags); \ - addr = (struct mem_list *) (size + (unsigned long) addr); \ - } mem_map[MAP_NR((unsigned long) addr)] = 1; \ -} while (0) - -unsigned long __get_free_pages(int priority, unsigned long order) -{ - unsigned long flags; - int reserved_pages; - - if (intr_count && priority != GFP_ATOMIC) { - static int count = 0; - if (++count < 5) { - printk("gfp called nonatomically from interrupt %p\n", - __builtin_return_address(0)); - priority = GFP_ATOMIC; - } - } - reserved_pages = 5; - if (priority != GFP_NFS) - reserved_pages = min_free_pages; - save_flags(flags); -repeat: - cli(); - if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) { - RMQUEUE(order); - restore_flags(flags); - return 0; - } - restore_flags(flags); - if (priority != GFP_BUFFER && try_to_free_page(priority)) - goto repeat; - return 0; -} - -/* - * Yes, I know this is ugly. Don't tell me. - */ -unsigned long __get_dma_pages(int priority, unsigned long order) -{ - unsigned long list = 0; - unsigned long result; - unsigned long limit = 16*1024*1024; - - /* if (EISA_bus) limit = ~0UL; */ - if (priority != GFP_ATOMIC) - priority = GFP_BUFFER; - for (;;) { - result = __get_free_pages(priority, order); - if (result < limit) /* covers failure as well */ - break; - *(unsigned long *) result = list; - list = result; - } - while (list) { - unsigned long tmp = list; - list = *(unsigned long *) list; - free_pages(tmp, order); - } - return result; -} - -/* - * Show free area list (used inside shift_scroll-lock stuff) - * We also calculate the percentage fragmentation. We do this by counting the - * memory on each free list with the exception of the first item on the list. - */ -void show_free_areas(void) -{ - unsigned long order, flags; - unsigned long total = 0; - - printk("Free pages: %6dkB\n ( ",nr_free_pages<<(PAGE_SHIFT-10)); - save_flags(flags); - cli(); - for (order=0 ; order < NR_MEM_LISTS; order++) { - struct mem_list * tmp; - unsigned long nr = 0; - for (tmp = free_area_list[order].next ; tmp != free_area_list + order ; tmp = tmp->next) { - nr ++; - } - total += nr * (4 << order); - printk("%lu*%ukB ", nr, 4 << order); - } - restore_flags(flags); - printk("= %lukB)\n", total); -#ifdef SWAP_CACHE_INFO - show_swap_cache_info(); -#endif -} - -/* - * Trying to stop swapping from a file is fraught with races, so - * we repeat quite a bit here when we have to pause. swapoff() - * isn't exactly timing-critical, so who cares? - */ -static int try_to_unuse(unsigned int type) -{ - int nr, pgt, pg; - unsigned long page, *ppage; - unsigned long tmp = 0; - struct task_struct *p; - - nr = 0; - -/* - * When we have to sleep, we restart the whole algorithm from the same - * task we stopped in. That at least rids us of all races. - */ -repeat: - for (; nr < NR_TASKS ; nr++) { - p = task[nr]; - if (!p) - continue; - for (pgt = 0 ; pgt < PTRS_PER_PAGE ; pgt++) { - ppage = pgt + ((unsigned long *) p->tss.cr3); - page = *ppage; - if (!page) - continue; - if (!(page & PAGE_PRESENT) || (page >= high_memory)) - continue; - if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED) - continue; - ppage = (unsigned long *) (page & PAGE_MASK); - for (pg = 0 ; pg < PTRS_PER_PAGE ; pg++,ppage++) { - page = *ppage; - if (!page) - continue; - if (page & PAGE_PRESENT) { - if (!(page = in_swap_cache(page))) - continue; - if (SWP_TYPE(page) != type) - continue; - *ppage |= PAGE_DIRTY; - delete_from_swap_cache(*ppage); - continue; - } - if (SWP_TYPE(page) != type) - continue; - if (!tmp) { - if (!(tmp = __get_free_page(GFP_KERNEL))) - return -ENOMEM; - goto repeat; - } - read_swap_page(page, (char *) tmp); - if (*ppage == page) { - *ppage = tmp | (PAGE_DIRTY | PAGE_PRIVATE); - ++p->mm->rss; - swap_free(page); - tmp = 0; - } - goto repeat; - } - } - } - free_page(tmp); - return 0; -} - -asmlinkage int sys_swapoff(const char * specialfile) -{ - struct swap_info_struct * p; - struct inode * inode; - unsigned int type; - int i; - - if (!suser()) - return -EPERM; - i = namei(specialfile,&inode); - if (i) - return i; - p = swap_info; - for (type = 0 ; type < nr_swapfiles ; type++,p++) { - if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK) - continue; - if (p->swap_file) { - if (p->swap_file == inode) - break; - } else { - if (!S_ISBLK(inode->i_mode)) - continue; - if (p->swap_device == inode->i_rdev) - break; - } - } - iput(inode); - if (type >= nr_swapfiles) - return -EINVAL; - p->flags = SWP_USED; - i = try_to_unuse(type); - if (i) { - p->flags = SWP_WRITEOK; - return i; - } - nr_swap_pages -= p->pages; - iput(p->swap_file); - p->swap_file = NULL; - p->swap_device = 0; - vfree(p->swap_map); - p->swap_map = NULL; - free_page((long) p->swap_lockmap); - p->swap_lockmap = NULL; - p->flags = 0; - return 0; -} - -/* - * Written 01/25/92 by Simmule Turner, heavily changed by Linus. - * - * The swapon system call - */ -asmlinkage int sys_swapon(const char * specialfile) -{ - struct swap_info_struct * p; - struct inode * swap_inode; - unsigned int type; - int i,j; - int error; - - if (!suser()) - return -EPERM; - p = swap_info; - for (type = 0 ; type < nr_swapfiles ; type++,p++) - if (!(p->flags & SWP_USED)) - break; - if (type >= MAX_SWAPFILES) - return -EPERM; - if (type >= nr_swapfiles) - nr_swapfiles = type+1; - p->flags = SWP_USED; - p->swap_file = NULL; - p->swap_device = 0; - p->swap_map = NULL; - p->swap_lockmap = NULL; - p->lowest_bit = 0; - p->highest_bit = 0; - p->max = 1; - error = namei(specialfile,&swap_inode); - if (error) - goto bad_swap; - p->swap_file = swap_inode; - error = -EBUSY; - if (swap_inode->i_count != 1) - goto bad_swap; - error = -EINVAL; - if (S_ISBLK(swap_inode->i_mode)) { - p->swap_device = swap_inode->i_rdev; - p->swap_file = NULL; - iput(swap_inode); - error = -ENODEV; - if (!p->swap_device) - goto bad_swap; - error = -EBUSY; - for (i = 0 ; i < nr_swapfiles ; i++) { - if (i == type) - continue; - if (p->swap_device == swap_info[i].swap_device) - goto bad_swap; - } - } else if (!S_ISREG(swap_inode->i_mode)) - goto bad_swap; - p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER); - if (!p->swap_lockmap) { - printk("Unable to start swapping: out of memory :-)\n"); - error = -ENOMEM; - goto bad_swap; - } - read_swap_page(SWP_ENTRY(type,0), (char *) p->swap_lockmap); - if (memcmp("SWAP-SPACE",p->swap_lockmap+4086,10)) { - printk("Unable to find swap-space signature\n"); - error = -EINVAL; - goto bad_swap; - } - memset(p->swap_lockmap+PAGE_SIZE-10,0,10); - j = 0; - p->lowest_bit = 0; - p->highest_bit = 0; - for (i = 1 ; i < 8*PAGE_SIZE ; i++) { - if (test_bit(i,p->swap_lockmap)) { - if (!p->lowest_bit) - p->lowest_bit = i; - p->highest_bit = i; - p->max = i+1; - j++; - } - } - if (!j) { - printk("Empty swap-file\n"); - error = -EINVAL; - goto bad_swap; - } - p->swap_map = (unsigned char *) vmalloc(p->max); - if (!p->swap_map) { - error = -ENOMEM; - goto bad_swap; - } - for (i = 1 ; i < p->max ; i++) { - if (test_bit(i,p->swap_lockmap)) - p->swap_map[i] = 0; - else - p->swap_map[i] = 0x80; - } - p->swap_map[0] = 0x80; - memset(p->swap_lockmap,0,PAGE_SIZE); - p->flags = SWP_WRITEOK; - p->pages = j; - nr_swap_pages += j; - printk("Adding Swap: %dk swap-space\n",j<<2); - return 0; -bad_swap: - free_page((long) p->swap_lockmap); - vfree(p->swap_map); - iput(p->swap_file); - p->swap_device = 0; - p->swap_file = NULL; - p->swap_map = NULL; - p->swap_lockmap = NULL; - p->flags = 0; - return error; -} - -void si_swapinfo(struct sysinfo *val) -{ - unsigned int i, j; - - val->freeswap = val->totalswap = 0; - for (i = 0; i < nr_swapfiles; i++) { - if ((swap_info[i].flags & SWP_WRITEOK) != SWP_WRITEOK) - continue; - for (j = 0; j < swap_info[i].max; ++j) - switch (swap_info[i].swap_map[j]) { - case 128: - continue; - case 0: - ++val->freeswap; - default: - ++val->totalswap; - } - } - val->freeswap <<= PAGE_SHIFT; - val->totalswap <<= PAGE_SHIFT; - return; -} - -/* - * set up the free-area data structures: - * - mark all pages MAP_PAGE_RESERVED - * - mark all memory queues empty - * - clear the memory bitmaps - */ -unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem) -{ - unsigned short * p; - unsigned long mask = PAGE_MASK; - int i; - - /* - * select nr of pages we try to keep free for important stuff - * with a minimum of 16 pages. This is totally arbitrary - */ - i = end_mem >> (PAGE_SHIFT+6); - if (i < 16) - i = 16; - min_free_pages = i; - start_mem = init_swap_cache(start_mem, end_mem); - mem_map = (unsigned short *) start_mem; - p = mem_map + MAP_NR(end_mem); - start_mem = (unsigned long) p; - while (p > mem_map) - *--p = MAP_PAGE_RESERVED; - - for (i = 0 ; i < NR_MEM_LISTS ; i++, mask <<= 1) { - unsigned long bitmap_size; - free_area_list[i].prev = free_area_list[i].next = &free_area_list[i]; - end_mem = (end_mem + ~mask) & mask; - bitmap_size = end_mem >> (PAGE_SHIFT + i); - bitmap_size = (bitmap_size + 7) >> 3; - free_area_map[i] = (unsigned char *) start_mem; - memset((void *) start_mem, 0, bitmap_size); - start_mem += bitmap_size; - } - return start_mem; -} diff --git a/arch/i386/mm/vmalloc.c b/arch/i386/mm/vmalloc.c deleted file mode 100644 index 0dbd16d54..000000000 --- a/arch/i386/mm/vmalloc.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * linux/mm/vmalloc.c - * - * Copyright (C) 1993 Linus Torvalds - */ - -#include <asm/system.h> -#include <linux/config.h> - -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/head.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/malloc.h> -#include <asm/segment.h> - -struct vm_struct { - unsigned long flags; - void * addr; - unsigned long size; - struct vm_struct * next; -}; - -static struct vm_struct * vmlist = NULL; - -/* Just any arbitrary offset to the start of the vmalloc VM area: the - * current 8MB value just means that there will be a 8MB "hole" after the - * physical memory until the kernel virtual memory starts. That means that - * any out-of-bounds memory accesses will hopefully be caught. - * The vmalloc() routines leaves a hole of 4kB between each vmalloced - * area for the same reason. ;) - */ -#define VMALLOC_OFFSET (8*1024*1024) - -static inline void set_pgdir(unsigned long dindex, unsigned long value) -{ - struct task_struct * p; - - p = &init_task; - do { - ((unsigned long *) p->tss.cr3)[dindex] = value; - p = p->next_task; - } while (p != &init_task); -} - -static int free_area_pages(unsigned long dindex, unsigned long index, unsigned long nr) -{ - unsigned long page, *pte; - - if (!(PAGE_PRESENT & (page = swapper_pg_dir[dindex]))) - return 0; - page &= PAGE_MASK; - pte = index + (unsigned long *) page; - do { - unsigned long pg = *pte; - *pte = 0; - if (pg & PAGE_PRESENT) - free_page(pg); - pte++; - } while (--nr); - pte = (unsigned long *) page; - for (nr = 0 ; nr < 1024 ; nr++, pte++) - if (*pte) - return 0; - set_pgdir(dindex,0); - mem_map[MAP_NR(page)] = 1; - free_page(page); - invalidate(); - return 0; -} - -static int alloc_area_pages(unsigned long dindex, unsigned long index, unsigned long nr) -{ - unsigned long page, *pte; - - page = swapper_pg_dir[dindex]; - if (!page) { - page = get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - if (swapper_pg_dir[dindex]) { - free_page(page); - page = swapper_pg_dir[dindex]; - } else { - mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED; - set_pgdir(dindex, page | PAGE_SHARED); - } - } - page &= PAGE_MASK; - pte = index + (unsigned long *) page; - *pte = PAGE_SHARED; /* remove a race with vfree() */ - do { - unsigned long pg = get_free_page(GFP_KERNEL); - - if (!pg) - return -ENOMEM; - *pte = pg | PAGE_SHARED; - pte++; - } while (--nr); - invalidate(); - return 0; -} - -static int do_area(void * addr, unsigned long size, - int (*area_fn)(unsigned long,unsigned long,unsigned long)) -{ - unsigned long nr, dindex, index; - - nr = size >> PAGE_SHIFT; - dindex = (TASK_SIZE + (unsigned long) addr) >> 22; - index = (((unsigned long) addr) >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - while (nr > 0) { - unsigned long i = PTRS_PER_PAGE - index; - - if (i > nr) - i = nr; - nr -= i; - if (area_fn(dindex, index, i)) - return -1; - index = 0; - dindex++; - } - return 0; -} - -void vfree(void * addr) -{ - struct vm_struct **p, *tmp; - - if (!addr) - return; - if ((PAGE_SIZE-1) & (unsigned long) addr) { - printk("Trying to vfree() bad address (%p)\n", addr); - return; - } - for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) { - if (tmp->addr == addr) { - *p = tmp->next; - do_area(tmp->addr, tmp->size, free_area_pages); - kfree(tmp); - return; - } - } - printk("Trying to vfree() nonexistent vm area (%p)\n", addr); -} - -void * vmalloc(unsigned long size) -{ - void * addr; - struct vm_struct **p, *tmp, *area; - - size = PAGE_ALIGN(size); - if (!size || size > high_memory) - return NULL; - area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); - if (!area) - return NULL; - addr = (void *) ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); - area->size = size + PAGE_SIZE; - area->next = NULL; - for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { - if (size + (unsigned long) addr < (unsigned long) tmp->addr) - break; - addr = (void *) (tmp->size + (unsigned long) tmp->addr); - } - area->addr = addr; - area->next = *p; - *p = area; - if (do_area(addr, size, alloc_area_pages)) { - vfree(addr); - return NULL; - } - return addr; -} - -int vread(char *buf, char *addr, int count) -{ - struct vm_struct **p, *tmp; - char *vaddr, *buf_start = buf; - int n; - - for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { - vaddr = (char *) tmp->addr; - while (addr < vaddr) { - if (count == 0) - goto finished; - put_fs_byte('\0', buf++), addr++, count--; - } - n = tmp->size - PAGE_SIZE; - if (addr > vaddr) - n -= addr - vaddr; - while (--n >= 0) { - if (count == 0) - goto finished; - put_fs_byte(*addr++, buf++), count--; - } - } -finished: - return buf - buf_start; -} diff --git a/arch/i386/sched.c b/arch/i386/sched.c deleted file mode 100644 index 6eed6e8f5..000000000 --- a/arch/i386/sched.c +++ /dev/null @@ -1,861 +0,0 @@ -/* - * linux/kernel/sched.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * 'sched.c' is the main kernel file. It contains scheduling primitives - * (sleep_on, wakeup, schedule etc) as well as a number of simple system - * call functions (type getpid(), which just extracts a field from - * current-task - */ - -#include <linux/config.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/kernel.h> -#include <linux/kernel_stat.h> -#include <linux/fdreg.h> -#include <linux/errno.h> -#include <linux/time.h> -#include <linux/ptrace.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/tqueue.h> -#include <linux/resource.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/segment.h> - -#define TIMER_IRQ 0 - -#include <linux/timex.h> - -/* - * kernel variables - */ -long tick = 1000000 / HZ; /* timer interrupt period */ -volatile struct timeval xtime; /* The current time */ -int tickadj = 500/HZ; /* microsecs */ - -DECLARE_TASK_QUEUE(tq_timer); -DECLARE_TASK_QUEUE(tq_immediate); - -/* - * phase-lock loop variables - */ -int time_status = TIME_BAD; /* clock synchronization status */ -long time_offset = 0; /* time adjustment (us) */ -long time_constant = 0; /* pll time constant */ -long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */ -long time_precision = 1; /* clock precision (us) */ -long time_maxerror = 0x70000000;/* maximum error */ -long time_esterror = 0x70000000;/* estimated error */ -long time_phase = 0; /* phase offset (scaled us) */ -long time_freq = 0; /* frequency offset (scaled ppm) */ -long time_adj = 0; /* tick adjust (scaled 1 / HZ) */ -long time_reftime = 0; /* time at last adjustment (s) */ - -long time_adjust = 0; -long time_adjust_step = 0; - -int need_resched = 0; -unsigned long event = 0; - -/* - * Tell us the machine setup.. - */ -int hard_math = 0; /* set by boot/head.S */ -int x86 = 0; /* set by boot/head.S to 3 or 4 */ -int ignore_irq13 = 0; /* set if exception 16 works */ -int wp_works_ok = 0; /* set if paging hardware honours WP */ -int hlt_works_ok = 1; /* set if the "hlt" instruction works */ - -/* - * Bus types .. - */ -int EISA_bus = 0; - -extern int _setitimer(int, struct itimerval *, struct itimerval *); -unsigned long * prof_buffer = NULL; -unsigned long prof_len = 0; - -#define _S(nr) (1<<((nr)-1)) - -extern void mem_use(void); - -extern int timer_interrupt(void); -asmlinkage int system_call(void); - -static unsigned long init_kernel_stack[1024] = { STACK_MAGIC, }; -static struct vm_area_struct init_mmap = INIT_MMAP; -struct task_struct init_task = INIT_TASK; - -unsigned long volatile jiffies=0; - -struct task_struct *current = &init_task; -struct task_struct *last_task_used_math = NULL; - -struct task_struct * task[NR_TASKS] = {&init_task, }; - -long user_stack [ PAGE_SIZE>>2 ] = { STACK_MAGIC, }; - -struct { - long * a; - short b; - } stack_start = { & user_stack [PAGE_SIZE>>2] , KERNEL_DS }; - -struct kernel_stat kstat = { 0 }; - -/* - * 'math_state_restore()' saves the current math information in the - * old math state array, and gets the new ones from the current task - * - * Careful.. There are problems with IBM-designed IRQ13 behaviour. - * Don't touch unless you *really* know how it works. - */ -asmlinkage void math_state_restore(void) -{ - __asm__ __volatile__("clts"); - 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) { - __asm__("frstor %0": :"m" (current->tss.i387)); - } else { - __asm__("fninit"); - current->used_math=1; - } - timer_active &= ~(1<<COPRO_TIMER); -} - -#ifndef CONFIG_MATH_EMULATION - -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); - schedule(); -} - -#endif /* CONFIG_MATH_EMULATION */ - -unsigned long itimer_ticks = 0; -unsigned long itimer_next = ~0; - -/* - * 'schedule()' is the scheduler function. It's a very simple and nice - * scheduler: it's not perfect, but certainly works for most things. - * The one thing you might take a look at is the signal-handler code here. - * - * NOTE!! Task 0 is the 'idle' task, which gets called when no other - * tasks can run. It can not be killed, and it cannot sleep. The 'state' - * information in task[0] is never used. - * - * The "confuse_gcc" goto is used only to get better assembly code.. - * Dijkstra probably hates me. - */ -asmlinkage void schedule(void) -{ - int c; - struct task_struct * p; - struct task_struct * next; - unsigned long ticks; - -/* check alarm, wake up any interruptible tasks that have got a signal */ - - if (intr_count) { - printk("Aiee: scheduling in interrupt\n"); - intr_count = 0; - } - cli(); - ticks = itimer_ticks; - itimer_ticks = 0; - itimer_next = ~0; - sti(); - need_resched = 0; - p = &init_task; - for (;;) { - if ((p = p->next_task) == &init_task) - goto confuse_gcc1; - if (ticks && p->it_real_value) { - if (p->it_real_value <= ticks) { - send_sig(SIGALRM, p, 1); - if (!p->it_real_incr) { - p->it_real_value = 0; - goto end_itimer; - } - do { - p->it_real_value += p->it_real_incr; - } while (p->it_real_value <= ticks); - } - p->it_real_value -= ticks; - if (p->it_real_value < itimer_next) - itimer_next = p->it_real_value; - } -end_itimer: - if (p->state != TASK_INTERRUPTIBLE) - continue; - if (p->signal & ~p->blocked) { - p->state = TASK_RUNNING; - continue; - } - if (p->timeout && p->timeout <= jiffies) { - p->timeout = 0; - p->state = TASK_RUNNING; - } - } -confuse_gcc1: - -/* this is the scheduler proper: */ -#if 0 - /* give processes that go to sleep a bit higher priority.. */ - /* This depends on the values for TASK_XXX */ - /* This gives smoother scheduling for some things, but */ - /* can be very unfair under some circumstances, so.. */ - if (TASK_UNINTERRUPTIBLE >= (unsigned) current->state && - current->counter < current->priority*2) { - ++current->counter; - } -#endif - c = -1000; - next = p = &init_task; - for (;;) { - if ((p = p->next_task) == &init_task) - goto confuse_gcc2; - if (p->state == TASK_RUNNING && p->counter > c) - c = p->counter, next = p; - } -confuse_gcc2: - if (!c) { - for_each_task(p) - p->counter = (p->counter >> 1) + p->priority; - } - if (current == next) - return; - kstat.context_swtch++; - switch_to(next); - /* Now maybe reload the debug registers */ - if(current->debugreg[7]){ - loaddebug(0); - loaddebug(1); - loaddebug(2); - loaddebug(3); - loaddebug(6); - }; -} - -asmlinkage int sys_pause(void) -{ - current->state = TASK_INTERRUPTIBLE; - schedule(); - return -ERESTARTNOHAND; -} - -/* - * wake_up doesn't wake up stopped processes - they have to be awakened - * with signals or similar. - * - * Note that this doesn't need cli-sti pairs: interrupts may not change - * the wait-queue structures directly, but only call wake_up() to wake - * a process. The process itself must remove the queue once it has woken. - */ -void wake_up(struct wait_queue **q) -{ - struct wait_queue *tmp; - struct task_struct * p; - - if (!q || !(tmp = *q)) - return; - do { - if ((p = tmp->task) != NULL) { - if ((p->state == TASK_UNINTERRUPTIBLE) || - (p->state == TASK_INTERRUPTIBLE)) { - p->state = TASK_RUNNING; - if (p->counter > current->counter + 3) - need_resched = 1; - } - } - if (!tmp->next) { - printk("wait_queue is bad (eip = %p)\n", - __builtin_return_address(0)); - printk(" q = %p\n",q); - printk(" *q = %p\n",*q); - printk(" tmp = %p\n",tmp); - break; - } - tmp = tmp->next; - } while (tmp != *q); -} - -void wake_up_interruptible(struct wait_queue **q) -{ - struct wait_queue *tmp; - struct task_struct * p; - - if (!q || !(tmp = *q)) - return; - do { - if ((p = tmp->task) != NULL) { - if (p->state == TASK_INTERRUPTIBLE) { - p->state = TASK_RUNNING; - if (p->counter > current->counter + 3) - need_resched = 1; - } - } - if (!tmp->next) { - printk("wait_queue is bad (eip = %p)\n", - __builtin_return_address(0)); - printk(" q = %p\n",q); - printk(" *q = %p\n",*q); - printk(" tmp = %p\n",tmp); - break; - } - tmp = tmp->next; - } while (tmp != *q); -} - -void __down(struct semaphore * sem) -{ - struct wait_queue wait = { current, NULL }; - add_wait_queue(&sem->wait, &wait); - current->state = TASK_UNINTERRUPTIBLE; - while (sem->count <= 0) { - schedule(); - current->state = TASK_UNINTERRUPTIBLE; - } - current->state = TASK_RUNNING; - remove_wait_queue(&sem->wait, &wait); -} - -static inline void __sleep_on(struct wait_queue **p, int state) -{ - unsigned long flags; - struct wait_queue wait = { current, NULL }; - - if (!p) - return; - if (current == task[0]) - panic("task[0] trying to sleep"); - current->state = state; - add_wait_queue(p, &wait); - save_flags(flags); - sti(); - schedule(); - remove_wait_queue(p, &wait); - restore_flags(flags); -} - -void interruptible_sleep_on(struct wait_queue **p) -{ - __sleep_on(p,TASK_INTERRUPTIBLE); -} - -void sleep_on(struct wait_queue **p) -{ - __sleep_on(p,TASK_UNINTERRUPTIBLE); -} - -/* - * The head for the timer-list has a "expires" field of MAX_UINT, - * and the sorting routine counts on this.. - */ -static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL }; -#define SLOW_BUT_DEBUGGING_TIMERS 1 - -void add_timer(struct timer_list * timer) -{ - unsigned long flags; - struct timer_list *p; - -#if SLOW_BUT_DEBUGGING_TIMERS - if (timer->next || timer->prev) { - printk("add_timer() called with non-zero list from %p\n", - __builtin_return_address(0)); - return; - } -#endif - p = &timer_head; - timer->expires += jiffies; - save_flags(flags); - cli(); - do { - p = p->next; - } while (timer->expires > p->expires); - timer->next = p; - timer->prev = p->prev; - p->prev = timer; - timer->prev->next = timer; - restore_flags(flags); -} - -int del_timer(struct timer_list * timer) -{ - unsigned long flags; -#if SLOW_BUT_DEBUGGING_TIMERS - struct timer_list * p; - - p = &timer_head; - save_flags(flags); - cli(); - while ((p = p->next) != &timer_head) { - if (p == timer) { - timer->next->prev = timer->prev; - timer->prev->next = timer->next; - timer->next = timer->prev = NULL; - restore_flags(flags); - timer->expires -= jiffies; - return 1; - } - } - if (timer->next || timer->prev) - printk("del_timer() called from %p with timer not initialized\n", - __builtin_return_address(0)); - restore_flags(flags); - return 0; -#else - save_flags(flags); - cli(); - if (timer->next) { - timer->next->prev = timer->prev; - timer->prev->next = timer->next; - timer->next = timer->prev = NULL; - restore_flags(flags); - timer->expires -= jiffies; - return 1; - } - restore_flags(flags); - return 0; -#endif -} - -unsigned long timer_active = 0; -struct timer_struct timer_table[32]; - -/* - * Hmm.. Changed this, as the GNU make sources (load.c) seems to - * imply that avenrun[] is the standard name for this kind of thing. - * Nothing else seems to be standardized: the fractional size etc - * all seem to differ on different machines. - */ -unsigned long avenrun[3] = { 0,0,0 }; - -/* - * Nr of active tasks - counted in fixed-point numbers - */ -static unsigned long count_active_tasks(void) -{ - struct task_struct **p; - unsigned long nr = 0; - - for(p = &LAST_TASK; p > &FIRST_TASK; --p) - if (*p && ((*p)->state == TASK_RUNNING || - (*p)->state == TASK_UNINTERRUPTIBLE || - (*p)->state == TASK_SWAPPING)) - nr += FIXED_1; - return nr; -} - -static inline void calc_load(void) -{ - unsigned long active_tasks; /* fixed-point */ - static int count = LOAD_FREQ; - - if (count-- > 0) - return; - count = LOAD_FREQ; - active_tasks = count_active_tasks(); - CALC_LOAD(avenrun[0], EXP_1, active_tasks); - CALC_LOAD(avenrun[1], EXP_5, active_tasks); - CALC_LOAD(avenrun[2], EXP_15, active_tasks); -} - -/* - * this routine handles the overflow of the microsecond field - * - * The tricky bits of code to handle the accurate clock support - * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame. - * They were originally developed for SUN and DEC kernels. - * All the kudos should go to Dave for this stuff. - * - * These were ported to Linux by Philip Gladstone. - */ -static void second_overflow(void) -{ - long ltemp; - /* last time the cmos clock got updated */ - static long last_rtc_update=0; - extern int set_rtc_mmss(unsigned long); - - /* Bump the maxerror field */ - time_maxerror = (0x70000000-time_maxerror < time_tolerance) ? - 0x70000000 : (time_maxerror + time_tolerance); - - /* Run the PLL */ - if (time_offset < 0) { - ltemp = (-(time_offset+1) >> (SHIFT_KG + time_constant)) + 1; - time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); - time_offset += (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE); - time_adj = - time_adj; - } else if (time_offset > 0) { - ltemp = ((time_offset-1) >> (SHIFT_KG + time_constant)) + 1; - time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); - time_offset -= (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE); - } else { - time_adj = 0; - } - - time_adj += (time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE)) - + FINETUNE; - - /* Handle the leap second stuff */ - switch (time_status) { - case TIME_INS: - /* ugly divide should be replaced */ - if (xtime.tv_sec % 86400 == 0) { - xtime.tv_sec--; /* !! */ - time_status = TIME_OOP; - printk("Clock: inserting leap second 23:59:60 GMT\n"); - } - break; - - case TIME_DEL: - /* ugly divide should be replaced */ - if (xtime.tv_sec % 86400 == 86399) { - xtime.tv_sec++; - time_status = TIME_OK; - printk("Clock: deleting leap second 23:59:59 GMT\n"); - } - break; - - case TIME_OOP: - time_status = TIME_OK; - break; - } - if (xtime.tv_sec > last_rtc_update + 660) - 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 one min */ -} - -/* - * disregard lost ticks for now.. We don't care enough. - */ -static void timer_bh(void * unused) -{ - unsigned long mask; - struct timer_struct *tp; - struct timer_list * timer; - - cli(); - while ((timer = timer_head.next) != &timer_head && timer->expires < jiffies) { - void (*fn)(unsigned long) = timer->function; - unsigned long data = timer->data; - timer->next->prev = timer->prev; - timer->prev->next = timer->next; - timer->next = timer->prev = NULL; - sti(); - fn(data); - cli(); - } - sti(); - - for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) { - if (mask > timer_active) - break; - if (!(mask & timer_active)) - continue; - if (tp->expires > jiffies) - continue; - timer_active &= ~mask; - tp->fn(); - sti(); - } -} - -void tqueue_bh(void * unused) -{ - run_task_queue(&tq_timer); -} - -void immediate_bh(void * unused) -{ - run_task_queue(&tq_immediate); -} - -/* - * The int argument is really a (struct pt_regs *), in case the - * interrupt wants to know from where it was called. The timer - * irq uses this to decide if it should update the user or system - * times. - */ -static void do_timer(struct pt_regs * regs) -{ - unsigned long mask; - struct timer_struct *tp; - - long ltemp, psecs; - - /* Advance the phase, once it gets to one microsecond, then - * advance the tick more. - */ - time_phase += time_adj; - if (time_phase < -FINEUSEC) { - ltemp = -time_phase >> SHIFT_SCALE; - time_phase += ltemp << SHIFT_SCALE; - xtime.tv_usec += tick + time_adjust_step - ltemp; - } - else if (time_phase > FINEUSEC) { - ltemp = time_phase >> SHIFT_SCALE; - time_phase -= ltemp << SHIFT_SCALE; - xtime.tv_usec += tick + time_adjust_step + ltemp; - } else - xtime.tv_usec += tick + time_adjust_step; - - if (time_adjust) - { - /* We are doing an adjtime thing. - * - * Modify the value of the tick for next time. - * Note that a positive delta means we want the clock - * to run fast. This means that the tick should be bigger - * - * Limit the amount of the step for *next* tick to be - * in the range -tickadj .. +tickadj - */ - if (time_adjust > tickadj) - time_adjust_step = tickadj; - else if (time_adjust < -tickadj) - time_adjust_step = -tickadj; - else - time_adjust_step = time_adjust; - - /* Reduce by this step the amount of time left */ - time_adjust -= time_adjust_step; - } - else - time_adjust_step = 0; - - if (xtime.tv_usec >= 1000000) { - xtime.tv_usec -= 1000000; - xtime.tv_sec++; - second_overflow(); - } - - jiffies++; - calc_load(); - if ((VM_MASK & regs->eflags) || (3 & regs->cs)) { - current->utime++; - if (current != task[0]) { - if (current->priority < 15) - kstat.cpu_nice++; - else - kstat.cpu_user++; - } - /* Update ITIMER_VIRT for current task if not in a system call */ - if (current->it_virt_value && !(--current->it_virt_value)) { - current->it_virt_value = current->it_virt_incr; - send_sig(SIGVTALRM,current,1); - } - } else { - current->stime++; - if(current != task[0]) - kstat.cpu_system++; -#ifdef CONFIG_PROFILE - if (prof_buffer && current != task[0]) { - unsigned long eip = regs->eip; - eip >>= 2; - if (eip < prof_len) - prof_buffer[eip]++; - } -#endif - } - /* - * check the cpu time limit on the process. - */ - if ((current->rlim[RLIMIT_CPU].rlim_max != RLIM_INFINITY) && - (((current->stime + current->utime) / HZ) >= current->rlim[RLIMIT_CPU].rlim_max)) - send_sig(SIGKILL, current, 1); - if ((current->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) && - (((current->stime + current->utime) % HZ) == 0)) { - psecs = (current->stime + current->utime) / HZ; - /* send when equal */ - if (psecs == current->rlim[RLIMIT_CPU].rlim_cur) - send_sig(SIGXCPU, current, 1); - /* and every five seconds thereafter. */ - else if ((psecs > current->rlim[RLIMIT_CPU].rlim_cur) && - ((psecs - current->rlim[RLIMIT_CPU].rlim_cur) % 5) == 0) - send_sig(SIGXCPU, current, 1); - } - - if (current != task[0] && 0 > --current->counter) { - current->counter = 0; - need_resched = 1; - } - /* Update ITIMER_PROF for the current task */ - if (current->it_prof_value && !(--current->it_prof_value)) { - current->it_prof_value = current->it_prof_incr; - send_sig(SIGPROF,current,1); - } - for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) { - if (mask > timer_active) - break; - if (!(mask & timer_active)) - continue; - if (tp->expires > jiffies) - continue; - mark_bh(TIMER_BH); - } - cli(); - itimer_ticks++; - if (itimer_ticks > itimer_next) - need_resched = 1; - if (timer_head.next->expires < jiffies) - mark_bh(TIMER_BH); - if (tq_timer != &tq_last) - mark_bh(TQUEUE_BH); - sti(); -} - -asmlinkage int sys_alarm(long seconds) -{ - struct itimerval it_new, it_old; - - it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; - it_new.it_value.tv_sec = seconds; - it_new.it_value.tv_usec = 0; - _setitimer(ITIMER_REAL, &it_new, &it_old); - return(it_old.it_value.tv_sec + (it_old.it_value.tv_usec / 1000000)); -} - -asmlinkage int sys_getpid(void) -{ - return current->pid; -} - -asmlinkage int sys_getppid(void) -{ - return current->p_opptr->pid; -} - -asmlinkage int sys_getuid(void) -{ - return current->uid; -} - -asmlinkage int sys_geteuid(void) -{ - return current->euid; -} - -asmlinkage int sys_getgid(void) -{ - return current->gid; -} - -asmlinkage int sys_getegid(void) -{ - return current->egid; -} - -asmlinkage int sys_nice(long increment) -{ - int newprio; - - if (increment < 0 && !suser()) - return -EPERM; - newprio = current->priority - increment; - if (newprio < 1) - newprio = 1; - if (newprio > 35) - newprio = 35; - current->priority = newprio; - return 0; -} - -static void show_task(int nr,struct task_struct * p) -{ - unsigned long free; - static char * stat_nam[] = { "R", "S", "D", "Z", "T", "W" }; - - printk("%-8s %3d ", p->comm, (p == current) ? -nr : nr); - if (((unsigned) p->state) < sizeof(stat_nam)/sizeof(char *)) - printk(stat_nam[p->state]); - else - printk(" "); - if (p == current) - printk(" current "); - else - printk(" %08lX ", ((unsigned long *)p->tss.esp)[3]); - for (free = 1; free < 1024 ; free++) { - if (((unsigned long *)p->kernel_stack_page)[free]) - break; - } - printk("%5lu %5d %6d ", free << 2, p->pid, p->p_pptr->pid); - if (p->p_cptr) - printk("%5d ", p->p_cptr->pid); - else - printk(" "); - if (p->p_ysptr) - printk("%7d", p->p_ysptr->pid); - else - printk(" "); - if (p->p_osptr) - printk(" %5d\n", p->p_osptr->pid); - else - printk("\n"); -} - -void show_state(void) -{ - int i; - - printk(" free sibling\n"); - printk(" task PC stack pid father child younger older\n"); - for (i=0 ; i<NR_TASKS ; i++) - if (task[i]) - show_task(i,task[i]); -} - -void sched_init(void) -{ - int i; - struct desc_struct * p; - - bh_base[TIMER_BH].routine = timer_bh; - bh_base[TQUEUE_BH].routine = tqueue_bh; - bh_base[IMMEDIATE_BH].routine = immediate_bh; - if (sizeof(struct sigaction) != 16) - panic("Struct sigaction MUST be 16 bytes"); - set_tss_desc(gdt+FIRST_TSS_ENTRY,&init_task.tss); - set_ldt_desc(gdt+FIRST_LDT_ENTRY,&default_ldt,1); - set_system_gate(0x80,&system_call); - p = gdt+2+FIRST_TSS_ENTRY; - for(i=1 ; i<NR_TASKS ; i++) { - task[i] = NULL; - p->a=p->b=0; - p++; - p->a=p->b=0; - p++; - } -/* Clear NT, so that we won't have troubles with that later on */ - __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); - load_TR(0); - load_ldt(0); - outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ - outb_p(LATCH & 0xff , 0x40); /* LSB */ - outb(LATCH >> 8 , 0x40); /* MSB */ - if (request_irq(TIMER_IRQ,(void (*)(int)) do_timer, 0, "timer") != 0) - panic("Could not allocate timer IRQ!"); -} diff --git a/arch/mips/.gdbinit b/arch/mips/.gdbinit new file mode 100644 index 000000000..8bdcdae68 --- /dev/null +++ b/arch/mips/.gdbinit @@ -0,0 +1,7 @@ +echo Setting up the environment for debugging vmlinux...\n +echo set remotedebug 0 \n +set remotedebug 0 +echo cd arch/mips/kernel \n +cd arch/mips/kernel +echo target remote /dev/ttyS0 \n +target remote /dev/ttyS0 diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 0dc133749..baec00d70 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -1,71 +1,101 @@ # -# Makefile.mips +# arch/mips/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture # # This file is subject to the terms and conditions of the GNU General Public # License. See the file "COPYING" in the main directory of this archive # for more details. # -# Copyright (C) 1994 by Waldorf GMBH, +# Copyright (C) 1994, 1995 by Waldorf Electronics, # written by Ralf Baechle # -AS = mips-linux-as -ASFLAGS = -mips3 -mcpu=r4000 -LD = mips-linux-ld -HOSTCC = gcc -CC = mips-linux-gcc -V 2.5.8 -Wa,-mips3 -mcpu=r4000 -D__KERNEL__ -I$(TOPDIR)/include -#CC = mips-linux-gcc -V 2.6.2 -Wa,-mips3 -mcpu=r4600 -D__KERNEL__ -I$(TOPDIR)/include -MAKE = make -CPP = $(CC) -E -AR = mips-linux-ar -RANLIB = mips-linux-ranlib -STRIP = strip -KERNELHDRS = /home/ralf/src/linux +ifdef CONFIG_ELF_COMPILER +ifdef CONFIG_CPU_LITTLE_ENDIAN +prefix = mipsel-linuxelf- +else +prefix = mips-linuxelf- +endif +else +ifdef CONFIG_CPU_LITTLE_ENDIAN +prefix = mipsel-linux- +else +prefix = mips-linux- +endif +endif + +AS = $(prefix)as +LD = $(prefix)ld +LINKFLAGS = -N -Ttext 0x80000000 +#HOSTCC = gcc +CC = $(prefix)gcc -D__KERNEL__ -I$(TOPDIR)/include +CPP = $(CC) -E $(CFLAGS) +AR = $(prefix)ar +RANLIB = $(prefix)ranlib +STRIP = $(prefix)strip +NM = $(prefix)nm + +# +# The new ELF GCC uses -G0 -mabicalls -fpic as default. We don't need PIC +# code in the kernel since it only slows down the whole thing. For the +# old GCC these options are just the defaults. At some point we might +# make use of global pointer optimaztions. +# +ifdef CONFIG_OBJECT_ELF +CFLAGS := $(CFLAGS) -G0 -mno-abicalls -fno-pic #-pipe +endif + +ifdef CONFIG_REMOTE_DEBUG +CFLAGS := $(CFLAGS) -g +endif + +ifdef CONFIG_CPU_R3000 +CFLAGS := $(CFLAGS) -mcpu=r3000 -mips1 +ASFLAGS := $(ASFLAGS) -mcpu=r3000 -mips1 +endif +ifdef CONFIG_CPU_R6000 +CFLAGS := $(CFLAGS) -mcpu=r6000 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r6000 -mips2 +endif +ifdef CONFIG_CPU_R4X00 +CFLAGS := $(CFLAGS) -D__R4000__ -mcpu=r4400 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r4400 -mips2 +endif +ifdef CONFIG_CPU_R4600 +CFLAGS := $(CFLAGS) -D__R4000__ -mcpu=r4600 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r4600 -mips2 +endif +ifdef CONFIG_CPU_R8000 +CFLAGS := $(CFLAGS) -D__R4000__ -mcpu=r8000 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r8000 -mips2 +endif +ifdef CONFIG_CPU_R10000 +CFLAGS := $(CFLAGS) -D__R4000__ -mcpu=r8000 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r8000 -mips2 +endif + +HEAD := arch/mips/kernel/head.o -zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem - $(MAKE) -C zBoot +SUBDIRS := $(SUBDIRS) arch/mips/kernel arch/mips/mm arch/mips/lib +ARCHIVES := arch/mips/kernel/kernel.o arch/mips/mm/mm.o $(ARCHIVES) +LIBS := arch/mips/lib/lib.a $(LIBS) arch/mips/lib/lib.a -zImage: $(CONFIGURE) tools/zSystem - cp tools/System zImage - sync +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot -#zImage: $(CONFIGURE) zBoot/zSystem tools/build -# tools/build zBoot/zSystem $(ROOT_DEV) > zImage -# sync +zImage: vmlinux + @$(MAKEBOOT) zImage -zdisk: zImage - mcopy -n zImage a:vmlinux +compressed: zImage -tools/zSystem: boot/head.o init/main.o init/init.o tools/version.o linuxsubdirs - $(LD) $(LOWLDFLAGS) boot/head.o init/main.o init/init.o \ - tools/version.o \ - $(ARCHIVES) \ - $(FILESYSTEMS) \ - $(DRIVERS) \ - $(LIBS) \ - -N -Ttext 0x80000000 \ - -o tools/System - nm tools/System | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ - sort > System.map +zdisk: vmlinux + @$(MAKEBOOT) zdisk -#tools/system: boot/head.o init/main.o init/init.o tools/version.o linuxsubdirs -# $(LD) $(LOWLDFLAGS) boot/head.o init/main.o tools/version.o \ -# $(ARCHIVES) \ -# $(FILESYSTEMS) \ -# $(DRIVERS) \ -# $(LIBS) \ -# -N -Ttext 0x80000000 \ -# -o tools/system -# nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ -# sort > System.map +archclean: + @$(MAKEBOOT) clean -#tools/zSystem: boot/head.o init/main.o tools/version.o linuxsubdirs -# $(LD) $(HIGHLDFLAGS) boot/head.o init/main.o tools/version.o \ -# $(ARCHIVES) \ -# $(FILESYSTEMS) \ -# $(DRIVERS) \ -# $(LIBS) \ -# -N -Ttext 0x80600000 \ -# -o tools/zSystem -# nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ -# sort > zSystem.map +archdep: + @$(MAKEBOOT) dep diff --git a/arch/mips/TODO b/arch/mips/TODO new file mode 100644 index 000000000..29afd71f7 --- /dev/null +++ b/arch/mips/TODO @@ -0,0 +1,10 @@ + - Check the definitions for F_EXLCK and F_SHLCK in include/asm-mips/fcntl.h + for correctness and compatibility with MIPS ABI. + - Check the definitions for O_NDELAY in include/asm-mips/fcntl.h for + correctness and compatibility with MIPS ABI. + - What are the fields l_sysid and pad in struct flock supposed to contain? + Do we need to handle them in the kernel? + - Check the resource limits defines in asm-mips/resource.h + - Recheck struct stat in asm-mips/stat.h + - Use timestruc_t in struct stat in asm/stat.h instead of time_t + - cacheflush should check for illegal flags diff --git a/arch/mips/bios32.c b/arch/mips/bios32.c deleted file mode 100644 index bb011a852..000000000 --- a/arch/mips/bios32.c +++ /dev/null @@ -1,8 +0,0 @@ -/* - * bios32.c - BIOS32, PCI BIOS functions. - * - * Copyright (C) 1994 by Waldorf GMBH, - * written by Ralf Baechle - * - * Just nothing for a MIPS board... - */ diff --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile new file mode 100644 index 000000000..93784137d --- /dev/null +++ b/arch/mips/boot/Makefile @@ -0,0 +1,47 @@ +# +# arch/mips/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Ralf Baechle +# + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) $(ASFLAGS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +OBJS = milo.o a.out.o + +# +# Fake compressed boot +# +zImage: $(CONFIGURE) $(TOPDIR)/vmlinux + cp $(TOPDIR)/vmlinux $@ + $(STRIP) --discard-all $@ + +zdisk: zImage + mcopy -n zImage a:vmlinux + +dep: + $(CPP) -M *.[cS] > .depend + +clean: + rm -f zImage + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/arch/mips/boot/a.out.c b/arch/mips/boot/a.out.c new file mode 100644 index 000000000..15515b219 --- /dev/null +++ b/arch/mips/boot/a.out.c @@ -0,0 +1,254 @@ +/* + * arch/mips/boot/milo.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1994, 1995 by Ralf Baechle + * Copyright (C) 1993 by Hamish Macdonald + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> + +#include "loader.h" + +static int kfd; +static struct exec kexec; +static off_t filesize; +static struct nlist *syms; +static char *strs; +static long strsize, numsyms; + +/* + * Read a symbol from the kernel executable + */ +static struct nlist *aout_get_nlist(char *symbol) +{ + struct nlist *nl, *p = NULL; + + for (nl = syms; nl < syms + numsyms; nl++) { + /* + * We accept only extern visible .text, .data and .bss symbols. + */ + if (strcmp (symbol, strs + nl->n_un.n_strx) == 0 + && ((nl->n_type == N_TEXT | N_EXT) || + (nl->n_type == N_DATA | N_EXT) || + (nl->n_type == N_BSS | N_EXT))) { + p = (struct nlist *)malloc (sizeof (struct nlist) + + strlen(strs + nl->n_un.n_strx) + 1); + if (!p) + break; + *p = *nl; + p->n_un.n_name = (char *)(p+1); + strcpy (p->n_un.n_name, strs + nl->n_un.n_strx); + } + } + return p; +} + +/* + * Return a pointer to the internal symbol + */ +static char *aout_ld_isymbol(char *symbol) +{ + static char isymbol[64]; + + strcpy(isymbol, STR(C_LABEL_PREFIX)); + strcat(isymbol, symbol); + + return isymbol; +} + +/* + * Return base address for the loaded kernel + */ +static u_long aout_get_kbase(void) +{ + return (u_long) kexec.a_entry; +} + +/* + * Return size of kernel code + data + */ +static u_long aout_get_ksize(void) +{ + return (u_long) (kexec.a_text + kexec.a_data); +} + +/* + * Load a.out kernel into memory + */ +static int aout_load_kernel(void *mem) +{ + if (lseek (kfd, N_TXTOFF(kexec), L_SET) == -1) + { + fprintf (stderr, "Failed to seek to text\n\r"); + exit (EXIT_FAILURE); + } + if (read (kfd, mem, kexec.a_text) != kexec.a_text) + { + fprintf (stderr, "Failed to read text\n\r"); + exit (EXIT_FAILURE); + } + if (lseek (kfd, N_DATOFF(kexec), L_SET) == -1) + { + fprintf (stderr, "Failed to seek to data\n\r"); + exit (EXIT_FAILURE); + } + if (read (kfd, mem + kexec.a_text, kexec.a_data) != kexec.a_data) + { + fprintf (stderr, "Failed to read data\n\r"); + exit (EXIT_FAILURE); + } + close (kfd); + + return 0; +} + +/* + * Print some statistics about the kernel + */ +static void aout_print_stats(void) +{ + printf("Kernel text at 0x%08lx, size %d bytes\n\r", + kexec.a_entry + N_TXTADDR(kexec), kexec.a_text); + printf("Kernel data at 0x%08lx, size %d bytes\n\r", + kexec.a_entry + N_DATADDR(kexec), kexec.a_data ); + printf("Kernel bss at 0x%08lx, size %d bytes\n\r", + kexec.a_entry + N_BSSADDR(kexec), kexec.a_bss ); +} + +static int aout_open_kernel(char *kernel) +{ + int sfd; + + /* + * open kernel executable and read exec header + */ + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Open kernel file\r\n"); + fflush(stdout); + } + if ((kfd = open (kernel, O_RDONLY)) == -1) + { + printf ("Unable to open kernel file %s\r\n", kernel); + exit (EXIT_FAILURE); + } + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Reading exec header\r\n"); + fflush(stdout); + } + if (read (kfd, (void *)&kexec, sizeof(kexec)) != sizeof(kexec)) + { + printf ("Unable to read exec header from %s\n\r", kernel); + exit (EXIT_FAILURE); + } + + /* + * Is this really a kernel??? + * (My Mips docs mention SMAGIC, too, but don't tell about what + * a SMAGIC file exactly is. If someone knows, please tell me -Ralf) + * + * FIXME: QMAGIC doesn't work yet. + */ + if(N_MAGIC(kexec) != OMAGIC && + N_MAGIC(kexec) != NMAGIC && + N_MAGIC(kexec) != ZMAGIC && + N_MAGIC(kexec) != QMAGIC) + { + fprintf(stderr, "Kernel %s is no MIPS binary.\r\n", kernel); + exit (EXIT_FAILURE); + } + if(N_MACHTYPE(kexec) != M_MIPS1 && + N_MACHTYPE(kexec) != M_MIPS2) + { + fprintf(stderr, "Kernel %s is no MIPS binary.\r\n", kernel); + exit (EXIT_FAILURE); + } + + /* + * Read file's symbol table + */ + /* + * Open kernel executable and read exec header - we need to + * use a second open file since the ARC standard doesn't + * support reverse seeking on files which might run us in + * trouble later on. + */ + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Open kernel file\r\n"); + fflush(stdout); + } + if ((sfd = open (kernel, O_RDONLY)) == -1) + { + printf ("Unable to open kernel %s for reading symbols.\r\n", kernel); + exit (EXIT_FAILURE); + } + syms = (struct nlist *)malloc (kexec.a_syms); + if (!syms) + { + return 0; + } + + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Seeking to symbol table\r\n"); + fflush(stdout); + } + lseek (sfd, N_SYMOFF(kexec), L_SET); + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Reading symbol table\r\n"); + fflush(stdout); + } + read (sfd, syms, kexec.a_syms); + numsyms = kexec.a_syms / sizeof (struct nlist); + filesize = lseek (sfd, 0L, L_XTND); + strsize = filesize - N_STROFF(kexec); + strs = (char *)malloc (strsize); + + if (!strs) + { + free (syms); + syms = NULL; + return 0; + } + + lseek (sfd, N_STROFF(kexec), L_SET); + read (sfd, strs, strsize); + close(sfd); + + return 0; +} + +static void aout_close_kernel(void) +{ + if (strs) + { + free (strs); + strs = NULL; + } + if (syms) + { + free (syms); + syms = NULL; + } + close(kfd); +} + +struct loader loader_aout = { + aout_get_nlist, + aout_ld_isymbol, + aout_get_kbase, + aout_get_ksize, + aout_load_kernel, + aout_print_stats, + aout_open_kernel, + aout_close_kernel +}; diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile new file mode 100644 index 000000000..228ee83bc --- /dev/null +++ b/arch/mips/boot/compressed/Makefile @@ -0,0 +1,45 @@ +# +# linux/arch/i386/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +HEAD = head.o +SYSTEM = $(TOPDIR)/vmlinux + +OBJECTS = $(HEAD) inflate.o unzip.o misc.o + +CFLAGS = -O2 -DSTDC_HEADERS + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) $(ASFLAGS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +all: vmlinux + +vmlinux: piggy.o $(OBJECTS) + $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/linux/tasks.h + $(CPP) -traditional head.S -o head.s + +piggy.o: $(SYSTEM) xtract piggyback + ./xtract $(SYSTEM) | gzip -9 | ./piggyback > piggy.o + +xtract: xtract.c + $(HOSTCC) $(CFLAGS) -o xtract xtract.c + +piggyback: piggyback.c + $(HOSTCC) $(CFLAGS) -o piggyback piggyback.c + +clean: + rm -f xtract piggyback vmlinux diff --git a/arch/mips/boot/compressed/cache.S b/arch/mips/boot/compressed/cache.S new file mode 100644 index 000000000..8e7fff0c1 --- /dev/null +++ b/arch/mips/boot/compressed/cache.S @@ -0,0 +1,162 @@ +/* + * arch/mips/boot/compressed/cache.S + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * Written by Ralf Baechle + * + * Flush instruction/data caches + * + * Parameters: a0 - starting address to flush + * a1 - size of area to be flushed + * a2 - which caches to be flushed + * + * FIXME: - ignores parameters in a0/a1 + * - doesn't know about second level caches + */ +#include <linux/autoconf.h> + +#include <asm/asm.h> +#include <asm/cachectl.h> +#include <asm/mipsregs.h> +#include <asm/segment.h> + +#ifdef __R4000__ + +/* + * Some bits in the config register + */ +#define CONFIG_IB (1<<5) +#define CONFIG_DB (1<<4) + + /* + * Flush instruction/data caches + * + * Parameters: a0 - starting address to flush + * a1 - size of area to be flushed + * a2 - which caches to be flushed + * + */ + + .text + + .set noreorder + LEAF(cacheflush) + andi t1,a2,DCACHE + beqz t1,do_icache + li t0,KSEG0 # delay slot + + /* + * Writeback data cache, even lines + */ + li t1,CACHELINES-1 +1: cache Index_Writeback_Inv_D,0(t0) + cache Index_Writeback_Inv_D,32(t0) + cache Index_Writeback_Inv_D,64(t0) + cache Index_Writeback_Inv_D,96(t0) + cache Index_Writeback_Inv_D,128(t0) + cache Index_Writeback_Inv_D,160(t0) + cache Index_Writeback_Inv_D,192(t0) + cache Index_Writeback_Inv_D,224(t0) + cache Index_Writeback_Inv_D,256(t0) + cache Index_Writeback_Inv_D,288(t0) + cache Index_Writeback_Inv_D,320(t0) + cache Index_Writeback_Inv_D,352(t0) + cache Index_Writeback_Inv_D,384(t0) + cache Index_Writeback_Inv_D,416(t0) + cache Index_Writeback_Inv_D,448(t0) + cache Index_Writeback_Inv_D,480(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + + /* + * Writeback data cache, odd lines + * Only needed for 16 byte line size + */ + mfc0 t1,CP0_CONFIG + andi t1,CONFIG_IB + bnez t1,do_icache + li t1,CACHELINES-1 +1: cache Index_Writeback_Inv_D,16(t0) + cache Index_Writeback_Inv_D,48(t0) + cache Index_Writeback_Inv_D,80(t0) + cache Index_Writeback_Inv_D,112(t0) + cache Index_Writeback_Inv_D,144(t0) + cache Index_Writeback_Inv_D,176(t0) + cache Index_Writeback_Inv_D,208(t0) + cache Index_Writeback_Inv_D,240(t0) + cache Index_Writeback_Inv_D,272(t0) + cache Index_Writeback_Inv_D,304(t0) + cache Index_Writeback_Inv_D,336(t0) + cache Index_Writeback_Inv_D,368(t0) + cache Index_Writeback_Inv_D,400(t0) + cache Index_Writeback_Inv_D,432(t0) + cache Index_Writeback_Inv_D,464(t0) + cache Index_Writeback_Inv_D,496(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + +do_icache: andi t1,a2,ICACHE + beqz t1,done + + /* + * Flush instruction cache, even lines + */ + lui t0,0x8000 + li t1,CACHELINES-1 +1: cache Index_Invalidate_I,0(t0) + cache Index_Invalidate_I,32(t0) + cache Index_Invalidate_I,64(t0) + cache Index_Invalidate_I,96(t0) + cache Index_Invalidate_I,128(t0) + cache Index_Invalidate_I,160(t0) + cache Index_Invalidate_I,192(t0) + cache Index_Invalidate_I,224(t0) + cache Index_Invalidate_I,256(t0) + cache Index_Invalidate_I,288(t0) + cache Index_Invalidate_I,320(t0) + cache Index_Invalidate_I,352(t0) + cache Index_Invalidate_I,384(t0) + cache Index_Invalidate_I,416(t0) + cache Index_Invalidate_I,448(t0) + cache Index_Invalidate_I,480(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + + /* + * Flush instruction cache, even lines + * Only needed for 16 byte line size + */ + mfc0 t1,CP0_CONFIG + andi t1,CONFIG_DB + bnez t1,done + li t1,CACHELINES-1 +1: cache Index_Invalidate_I,16(t0) + cache Index_Invalidate_I,48(t0) + cache Index_Invalidate_I,80(t0) + cache Index_Invalidate_I,112(t0) + cache Index_Invalidate_I,144(t0) + cache Index_Invalidate_I,176(t0) + cache Index_Invalidate_I,208(t0) + cache Index_Invalidate_I,240(t0) + cache Index_Invalidate_I,272(t0) + cache Index_Invalidate_I,304(t0) + cache Index_Invalidate_I,336(t0) + cache Index_Invalidate_I,368(t0) + cache Index_Invalidate_I,400(t0) + cache Index_Invalidate_I,432(t0) + cache Index_Invalidate_I,464(t0) + cache Index_Invalidate_I,496(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + +done: j ra + nop + END(sys_cacheflush) + +#else /* !defined (__R4000__) */ +#error "No R3000 cacheflushing implemented yet!" +#endif /* !defined (__R4000__) */ diff --git a/arch/mips/boot/compressed/crypt.h b/arch/mips/boot/compressed/crypt.h new file mode 100644 index 000000000..2a4c203ca --- /dev/null +++ b/arch/mips/boot/compressed/crypt.h @@ -0,0 +1,12 @@ +/* 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/mips/boot/compressed/gzip.h b/arch/mips/boot/compressed/gzip.h new file mode 100644 index 000000000..2f738b945 --- /dev/null +++ b/arch/mips/boot/compressed/gzip.h @@ -0,0 +1,284 @@ +/* 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/mips/boot/compressed/head.S b/arch/mips/boot/compressed/head.S new file mode 100644 index 000000000..0ca599563 --- /dev/null +++ b/arch/mips/boot/compressed/head.S @@ -0,0 +1,52 @@ +/* + * arch/mips/boot/compressed/head.S + * + * Copyright (C) 1995 by Ralf Baechle + * + * Head.S contains the MIPS exception handler and startup code. + */ + +#include <asm/asm.h> + + .text + .set noreorder +/* + * Compressed kernel entry + */ + NESTED(kernel_entry, 16, sp) + /* + * Set EXL in c0_status. The results in the lowest two + * gigabytes identity mapped. + */ + mfc0 t0,CP0_STATUS + ori t0,4 + mtc0 t0,CP0_STATUS + + /* + * Clear BSS first so that there are no surprises... + */ + la t0,_edata + la t1,_end + sw zero,(t0) +1: addiu t0,4 + bnel t0,t1,1b + sw zero,(t0) + END(kernel_entry) + + /* + * Do the decompression, and jump to the new kernel.. + */ + jal C_LABEL(decompress_kernel) + nop + + /* + * Flush caches + */ + jal C_LABEL(cacheflush) + + /* + * Jump into the decompressed kernel + */ + la t0,KSEG0 + jr t0 + nop diff --git a/arch/mips/boot/compressed/inflate.c b/arch/mips/boot/compressed/inflate.c new file mode 100644 index 000000000..848fef6ae --- /dev/null +++ b/arch/mips/boot/compressed/inflate.c @@ -0,0 +1,810 @@ +#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/mips/boot/compressed/lzw.h b/arch/mips/boot/compressed/lzw.h new file mode 100644 index 000000000..4e640f5a2 --- /dev/null +++ b/arch/mips/boot/compressed/lzw.h @@ -0,0 +1,42 @@ +/* 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/mips/boot/compressed/misc.c b/arch/mips/boot/compressed/misc.c new file mode 100644 index 000000000..1e3bb5f82 --- /dev/null +++ b/arch/mips/boot/compressed/misc.c @@ -0,0 +1,438 @@ +/* + * misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * Modified for Linux/MIPS 1995 by Ralf Baechle + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * puts by Nick Holloway 1993 + */ + +#include "gzip.h" +#include "lzw.h" + +#include <asm/segment.h> + +/* + * These are set up by the setup-routine at boot-time: + */ + +struct screen_info { + unsigned char orig_x; + unsigned char orig_y; + unsigned char unused1[2]; + unsigned short orig_video_page; + unsigned char orig_video_mode; + unsigned char orig_video_cols; + unsigned short orig_video_ega_ax; + unsigned short orig_video_ega_bx; + unsigned short orig_video_ega_cx; + unsigned char orig_video_lines; +}; + +/* + * This is set up by the setup-routine at boot-time + */ +#define EXT_MEM_K (*(unsigned short *)0x90002) +#define DRIVE_INFO (*(struct drive_info *)0x90080) +#define SCREEN_INFO (*(struct screen_info *)0x90000) +#define RAMDISK_SIZE (*(unsigned short *)0x901F8) +#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; +int graphmode; + +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 void puts(const char *); + +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) + return p; + puts("large kernel, low 1M tight..."); + free_mem_ptr = (long)input_data; + } +} + +void free(void *where) +{ /* Don't care */ +} + +static void scroll() +{ + int i; + + memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 ); + for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 ) + vidmem[i] = ' '; +} + +static void puts(const char *s) +{ + int x,y; + char c; + + if (graphmode) + { + /* + * No support for graphic console. We'd need a font + * and that would render the compression somewhat senseless... + */ + return; + } + + x = SCREEN_INFO.orig_x; + y = SCREEN_INFO.orig_y; + + while ( ( c = *s++ ) != '\0' ) { + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } + } + + SCREEN_INFO.orig_x = x; + SCREEN_INFO.orig_y = y; +} + +__ptr_t memset(__ptr_t s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i<n;i++) ss[i] = c; +} + +__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src, + size_t __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__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; +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty + * and at least one byte is really needed. + */ +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]; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +void flush_window() +{ + if (outcnt == 0) return; + updcrc(window, outcnt); + + memcpy(&output_data[output_ptr], (char *)window, outcnt); + + 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) +{ +/* 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; + } + crc_32_tab[i] = c; + } +} + +void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +#define STACK_SIZE 4096UL + +long user_stack [STACK_SIZE]; + +void decompress_kernel(void) +{ + if (boot_info.machtype == MACH_MIPS_MAGNUM_4000) + { + /* + * We don't have a font for graphic console so this means silence... + */ + graphmode = 1; + } + else if (boot_info.machtype = MACH_ACER_PICA_61) + { + vidmem = boot_info.vram_base; + vidmem = vidmem + 0x8000; + } + else + { + if (SCREEN_INFO.orig_video_mode == 7) + vidmem = SLOTSPACE + 0xb0000; + else + vidmem = SLOTSPACE + 0xb8000; + } + + lines = SCREEN_INFO.orig_video_lines; + cols = SCREEN_INFO.orig_video_cols; + + if (EXT_MEM_K < 1024) error("<2M of mem\n"); + + output_data = (char *)0x100000; /* Points to 1M */ + output_ptr = 0; + + exit_code = 0; + test = 0; + input_ptr = 0; + part_nb = 0; + + clear_bufs(); + makecrc(); + + puts("Uncompressing Linux..."); + + method = get_method(0); + + work(0, 0); + + puts("done.\n"); + + puts("Now booting the kernel\n"); +} + +/* ======================================================================== + * 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(int in) /* input file descriptor */ +{ + 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(); + } + + /* 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 */ ; + } + } else + error("unknown compression method"); + return method; +} diff --git a/arch/mips/boot/compressed/piggyback.c b/arch/mips/boot/compressed/piggyback.c new file mode 100644 index 000000000..40284118b --- /dev/null +++ b/arch/mips/boot/compressed/piggyback.c @@ -0,0 +1,81 @@ +/* + * linux/zBoot/piggyback.c + * + * (C) 1993 Hannu Savolainen + */ + +/* + * This program reads the compressed system image from stdin and + * encapsulates it into an object file written to the stdout. + */ + +#include <stdio.h> +#include <unistd.h> +#include <a.out.h> + +int main(int argc, char *argv[]) +{ + int c, n=0, len=0; + char tmp_buf[512*1024]; + + struct exec obj = {0x00640107}; /* object header */ + char string_names[] = {"_input_data\0_input_len\0"}; + + struct nlist var_names[2] = /* Symbol table */ + { + { /* _input_data */ + (char *)4, 7, 0, 0, 0 + }, + { /* _input_len */ + (char *)16, 7, 0, 0, 0 + } + }; + + + len = 0; + while ((n = read(0, &tmp_buf[len], sizeof(tmp_buf)-len+1)) > 0) + len += n; + + if (n==-1) + { + perror("stdin"); + exit(-1); + } + + if (len >= sizeof(tmp_buf)) + { + fprintf(stderr, "%s: Input too large\n", argv[0]); + exit(-1); + } + + fprintf(stderr, "Compressed size %d.\n", len); + +/* + * Output object header + */ + obj.a_data = len + sizeof(long); + obj.a_syms = sizeof(var_names); + write(1, (char *)&obj, sizeof(obj)); + +/* + * Output data segment (compressed system & len) + */ + write(1, tmp_buf, len); + write(1, (char *)&len, sizeof(len)); + +/* + * Output symbol table + */ + var_names[1].n_value = len; + write(1, (char *)&var_names, sizeof(var_names)); + +/* + * Output string table + */ + len = sizeof(string_names) + sizeof(len); + write(1, (char *)&len, sizeof(len)); + write(1, string_names, sizeof(string_names)); + + exit(0); + +} diff --git a/arch/mips/boot/compressed/unzip.c b/arch/mips/boot/compressed/unzip.c new file mode 100644 index 000000000..d4a6617cd --- /dev/null +++ b/arch/mips/boot/compressed/unzip.c @@ -0,0 +1,180 @@ +/* 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/mips/boot/compressed/xtract.c b/arch/mips/boot/compressed/xtract.c new file mode 100644 index 000000000..91de49ca7 --- /dev/null +++ b/arch/mips/boot/compressed/xtract.c @@ -0,0 +1,86 @@ +/* + * linux/zBoot/xtract.c + * + * Copyright (C) 1993 Hannu Savolainen + * + * Extracts the system image and writes it to the stdout. + * based on tools/build.c by Linus Torvalds + */ + +#include <stdio.h> /* fprintf */ +#include <string.h> +#include <stdlib.h> /* contains exit */ +#include <sys/types.h> /* unistd.h needs this */ +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> /* contains read/write */ +#include <fcntl.h> +#include <a.out.h> + +#define N_MAGIC_OFFSET 1024 + +static int GCC_HEADER = sizeof(struct exec); + +#define STRINGIFY(x) #x + +void die(char * str) +{ + fprintf(stderr,"%s\n",str); + exit(1); +} + +void usage(void) +{ + die("Usage: xtract system [ | gzip | piggyback > piggy.s]"); +} + +int main(int argc, char ** argv) +{ + int i,c,id, sz; + char buf[1024]; + char major_root, minor_root; + struct stat sb; + + struct exec *ex = (struct exec *)buf; + + if (argc != 2) + usage(); + + if ((id=open(argv[1],O_RDONLY,0))<0) + die("Unable to open 'system'"); + if (read(id,buf,GCC_HEADER) != GCC_HEADER) + die("Unable to read header of 'system'"); + if (N_MAGIC(*ex) == ZMAGIC) { + GCC_HEADER = N_MAGIC_OFFSET; + lseek(id, GCC_HEADER, SEEK_SET); + } else if (N_MAGIC(*ex) != QMAGIC) + die("Non-GCC header of 'system'"); + + sz = N_SYMOFF(*ex) - GCC_HEADER + 4; /* +4 to get the same result than tools/build */ + + fprintf(stderr, "System size is %d\n", sz); + + while (sz) + { + int l, n; + + l = sz; + if (l > sizeof(buf)) l = sizeof(buf); + + if ((n=read(id, buf, l)) !=l) + { + if (n == -1) + perror(argv[1]); + else + fprintf(stderr, "Unexpected EOF\n"); + + die("Can't read system"); + } + + write(1, buf, l); + sz -= l; + } + + close(id); + return(0); +} diff --git a/arch/mips/boot/head.S b/arch/mips/boot/head.S deleted file mode 100644 index 73e9d2cf9..000000000 --- a/arch/mips/boot/head.S +++ /dev/null @@ -1,387 +0,0 @@ -/* - * mips/head.S - * - * Copyright (C) 1994 Ralf Baechle - * - * Head.S contains the MIPS 32-bit startup code. - */ - -/* - * prevent prototypes from being imported - */ -#define __ASSEMBLY__ - -#include <asm/segment.h> -#include <asm/cachectl.h> -#include <asm/mipsregs.h> -#include <asm/mipsconfig.h> -#include <asm/stackframe.h> -#include <asm/regdef.h> -#include <linux/tasks.h> - - - .globl _empty_bad_page - .globl _empty_bad_page_table - .globl _invalid_pg_table - .globl _empty_zero_page - .globl _tmp_floppy_area - .globl _floppy_track_buffer - .globl _swapper_pg_dir - - .set noreorder - - .text -/* - * This is space for the interrupt handlers. - * They are located at virtual address 0x80000000 (physical 0x0) - */ - /* - * TLB refill, EXL == 0 - */ -except_vec0: - .set noreorder - .set noat - /* - * This TLB-refill handler is supposed never to cause - * another tlb-refill exception. Unmapped pages will - * cause another type of exception. - */ - dmfc0 k0,CP0_CONTEXT - dsrl k0,k0,1 - lwu k0,(k1) - lwu k1,4(k1) - dmtc0 k0,CP0_ENTRYLO0 - dmtc0 k0,CP0_ENTRYLO1 - tlbwr - eret - - /* - * XTLB refill, EXL == 0 (X == 64-bit TLB) - */ - .org except_vec0+0x80 -except_vec1: - /* - * Not used yet... - */ - eret - - /* - * Cache Error - */ - .org except_vec1+0x80 -except_vec2: - /* - * Not used yet... - */ - eret - - /* - * General exception vector. - */ - .org except_vec2+0x80 -except_vec3: - SAVE_ALL - mfc0 t0,CP0_STATUS - ori t0,t0,0x1f - xori t0,t0,0x1f - mtc0 t0,CP0_STATUS - .set at - la k0,_exception_handlers - mfc0 k1,CP0_CAUSE - andi k1,k1,0x7c - addu k0,k0,k1 - lw k0,(k0) - FILL_LDS - jr k0 - nop - - -/******************************************************************************/ - - /* - * The following data is expected to be at certain absolute - * addresses, which are hardwired in - * include/asm-mips/mipsconfig.h - * If the following offset is to short, the assembler will - * break with an assertion failure. You then will have to - * increase it and to fix the address in - * include/asm-mips/mipsconfig.h - */ - - .org except_vec3+0x100 - .globl _kernelsp -_kernelsp: .word 0 - -kernel_entry: - -/* - * Flush the TLB - */ - dmtc0 zero,CP0_ENTRYHI - dmtc0 zero,CP0_ENTRYLO0 - dmtc0 zero,CP0_ENTRYLO1 - li t0,NUMBER_OF_TLB_ENTRIES-1 -1: mtc0 t0,CP0_INDEX - tlbwi - bne zero,t0,1b - subu t0,t0,1 - -/* - * Initialize memory management. - * Wire mapping for port i/o space 0xe0000000 -> 0x9000000900000000 - */ - li t0,3 - mtc0 t0,CP0_WIRED - li t0,PM_64K - mtc0 t0,CP0_PAGEMASK - la t3,map0 - ld t1,0(t3) - ld t2,8(t3) - mtc0 zero,CP0_INDEX - dmtc0 t1,CP0_ENTRYHI - dmtc0 t2,CP0_ENTRYLO0 - dmtc0 zero,CP0_ENTRYLO1 /* Invalid page */ - tlbwi - li t0,PM_1M - mtc0 t0,CP0_PAGEMASK - ld t1,16(t3) - ld t2,24(t3) - li t0,1 - mtc0 t0,CP0_INDEX - dmtc0 t1,CP0_ENTRYHI - dmtc0 t2,CP0_ENTRYLO0 - tlbwi - ld t1,32(t3) - ld t2,40(t3) - li t0,2 - mtc0 t0,CP0_INDEX - dmtc0 t1,CP0_ENTRYHI - dmtc0 t2,CP0_ENTRYLO0 - tlbwi - -/* - * We always use 4k pages. Therefore the PageMask register - * is expected to be setup for 4k pages. - */ - li t0,PM_4K - mtc0 t0,CP0_PAGEMASK - -/* - * Clear BSS first so that there are no surprises... - */ - la t0,__edata - la t1,__end - sw zero,(t0) -1: addiu t0,t0,4 - bnel t0,t1,1b - sw zero,(t0) - -/* - * Copy bootup parameters out of the way. First 2kB of - * _empty_zero_page is for boot parameters, second 2kB - * is for the command line. - */ -#if 0 - movl $0x90000,%esi - movl $_empty_zero_page,%edi - movl $512,%ecx - cld - rep - movsl - xorl %eax,%eax - movl $512,%ecx - rep - stosl - cmpw $(CL_MAGIC),CL_MAGIC_ADDR - jne 1f - movl $_empty_zero_page+2048,%edi - movzwl CL_OFFSET,%esi - addl $(CL_BASE_ADDR),%esi - movl $2048,%ecx - rep - movsb -#endif - - /* - * Preliminary stack... - */ - la sp,0x80700000 - sw sp,_kernelsp -6: - jal _start_kernel - nop - j 6b # main should never return here, but - # just in case, we know what happens. - -#if 0 -/* This is the default interrupt "handler" :-) */ -int_msg: - .asciz "Unknown interrupt\n" -.align 2 -ignore_int: - cld - pushl %eax - pushl %ecx - pushl %edx - push %ds - push %es - push %fs - movl $(KERNEL_DS),%eax - mov %ax,%ds - mov %ax,%es - mov %ax,%fs - pushl $int_msg - call _printk - popl %eax - pop %fs - pop %es - pop %ds - popl %edx - popl %ecx - popl %eax - iret -#endif - -#define CACHELINES 512 /* number of cachelines */ - -/* - * Flush instruction/data caches - * - * Parameters: a0 - starting address to flush - * a1 - size of area to be flushed - * a2 - which caches to be flushed - * - * FIXME: - ignores parameters - * - doesn't know about second level caches - */ - - .set noreorder - .globl _cacheflush - .text -_cacheflush: - /* - * Flush the instruction cache - */ - lui t0,0x8000 - li t1,CACHELINES-1 -1: cache 0,0(t0) - cache 0,32(t0) - cache 0,64(t0) - cache 0,96(t0) - cache 0,128(t0) - cache 0,160(t0) - cache 0,192(t0) - cache 0,224(t0) - cache 0,256(t0) - cache 0,288(t0) - cache 0,320(t0) - cache 0,352(t0) - cache 0,384(t0) - cache 0,416(t0) - cache 0,448(t0) - cache 0,480(t0) - addiu t0,t0,512 - bne zero,t1,1b - subu t1,t1,1 - /* - * Flush the data cache - */ - lui t0,0x8000 - li t1,CACHELINES-1 -1: cache 1,0(t0) - cache 1,32(t0) - cache 1,64(t0) - cache 1,96(t0) - cache 1,128(t0) - cache 1,160(t0) - cache 1,192(t0) - cache 1,224(t0) - cache 1,256(t0) - cache 1,288(t0) - cache 1,320(t0) - cache 1,352(t0) - cache 1,384(t0) - cache 1,416(t0) - cache 1,448(t0) - cache 1,480(t0) - addiu t0,t0,512 - bne zero,t1,1b - subu t1,t1,1 - - j ra - nop - - .globl _beep -_beep: lbu t0,0xe0000061 - xori t0,t0,3 - sb t0,0xe0000061 - jr ra - nop - -/* - * Instead of Intel's strage and unportable segment descriptor magic - * we difference user and kernel space by their address. - * Kernel space (== physical memory) is mapped at 0x80000000, - * User space is mapped at 0x0. - */ - - .data - - .globl _segment_fs - /* - * Inital wired mappings - */ -map0: .quad 0xc00000ffe0000000,0x24000017 - .quad 0xc00000ffe1000000,0x04000017 - .quad 0xc00000ffe2000000,0x04020017 - -/* - * 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, - * 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..... - */ - .text - - .org 0x1000 -_swapper_pg_dir: -/* - * The page tables are initialized to only 4MB here - the final page - * tables are set up later depending on memory size. - */ - .org 0x2000 -_pg0: - - .org 0x3000 -_empty_bad_page: - - .org 0x4000 -_empty_bad_page_table: - - .org 0x5000 -_invalid_pg_table: - - .org 0x6000 -_empty_zero_page: - - .org 0x7000 - -/* - * tmp_floppy_area is used by the floppy-driver when DMA cannot - * reach to a buffer-block. It needs to be aligned, so that it isn't - * on a 64kB border. - */ -_tmp_floppy_area: .fill 1024,1,0 -/* - * 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*36,1,0 - -_segment_fs: .word KERNEL_DS diff --git a/arch/mips/boot/loader.h b/arch/mips/boot/loader.h new file mode 100644 index 000000000..610111b9a --- /dev/null +++ b/arch/mips/boot/loader.h @@ -0,0 +1,24 @@ +/* + * Defines for Linux/MIPS executable loaders + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1995 by Ralf Baechle + */ +#ifndef __STAND_LOADER +#define __STAND_LOADER + +struct loader { + struct nlist *(*ld_get_nlist)(char *symbol); + char *(*ld_isymbol)(char *); + u_long (*ld_get_kbase)(void); + u_long (*ld_get_ksize)(void); + int (*ld_load_kernel)(void *mem); + void (*ld_print_stats)(void); + int (*ld_open_kernel)(char *kernel); + void (*ld_close_kernel)(void); +}; + +#endif /* __STAND_LOADER */ diff --git a/arch/mips/boot/milo.c b/arch/mips/boot/milo.c new file mode 100644 index 000000000..a8a25dd91 --- /dev/null +++ b/arch/mips/boot/milo.c @@ -0,0 +1,316 @@ +/* + * Load and launch Linux/MIPS kernel. + * + * Copyright (C) 1994, 1995 by Waldorf Electronics, + * written by Ralf Baechle and Andreas Busse + * + * Loosly based on bootstrap.c for Linux/68k, + * Copyright (C) 1993 by Hamish Macdonald, Greg Harp + * + * This file is subject to the terms and conditions of the + * GNU General Public License. See the file COPYING in the + * main directory of this archive for more details. + */ +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/file.h> +#include <sys/types.h> + +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/a.out.h> + +/* + * Prototypes + */ +void usage(void); + +/* + * Defaults, may be overiden by option or environment. + */ +static char *kernel_name = KERNEL_NAME; +static char *ramdisk_name = NULL; +int option_debuglevel = 0; +int option_verbose = 1; +int behaviour = BEHAVE_MILO; + +extern char *optarg; + +extern volatile void launch(char *kptr, char *rdptr, + u_long kernel_size, u_long rd_size); + +static char *kptr; /* kernel will be loaded there */ +static char *rdptr; /* ramdisk will be loaded there */ + +u_long loadaddr = LOADADDR; /* mallocinit() needs that */ +u_long kernel_base = KERNELBASE; /* whereever that is... */ +u_long start_mem; /* always true on an ARC system */ +u_long mem_size; +u_long rd_size; +u_long kernel_entry; +u_long kernel_size; +u_long kernel_bss_end; + +struct bootinfo bi; /* Linux boot info */ +struct screen_info si; /* Linux screen info */ +struct DisplayInfo *di; /* ARC display info */ + + +/* + * For now we just use the aout loader + */ +extern struct loader loader_aout; +struct loader *kld = &loader_aout; + +/* + * main() + */ +int main(int argc, char *argv[]) +{ + int ch; + + /* + * Print the greet message + */ + printf("Linux/MIPS ARC Standalone Shell "); + printf("V" STR(VERSION) "\r\n"); + printf("Copyright (C) Waldorf Electronics and others 1994, 1995\r\n\r\n"); + + /* + * Analyse arguments + */ + if(argc > 1) + { + while ((ch = getopt(argc, argv, "dik:r:v")) != EOF) + switch (ch) + { + case 'd': + option_debuglevel++; + option_verbose = 1; + break; + case 'i': + interactive = 1; + break; + case 'k': + kernel_name = optarg; + break; + case 'r': + ramdisk_name = optarg; + break; + case 'v': + option_verbose = 1; + break; + case '?': + default: + usage(); + } + } +} + +/* + * Do the actual boot + */ +int do_boot(char *kernel_name, char *ramdisk_name) +{ + int kfd, rfd = -1, i; + u_long memreq; + struct nlist *nl; + u_long kbi_offset, ksi_offset; + + /* + * Verify that there is enough RAM + */ + mem_size = bi.memupper - bi.memlower; + if (mem_size < 0x800000) + { + fprintf(stderr, + "Insufficient Memory to load Linux/MIPS, aborting\n\r"); + return(5); + } + + if (behaviour == BEHAVE_ARCDB) + { + printf("%s: kernel file is `%s'\r\n", NAMEOF_ARCDB, kernel_name); + if (ramdisk_name) + printf("%s: ramdisk file is `%s'\r\n", NAMEOF_ARCDB, ramdisk_name); + } + + /* + * Open kernel and gather some data from the executable + */ + if (kld->ld_open_kernel(kernel_name) < 0) + { + fprintf(stderr, "Error opening kernel file %s.\n\r", kernel_name); + return 5; + } + kernel_base = kld->ld_get_kbase(); + kernel_size = kld->ld_get_ksize(); + + bi.ramdisk_size = 0; /* default: no ramdisk */ + if (ramdisk_name) + { + if ((rfd = open (ramdisk_name, O_RDONLY)) == -1) + { + fprintf (stderr, + "Unable to open ramdisk file %s\n\r", ramdisk_name); + } + else + { + /* + * record ramdisk size + */ + bi.ramdisk_size = (lseek (rfd, 0, L_XTND) + 1023) >> 10; + + rd_size = lseek (rfd, 0, L_XTND); + if (rd_size & ((1<<10)-1)) + { + /* + * Most probably the file is no image at all or has been + * corrupted, so print a warning message. + */ + printf("Warning: Ramdisk size is not multiple of 1024 bytes.\r\n"); + } + bi.ramdisk_size = rd_size >> 10; + } + } + bi.ramdisk_base = (u_long)start_mem + mem_size - rd_size; + + /* + * find offset to boot_info structure + */ + if (!(nl = kld->ld_get_nlist (kld->ld_isymbol("boot_info")))) + { + perror("get_nlist1"); + return 1; + } + else + { + kbi_offset = nl->n_value - kernel_base; + free(nl); + } + + /* + * Find offset to screen_info structure + */ + if (!(nl = kld->ld_get_nlist (kld->ld_isymbol("screen_info")))) + { + perror("get_nlist2"); + return 1; + } + else + { + ksi_offset = nl->n_value - kernel_base; + free(nl); + } + + /* + * Find kernel entry point + */ + if (!(nl = kld->ld_get_nlist (kld->ld_isymbol("kernel_entry")))) + { + perror("get_nlist3"); + return 1; + } + else + { + kernel_entry = nl->n_value; + free(nl); + } + + /* + * End of bss segment - ramdisk will be placed there + */ + if (!(nl = kld->ld_get_nlist (kld->ld_isymbol("_end")))) + { + perror("get_nlist3"); + return 1; + } + else + { + kernel_bss_end = nl->n_value; + free(nl); + } + + /* + * allocate buffers for kernel and ramdisk + */ + if (!(kptr = (char *) malloc(kernel_size))) + { + fprintf (stderr, "Unable to allocate %d bytes of memory\n\r", memreq); + return 1; + } + memset (kptr, 0, kernel_size); + + if (rd_size) + { + if (!(rdptr = (char *) malloc(rd_size))) + { + fprintf (stderr, + "Unable to allocate %d bytes of memory\n\r", memreq); + return 1; + } + memset (rdptr, 0, rd_size); + } + + /* + * Should check whether kernel & RAMdisk moving doesn't overwrite + * the bootstraper during startup. + */ + if (option_verbose) + { + /* + * The following text should be printed by the loader module + */ + printf ("\r\n"); + kld->ld_print_stats(); + printf ("Kernel entry at 0x%08lx\n\r", kernel_entry); + } + + /* + * Now do the real loading + */ + if (kld->ld_load_kernel(kptr) < 0) + { + fprintf (stderr, "Failed to load kernel executable\r\n"); + free(kptr); + exit (EXIT_FAILURE); + } + kld->ld_close_kernel(); + + /* + * copy the boot and screen info structures to the kernel image, + * then execute the copy-and-go code + */ + memcpy ((void *)(kptr + kbi_offset), &bi, sizeof(bi)); + memcpy ((void *)(kptr + ksi_offset), &si, sizeof(si)); + + /* + * Write the manipulated kernel back + */ + + /* + * Success... + */ + return 0; +} + +/* + * usage() + * + * Options: + * -d - enable debugging (default is no debugging) + * -i - interactive (default is noninteractive) + * -k - kernel executable (default multi(0)disk(0)fdisk(0)\VMLINUX) + * -r - ramdisk image (default is no ramdisk) + * -v - verbose output + */ +void usage(void) +{ + fprintf (stderr, "Usage:\r\n" + "\tmilo [-d] [-i] [-k kernel_executable] [-r ramdisk_file] [-v]" + " [option...]\r\n"); + exit (EXIT_FAILURE); +} diff --git a/arch/mips/config.in b/arch/mips/config.in index 24f0c13f9..6ecc5e698 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -3,28 +3,84 @@ # see the Configure script. # +comment 'Machine setup' + +bool 'Support for Acer PICA 1 chipset' CONFIG_ACER_PICA_61 y +bool 'Support for DECstation' CONFIG_DECSTATION n +bool 'Support for Deskstation RPC44' CONFIG_DESKSTATION_RPC44 n +bool 'Support for Deskstation Tyne' CONFIG_DESKSTATION_TYNE n +bool 'Support for Mips Magnum 3000' CONFIG_MIPS_MAGNUM_3000 n +bool 'Support for Mips Magnum 4000' CONFIG_MIPS_MAGNUM_4000 y +if [ "$CONFIG_ACER_PICA_61" = "y" -o \ + "$CONFIG_MIPS_MAGNUM_4000" = "y" -o \ + "$CONFIG_OLIVETTI_M700" = "y" ]; then + echo "#define CONFIG_MIPS_JAZZ" >> $CONFIG_H + echo "CONFIG_MIPS_JAZZ=y" >> $CONFIG + CONFIG_MIPS_JAZZ=y +fi + +comment 'CPU selection' + +bool 'Generate code for R3000' CONFIG_CPU_R3000 n +#bool 'Generate code for R6000' CONFIG_CPU_R6000 n +bool 'Generate code for R4x00' CONFIG_CPU_R4X00 y +bool 'Generate code for R4600' CONFIG_CPU_R4600 n +bool 'Generate code for R8000' CONFIG_CPU_R8000 n +bool 'Generate code for R10000' CONFIG_CPU_R10000 n +bool 'Generate little endian code' CONFIG_CPU_LITTLE_ENDIAN y +bool 'Compile the kernel into the ELF object format' CONFIG_MIPS_ELF n +if [ "$CONFIG_MIPS_ELF" = "y" ]; then + bool 'Is your normal Linux/MIPS compiler the ELF compiler' CONFIG_ELF_COMPILER n +fi + comment 'General setup' -bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD n -bool 'Normal harddisk support' CONFIG_BLK_DEV_HD n +bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y +bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 n +if [ "$CONFIG_ST506" = "y" ]; then + comment 'Please see block/drivers/README.ide for help/info on IDE drives' + bool ' Use old (reliable) disk-only driver for primary i/f' CONFIG_BLK_DEV_HD y + 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 n + fi + if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then + bool ' Include support for IDE CDROM (ATAPI)' CONFIG_BLK_DEV_IDECD n + fi +fi + bool 'XT harddisk support' CONFIG_BLK_DEV_XD n -bool 'Networking support' CONFIG_NET n +bool 'Networking support' CONFIG_NET y bool 'System V IPC' CONFIG_SYSVIPC n bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n if [ "$CONFIG_NET" = "y" ]; then comment 'Networking options' -bool 'TCP/IP networking' CONFIG_INET n -if [ "$CONFIG_INET" "=" "y" ]; then -bool 'IP forwarding/gatewaying' CONFIG_IP_FORWARD y +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 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n -bool 'Reverse ARP' CONFIG_INET_RARP n -bool 'Assume subnets are local' CONFIG_INET_SNARL y -bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n +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 'Amateur Radio AX.25 Level 2' CONFIG_AX25 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 fi comment 'SCSI support' @@ -39,20 +95,28 @@ 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 y -bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR y -bool 'Scsi generic support' CONFIG_CHR_DEV_SG y +bool 'SCSI disk support' CONFIG_BLK_DEV_SD y +bool 'SCSI tape support' CONFIG_CHR_DEV_ST y +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' -bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n -bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y -bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n +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 '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 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +if [ "$CONFIG_PCI" = "y" ]; then + bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +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 @@ -60,7 +124,7 @@ bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE 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 'EISA EATA support' CONFIG_SCSI_EATA 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 fi @@ -75,22 +139,30 @@ if [ "$CONFIG_NETDEVICES" = "n" ]; then comment 'Skipping network driver configuration options...' else -bool 'Dummy net driver support' CONFIG_DUMMY n +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' SL_COMPRESSED y -# bool ' SLIP debugging on' SL_DUMP y + 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 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n -bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC y +bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then - bool 'WD80*3 support' CONFIG_WD80x3 y + bool 'WD80*3 support' CONFIG_WD80x3 n bool 'SMC Ultra support' CONFIG_ULTRA n fi -bool '3COM cards' CONFIG_NET_VENDOR_3COM y +bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n +bool '3COM cards' CONFIG_NET_VENDOR_3COM n if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then bool '3c501 support' CONFIG_EL1 n bool '3c503 support' CONFIG_EL2 n @@ -102,32 +174,54 @@ if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then fi bool 'Other ISA cards' CONFIG_NET_ISA n if [ "$CONFIG_NET_ISA" = "y" ]; then - bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n - bool 'Cabletron E21xx support (not recommended)' CONFIG_E2100 n + 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 'EtherExpress support' CONFIG_EEXPRESS n 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 support' CONFIG_HPLAN n - bool 'HP PCLAN PLUS support' CONFIG_HPLAN_PLUS n + 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 and on board controllers' CONFIG_NET_EISA n +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 'NI52EE support' CONFIG_NI52 n -#bool 'NI65EE support' CONFIG_NI65 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 n +fi + +if [ "$CONFIG_MIPS_JAZZ" = "y" ]; then + bool 'MIPS JAZZ onboard SONIC ethernet support' CONFIG_MIPS_JAZZ_SONIC 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 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n - bool 'Zenith Z-Note support' CONFIG_ZNET 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 fi @@ -149,17 +243,18 @@ fi comment 'Filesystems' -bool 'Standard (minix) fs support' CONFIG_MINIX_FS y +bool 'Standard (minix) fs support' CONFIG_MINIX_FS n bool 'Extended fs support' CONFIG_EXT_FS n -bool 'Second extended fs support' CONFIG_EXT2_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 n if [ "$CONFIG_MSDOS_FS" = "y" ]; then -bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n +#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' fi bool '/proc filesystem support' CONFIG_PROC_FS n if [ "$CONFIG_INET" = "y" ]; then -bool 'NFS filesystem support' CONFIG_NFS_FS y +bool 'NFS filesystem support' CONFIG_NFS_FS n 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" ]; then bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y @@ -171,7 +266,9 @@ bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n comment 'character devices' -bool 'Parallel printer support' CONFIG_PRINTER n +bool 'Parallel printer support' CONFIG_PRINTER y +bool 'Standard serial device support' CONFIG_SERIAL y +bool 'Cyclades async mux support' CONFIG_CYCLADES n bool 'Logitech busmouse support' CONFIG_BUSMOUSE n bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n if [ "$CONFIG_PSMOUSE" = "y" ]; then @@ -179,8 +276,7 @@ bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE n fi bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n -bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION n - + 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 n @@ -207,8 +303,12 @@ bool 'Sound card support' CONFIG_SOUND n comment 'Kernel hacking' +bool 'Remote kernel debugging support' CONFIG_REMOTE_DEBUG n #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n bool 'Kernel profiling support' CONFIG_PROFILE n +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 +bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y fi diff --git a/arch/mips/dummy.c b/arch/mips/dummy.c deleted file mode 100644 index b85a1d71e..000000000 --- a/arch/mips/dummy.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * This file handles Systemcalls not available for all CPUs. - * - * Written by Ralf Baechle, - * Copyright (C) 1994 by Waldorf GMBH - */ - -unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) -{ - printk("clone_page_tables\n"); - return start_mem; -} - -void fake_keyboard_interrupt(void) -{ -/* printk("fake_keyboard_interrupt\n"); */ -} diff --git a/arch/mips/entry.S b/arch/mips/entry.S deleted file mode 100644 index ebf2c1d9c..000000000 --- a/arch/mips/entry.S +++ /dev/null @@ -1,665 +0,0 @@ -/* - * linux/kernel/mips/sys_call.S - * - * Copyright (C) 1994 Waldorf GMBH - * written by Ralf Baechle - */ - -/* - * sys_call.S contains the system-call and fault low-level handling routines. - * This also contains the timer-interrupt handler, as well as all interrupts - * and faults that can result in a task-switch. - */ - -#define __ASSEMBLY__ - -#include <linux/sys.h> -#include <asm/segment.h> -#include <asm/mipsregs.h> -#include <asm/mipsconfig.h> -#include <asm/stackframe.h> -#include <asm/regdef.h> - -/* - * These are offsets into the task-struct. - */ -state = 0 -counter = 4 -priority = 8 -signal = 12 -blocked = 16 -flags = 20 -errno = 24 #/* MIPS OK */ -exec_domain = 60 #/* ??? */ - -ENOSYS = 38 - - .globl _system_call - .globl _lcall7 - .globl _device_not_available - .globl _coprocessor_error - .globl _divide_error - .globl _debug - .globl _nmi - .globl _int3 - .globl _overflow - .globl _bounds - .globl _invalid_op - .globl _double_fault - .globl _coprocessor_segment_overrun - .globl _invalid_TSS - .globl _segment_not_present - .globl _stack_segment - .globl _general_protection - .globl _reserved - .globl _alignment_check - .globl _page_fault - .globl ret_from_sys_call - .globl _sys_call_table - - .text - .set noreorder - .align 4 -handle_bottom_half: - lw s0,_intr_count - addiu s1,s0,1 - sw s1,_intr_count - mfc0 t0,CP0_STATUS # Enable IRQs - ori t0,t0,7 - xori t0,t0,6 - jal _do_bottom_half - mtc0 t0,CP0_STATUS - j 9f - sw s1,_intr_count - - .set reorder - .align 4 -reschedule: - la ra,ret_from_sys_call - j _schedule - - .set noreorder - .align 4 -_system_call: - li t1,NR_syscalls - bge t0,t1,ret_from_sys_call - .set nomacro - li t2,-ENOSYS # must be single instruction! - .set macro - lui t1,_sys_call_table - sll t0,t0,2 - addu t1,t0,t1 - lw t0,_sys_call_table(t1) - lw s0,_current - - beq zero,t0,ret_from_sys_call - lw t0,flags(s0) - sll t0,t0,2 # PF_TRACESYS - bltz t0,1f - sw zero,errno(s0) # delay slot - - jal t0 # do the real work - nop # fillme: delay slot - - sw v0,FR_REG2(sp) # save the return value - lw v0,errno(s0) - beq zero,v0,ret_from_sys_call - subu v0,zero,v0 # v0 = -v0 - # fixme: indicate error - j ret_from_sys_call - sw v0,FR_REG2(sp) - - .align 4 -1: jal _syscall_trace - nop -#if 0 - movl ORIG_EAX(%esp),%eax - call _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 -#endif -1: jal _syscall_trace - nop - - .align 4 -ret_from_sys_call: - lw t0,_intr_count # bottom half - bne zero,t0,2f - - lw t0,_bh_mask - lw t1,_bh_active - and t0,t0,t1 - bne zero,t0,handle_bottom_half -9: - mfc0 t0,CP0_STATUS # returning to supervisor ? - andi t0,t0,30 - subu t0,t0,6 - bltz t0,2f - -1: -#if 0 -/* - * Try whether this is needed or not... - */ - mfc0 t0,CP0_STATUS # enable irqs - ori t0,t0,0x7 - xori t0,t0,0x6 - mtc0 t0,CP0_STATUS -#endif - - lw t0,_need_resched - bne zero,t0,reschedule - - lw t0,_current - la t1,_task # task[0] cannot have signals - lw t2,state(s0) # state - beq t0,t1,2f - lw t0,counter(s0) # counter - beq zero,t2,reschedule # state == 0 ? - lw a0,blocked(s0) - # save blocked in a0 for - # signal handling - beq zero,t0,reschedule # counter == 0 ? - lw t0,signal(s0) - nor t1,zero,t0 - and t1,a0,t1 - beq zero,t1,skip_signal_return - nop -2: - jal _do_signal - move a1,sp - -skip_signal_return: - .set noreorder - .set noat -return: RESTORE_ALL - .set at - -/* - * Assumptions for _handle_int: - * - only bank a or b are possible interrupt sources - */ - .globl _handle_int -_handle_int: - .set noreorder - .text - la s0,PORT_BASE - li t1,0x0f - sb t1,0x20(s0) # poll command - lb t1,0x20(s0) # read result - FILL_LDS - bgtz t1,poll_second - andi t1,t1,7 - /* - * Acknowledge first pic - */ - lb t2,0x21(s0) - li s1,1 - sllv s1,s1,t1 - lb t4,_cache_21 - or t4,t4,s1 - sb t4,_cache_21 - sb t4,0x21(s0) - li t4,0x20 - sb t4,0x20(s0) - lw t0,_intr_count - addiu t0,t0,1 - sw t0,_intr_count - /* - * Now call the real handler - */ - la t0,_IRQ_vectors - sll t2,t1,2 - addu t0,t0,t2 - lw t0,(t0) - FILL_LDS - jalr t0 - nop - lw t0,_intr_count - subu t0,t0,1 - sw t0,_intr_count - /* - * Unblock first pic - */ -test1: lbu t1,0x21(s0) # tlbl exception?!? - lb t1,_cache_21 - nor s1,zero,s1 - and t1,t1,s1 - sb t1,_cache_21 - jr v0 - sb t1,0x21(s0) # delay slot - - .set at -poll_second: - li t1,0x0f - sb t1,0xa0(s0) # poll command - lb t1,0xa0(s0) # read result - FILL_LDS - bgtz t1,spurious_interrupt - andi t1,t1,7 - /* - * Acknowledge second pic - */ - lbu t2,0xa1(s0) - lbu t3,_cache_A1 - li s1,1 - sllv s1,s1,t1 - or t3,t3,s1 - sb t3,_cache_A1 - sb t3,0xa1(s0) - li t3,0x20 - sb t3,0xa0(s0) - lw t0,_intr_count - sb t3,0x20(s0) - addiu t0,t0,1 - sw t0,_intr_count - /* - * Now call the real handler - */ - la t0,_IRQ_vectors - sll t2,t1,2 - addu t0,t0,t2 - lw t0,32(t0) - FILL_LDS - jalr t0 - nop - lw t0,_intr_count - subu t0,t0,1 - sw t0,_intr_count - /* - * Unblock second pic - */ - lbu t1,0xa1(s0) - lb t1,_cache_A1 - nor s1,zero,s1 - and t1,t1,s1 - sb t1,_cache_A1 - jr v0 - sb t1,0xa1(s0) # delay slot - - .set at -spurious_interrupt: - /* - * Nothing happend... (whistle) - */ - lw t0,_spurious_count - la v0,return - addiu t0,t0,1 - sw t0,_spurious_count - jr ra - nop - - .globl _IRQ -_IRQ: move s2,ra - mfc0 t0,CP0_STATUS - ori t0,t0,0x1f - xori t0,t0,0x1e - mtc0 t0,CP0_STATUS - move a1,sp - jal _do_IRQ - move a0,t1 # Delay slot - mfc0 t0,CP0_STATUS - ori t0,t0,1 - xori t0,t0,1 - la v0,ret_from_sys_call - jr s2 - mtc0 t0,CP0_STATUS # Delay slot - - .globl _fast_IRQ -_fast_IRQ: move s2,ra - move a1,sp - jal _do_fast_IRQ - move a0,t1 # Delay slot - la v0,return - jr s2 - nop - - .globl _bad_IRQ -_bad_IRQ: - /* - * Don't return & unblock the pic - */ - j return - nop - - .bss - .globl _IRQ_vectors - -_IRQ_vectors: - .fill 16,4,0 - -/* - * Dummy handlers - */ - .text - .set noreorder - .set at - - .globl _handle_mod -_handle_mod: - la a0,mod_text - j _panic - nop - - .globl _handle_tlbl -_handle_tlbl: - la a0,badvaddr - mfc0 a1,CP0_BADVADDR - jal _printk - nop - la a0,status - lw a1,FR_STATUS(sp) - jal _printk - nop - la a0,eszero - move a1,s0 - jal _printk - nop - la a0,espe - move a1,sp - jal _printk - nop - la a0,jifftext - lw a1,_jiffies - jal _printk - nop - la a0,inttext - lw a1,_intr_count - jal _printk - nop - la a0,tlbl_msg - mfc0 a1,CP0_EPC - jal _printk - nop - la a0,tlbl_text - j _panic - nop - - .data -tlbl_msg: .asciz "tlbl exception at %x\n" -badvaddr: .asciz "accessing %x\n" -status: .asciz "cp0_status %x\n" -eszero: .asciz "s0 %x\n" -espe: .asciz "sp %x\n" -jifftext: .asciz "jiffies %d\n" -inttext: .asciz "IntNest: %d\n" - - .text - .globl _handle_tlbs -_handle_tlbs: - la a0,tlbs_text - j _panic - nop - - .globl _handle_adel -_handle_adel: - la v0,adel_text - jal _printk - nop - j _handle_tlbl - la a0,adel_text - j _panic - nop - - .globl _handle_ades -_handle_ades: - la a0,ades_text - j _panic - nop - - .globl _handle_ibe -_handle_ibe: - la a0,ibe_text - j _panic - nop - - .globl _handle_dbe -_handle_dbe: - la a0,dbe_text - j _panic - nop - - .globl _handle_sys -_handle_sys: - la a0,sys_text - j _panic - nop - - .globl _handle_bp -_handle_bp: - la a0,bp_text - j _panic - nop - - .globl _handle_ri -_handle_ri: - la a0,ri_text - j _panic - nop - - .globl _handle_cpu -_handle_cpu: - la a0,cpu_text - j _panic - nop - - .globl _handle_ov -_handle_ov: - la a0,ov_text - j _panic - nop - - .globl _handle_tr -_handle_tr: - la a0,tr_text - j _panic - nop - - .globl _handle_reserved -_handle_reserved: - la a0,reserved_text - j _panic - nop - - .globl _handle_fpe -_handle_fpe: - la a0,fpe_text - j _panic - nop - - .data -spurious_text: .asciz "Spurious interrupt" -fpe_text: .asciz "fpe exception" -reserved_text: .asciz "reserved exception" -tr_text: .asciz "tr exception" -ov_text: .asciz "ov exception" -cpu_text: .asciz "cpu exception" -ri_text: .asciz "ri exception" -bp_text: .asciz "bp exception" -sys_text: .asciz "sys exception" -dbe_text: .asciz "dbe exception" -ibe_text: .asciz "ibe exception" -ades_text: .asciz "ades exception" -adel_text: .asciz "adel exception" -tlbs_text: .asciz "tlbs exception" -mod_text: .asciz "mod exception" -tlbl_text: .asciz "tlbl exception" - -/* - * Exception handler table, 256 entries. - */ - .data - .globl _exception_handlers -_exception_handlers: - .word _handle_int /* 0 */ - .word _handle_mod - .word _handle_tlbl - .word _handle_tlbs - .word _handle_adel - .word _handle_ades - .word _handle_ibe - .word _handle_dbe - .word _handle_sys - .word _handle_bp - .word _handle_ri - .word _handle_cpu - .word _handle_ov - .word _handle_tr - .word _handle_reserved - .word _handle_fpe /* 15 */ -#if 0 - .fill 240,4,_handle_reserved -#endif - -/* - * Table of syscalls - */ - .data -_sys_call_table: - .word _sys_setup /* 0 */ - .word _sys_exit - .word _sys_fork - .word _sys_read - .word _sys_write - .word _sys_open /* 5 */ - .word _sys_close - .word _sys_waitpid - .word _sys_creat - .word _sys_link - .word _sys_unlink /* 10 */ - .word _sys_execve - .word _sys_chdir - .word _sys_time - .word _sys_mknod - .word _sys_chmod /* 15 */ - .word _sys_chown - .word _sys_break - .word _sys_stat - .word _sys_lseek - .word _sys_getpid /* 20 */ - .word _sys_mount - .word _sys_umount - .word _sys_setuid - .word _sys_getuid - .word _sys_stime /* 25 */ - .word _sys_ptrace - .word _sys_alarm - .word _sys_fstat - .word _sys_pause - .word _sys_utime /* 30 */ - .word _sys_stty - .word _sys_gtty - .word _sys_access - .word _sys_nice - .word _sys_ftime /* 35 */ - .word _sys_sync - .word _sys_kill - .word _sys_rename - .word _sys_mkdir - .word _sys_rmdir /* 40 */ - .word _sys_dup - .word _sys_pipe - .word _sys_times - .word _sys_prof - .word _sys_brk /* 45 */ - .word _sys_setgid - .word _sys_getgid - .word _sys_signal - .word _sys_geteuid - .word _sys_getegid /* 50 */ - .word _sys_acct - .word _sys_phys - .word _sys_lock - .word _sys_ioctl - .word _sys_fcntl /* 55 */ - .word _sys_mpx - .word _sys_setpgid - .word _sys_ulimit - .word _sys_olduname - .word _sys_umask /* 60 */ - .word _sys_chroot - .word _sys_ustat - .word _sys_dup2 - .word _sys_getppid - .word _sys_getpgrp /* 65 */ - .word _sys_setsid - .word _sys_sigaction - .word _sys_sgetmask - .word _sys_ssetmask - .word _sys_setreuid /* 70 */ - .word _sys_setregid - .word _sys_sigsuspend - .word _sys_sigpending - .word _sys_sethostname - .word _sys_setrlimit /* 75 */ - .word _sys_getrlimit - .word _sys_getrusage - .word _sys_gettimeofday - .word _sys_settimeofday - .word _sys_getgroups /* 80 */ - .word _sys_setgroups - .word _sys_select - .word _sys_symlink - .word _sys_lstat - .word _sys_readlink /* 85 */ - .word _sys_uselib - .word _sys_swapon - .word _sys_reboot - .word _sys_readdir - .word _sys_mmap /* 90 */ - .word _sys_munmap - .word _sys_truncate - .word _sys_ftruncate - .word _sys_fchmod - .word _sys_fchown /* 95 */ - .word _sys_getpriority - .word _sys_setpriority - .word _sys_profil - .word _sys_statfs - .word _sys_fstatfs /* 100 */ - .word _sys_ioperm - .word _sys_socketcall - .word _sys_syslog - .word _sys_setitimer - .word _sys_getitimer /* 105 */ - .word _sys_newstat - .word _sys_newlstat - .word _sys_newfstat - .word _sys_uname - .word _sys_iopl /* 110 */ - .word _sys_vhangup - .word _sys_idle - .word _sys_vm86 - .word _sys_wait4 - .word _sys_swapoff /* 115 */ - .word _sys_sysinfo - .word _sys_ipc - .word _sys_fsync - .word _sys_sigreturn - .word _sys_clone /* 120 */ - .word _sys_setdomainname - .word _sys_newuname - .word _sys_modify_ldt - .word _sys_adjtimex - .word _sys_mprotect /* 125 */ - .word _sys_sigprocmask - .word _sys_create_module - .word _sys_init_module - .word _sys_delete_module - .word _sys_get_kernel_syms /* 130 */ - .word _sys_quotactl - .word _sys_getpgid - .word _sys_fchdir - .word _sys_bdflush - .word _sys_sysfs /* 135 */ - .word _sys_personality - .word 0 /* for afs_syscall */ - .word _sys_setfsuid - .word _sys_setfsgid - .word _sys_llseek /* 140 */ - .space (NR_syscalls-140)*4 diff --git a/arch/mips/ioport.c b/arch/mips/ioport.c deleted file mode 100644 index ee3352410..000000000 --- a/arch/mips/ioport.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * linux/arch/mips/ioport.c - * - * Functions not implemented for Linux/MIPS - */ -#include <linux/linkage.h> -#include <linux/errno.h> - -asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) -{ - return -ENOSYS; -} - -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) -{ - return -ENOSYS; -} diff --git a/arch/mips/irq.S b/arch/mips/irq.S deleted file mode 100644 index 129c2843f..000000000 --- a/arch/mips/irq.S +++ /dev/null @@ -1,642 +0,0 @@ -/* - * linux/kernel/mips/sys_call.S - * - * Copyright (C) 1994 Waldorf GMBH - * written by Ralf Baechle - */ - -/* - * All code below must be relocatable! - */ - -/* - * sys_call.S contains the system-call and fault low-level handling routines. - * This also contains the timer-interrupt handler, as well as all interrupts - * and faults that can result in a task-switch. - * - * NOTE: This code handles signal-recognition, which happens every time - * after a timer-interrupt and after each system call. - * - * I changed all the .align's to 4 (16 byte alignment), as that's faster - * on a 486. - * - * Stack layout in 'ret_from_system_call': - * ptrace needs to have all regs on the stack. - * if the order here is changed, it needs to be - * updated in fork.c:copy_process, signal.c:do_signal, - * ptrace.c and ptrace.h - * - * 0(%esp) - %ebx - * 4(%esp) - %ecx - * 8(%esp) - %edx - * C(%esp) - %esi - * 10(%esp) - %edi - * 14(%esp) - %ebp - * 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 - */ - -#include <linux/segment.h> -#include <linux/sys.h> - -/* - * Offsets into the Interrupt stackframe. - */ -FR_REG1 = 0 -FR_REG2 = 4 -FR_REG3 = 8 -FR_REG4 = 12 -FR_REG5 = 16 -FR_REG6 = 20 -FR_REG7 = 24 -FR_REG8 = 28 -FR_REG9 = 32 -FR_REG10 = 36 -FR_REG11 = 40 -FR_REG12 = 44 -FR_REG13 = 48 -FR_REG14 = 52 -FR_REG15 = 56 -FR_REG16 = 60 -FR_REG17 = 64 -FR_REG18 = 68 -FR_REG19 = 72 -FR_REG20 = 76 -FR_REG21 = 80 -FR_REG22 = 84 -FR_REG23 = 88 -FR_REG24 = 92 -FR_REG25 = 96 -/* $26 and $27 not saved */ -FR_REG28 = 100 -FR_REG29 = 104 -FR_REG30 = 108 -FR_REG31 = 112 -/* - * Saved cp0 registers follow - */ -FR_STATUS = 116 -FR_EPC = 120 -FR_ERROREPC = 124 -FR_SIZE = 120 /* Size of stack frame */ - -/* - * These are offsets into the task-struct. - */ -state = 0 -counter = 4 -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, - .globl _invalid_op,_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(which_pc) \ - .set noreorder \ - .set noat \ - lui k0,0x8000 \ - move k1,$sp \ - lw sp,_kernelsp-except_vec0(k0) \ - subu sp,$sp,FR_SIZE \ - sw sp,_kernelsp-except_vec0(k0) \ /* Kernel SP */ - mfc0 v0,CP0_STATUS \ - sw v0,FR_STATUS(sp) \ - mfc0 v0,CP0_EPC \ - sw v0,FR_EPC \ - mfc0 v0,CP0_ERROREPC \ - sw v0,FR_ERROREPC \ - sw k1,FR_R27(sp) \ - sw $2,FR_R1(sp) \ - sw $2,FR_R2(sp) \ - sw $3,FR_R3(sp) \ - sw $4,FR_R4(sp) \ - sw $5,FR_R5(sp) \ - sw $6,FR_R6(sp) \ - sw $7,FR_R7(sp) \ - sw $8,FR_R8(sp) \ - sw $9,FR_R9(sp) \ - sw $10,FR_R10(sp) \ - sw $11,FR_R11(sp) \ - sw $12,FR_R12(sp) \ - sw $13,FR_R13(sp) \ - sw $14,FR_R14(sp) \ - sw $15,FR_R15(sp) \ - sw $16,FR_R16(sp) \ - sw $17,FR_R17(sp) \ - sw $18,FR_R18(sp) \ - sw $19,FR_R19(sp) \ - sw $20,FR_R20(sp) \ - sw $21,FR_R21(sp) \ - sw $22,FR_R22(sp) \ - sw $23,FR_R23(sp) \ - sw $24,FR_R24(sp) \ - sw $25,FR_R25(sp) \ - sw $28,FR_R28(sp) \ - sw $30,FR_R30(sp) \ - sw $31,FR_R31(sp) - - -#define RESTORE_ALL \ - lui k1,0x8000 \ - move k0,$sp \ - lw v0,FR_ERROREPC(k0) \ - lw v1,FR_EPC(k0) \ - mtc0 v0,CP0_ERROREPC(k0) \ - mtc0 v1,CP0_EPC(k0) \ - lw $31,FR_R31(k0) \ - lw $30,FR_R30(k0) \ - lw $28,FR_R28(k0) \ - lw $25,FR_R25(k0) \ - lw $24,FR_R24(k0) \ - lw $23,FR_R23(k0) \ - lw $22,FR_R22(k0) \ - lw $21,FR_R21(k0) \ - lw $20,FR_R20(k0) \ - lw $19,FR_R19(k0) \ - lw $18,FR_R18(k0) \ - lw $17,FR_R17(k0) \ - lw $16,FR_R16(k0) \ - lw $15,FR_R15(k0) \ - lw $14,FR_R14(k0) \ - lw $13,FR_R13(k0) \ - lw $12,FR_R12(k0) \ - lw $11,FR_R11(k0) \ - lw $10,FR_R10(k0) \ - lw $9,FR_R9(k0) \ - lw $8,FR_R8(k0) \ - lw $7,FR_R7(k0) \ - lw $6,FR_R6(k0) \ - lw $5,FR_R5(k0) \ - lw $4,FR_R4(k0) \ - lw $3,FR_R3(k0) \ - lw $2,FR_R2(k0) \ - lw $1,FR_R1(k0) \ - addiu k0,k0,FR_SIZE \ - sw k0,_kernelsp-except_vec0(k1) \ /* Kernel SP */ - eret - - .align 4 -handle_bottom_half: - pushfl - incl _intr_count - mtc0 zero,CP0_STATUS - call _do_bottom_half - popfl - decl _intr_count - j 9f - nop - - .align 4 -reschedule: - pushl $ret_from_sys_call - j _schedule - nop - - .align 4 -_system_call: - pushl %eax # save orig_eax - SAVE_ALL - movl $-ENOSYS,EAX(%esp) - 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 - 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 - j ret_from_sys_call - nop - - .align 4 -1: call _syscall_trace - movl ORIG_EAX(%esp),%eax - call _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 -ret_from_sys_call: - cmpl $0,_intr_count - jne 2f - movl _bh_mask,%eax - andl _bh_active,%eax - jne handle_bottom_half -9: movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are - testl $(VM_MASK),%eax # different then - 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 - 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 - jne signal_return -2: RESTORE_ALL - - .align 4 -signal_return: - movl %esp,%ecx - pushl %ecx - testl $(VM_MASK),EFLAGS(%ecx) - jne v86_signal_return - pushl %ebx - call _do_signal - popl %ebx - popl %ebx - RESTORE_ALL - - .align 4 -v86_signal_return: - call _save_v86_state - movl %eax,%esp - pushl %eax - pushl %ebx - call _do_signal - popl %ebx - popl %ebx - RESTORE_ALL - - .align 4 -_divide_error: - move $a1,zero # no error code - la $t0,$_do_divide_error - .align 4,0x90 -error_code: - push %fs - push %es - push %ds - pushl %eax - pushl %ebp - pushl %edi - pushl %esi - pushl %edx - pushl %ecx - pushl %ebx - cld - movl $-1, %eax - 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. - 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 - jal t0 # call handler - addl $8,%esp - j ret_from_sys_call - - .align 4 -_coprocessor_error: - move a1,zero - la t0,_do_coprocessor_error - j error_code - - .align 4 -_device_not_available: - pushl $-1 # mark this as an int - SAVE_ALL - pushl $ret_from_sys_call - movl %cr0,%eax - testl $0x4,%eax # EM (math emulation bit) - je _math_state_restore - pushl $0 # temporary storage for ORIG_EIP - call _math_emulate - addl $4,%esp - ret - - .set reorder - - .align 4 -_debug: - move a1,zero - la t0,_do_debug - j error_code - - .align 4 -_nmi: - move a1,zero - la t0,_do_nmi - j error_code - - .align 4 -_int3: - move a1,zero - la t0,_do_int3 - j error_code - - .align 4 -_overflow: - move a1,zero - la t0,_do_overflow - j error_code - - .align 4 -_bounds: - move a1,zero - la t0,_do_bounds - j error_code - - .align 4 -_invalid_op: - move a1,zero - la t0,_do_invalid_op - j error_code - - .align 4 -_segment_not_present: - la t0,_do_segment_not_present - j error_code - - .align 4 -_stack_segment: - la t0,_do_stack_segment - j error_code - - .align 4 -_general_protection: - la t0,_do_general_protection - j error_code - - .align 4 -_page_fault: - la t0,_do_page_fault - j error_code -/* - * TLB Refill exception entry point - * - * The mm data is stored in the context register and - */ - .text - .set noreorder - .set noat - dmfc0 k0,CP0_CONTEXT - dsrl k0,k0,2 - lw k0,(k0) # Level 1 descriptor - dmfc0 k1,CP0_BADVADDR - srl k1,k1,10 - andi k1,k1,0xffc - addu k1,k1,k1 - lwu k0,(k1) # 2 Level 2 entries - lwu k1,4(k1) - dmtc0 k0,CP0_ENTRYLO0 - dmtc0 k0,CP0_ENTRYLO1 - tlbwr - /* - * Now compute the return address. Since this is extremly - * timecritical the code is inlined - */ - mfc0 k0,CP0_CAUSE - bgtz k0,1f - - /* - * Damn - a branch delay slot. Compute new PC - */ - - /* - * That's it boys - back to work! - */ -1: eret - - - - - lui t0,>_exception_handlers - mfc0 t1,CP0_CAUSE - andi t1,t1,0x3fc - addu t0,t0,t1 - lw t0,<_exception_handlers(t0) - sw /* fill delay slot */ - jalr t0 - sw /* fill delay slot */ - - -/* - * Exception handler table, 256 entries. - */ - .data - .align 4 -_exception_handlers: - .word _handle_int /* 0 */ - .word _handle_mod - .word _handle_tlbl - .word _handle_tlbs - .word _handle_adel - .word _handle_ades - .word _handle_ibe - .word _handle_dbe - .word _handle_sys - .word _handle_bp - .word _handle_ri - .word _handle_cpu - .word _handle_ov - .word _handle_tr - .word _handle_reserved - .word _handle_fpe - .fill 240,4,_handle_reserved /* 16 */ - -/* - * Table of syscalls - */ - .data - .align 4 -_sys_call_table: - .word _sys_setup /* 0 */ - .word _sys_exit - .word _sys_fork - .word _sys_read - .word _sys_write - .word _sys_open /* 5 */ - .word _sys_close - .word _sys_wordpid - .word _sys_creat - .word _sys_link - .word _sys_unlink /* 10 */ - .word _sys_execve - .word _sys_chdir - .word _sys_time - .word _sys_mknod - .word _sys_chmod /* 15 */ - .word _sys_chown - .word _sys_break - .word _sys_stat - .word _sys_lseek - .word _sys_getpid /* 20 */ - .word _sys_mount - .word _sys_umount - .word _sys_setuid - .word _sys_getuid - .word _sys_stime /* 25 */ - .word _sys_ptrace - .word _sys_alarm - .word _sys_fstat - .word _sys_pause - .word _sys_utime /* 30 */ - .word _sys_stty - .word _sys_gtty - .word _sys_access - .word _sys_nice - .word _sys_ftime /* 35 */ - .word _sys_sync - .word _sys_kill - .word _sys_rename - .word _sys_mkdir - .word _sys_rmdir /* 40 */ - .word _sys_dup - .word _sys_pipe - .word _sys_times - .word _sys_prof - .word _sys_brk /* 45 */ - .word _sys_setgid - .word _sys_getgid - .word _sys_signal - .word _sys_geteuid - .word _sys_getegid /* 50 */ - .word _sys_acct - .word _sys_phys - .word _sys_lock - .word _sys_ioctl - .word _sys_fcntl /* 55 */ - .word _sys_mpx - .word _sys_setpgid - .word _sys_ulimit - .word _sys_olduname - .word _sys_umask /* 60 */ - .word _sys_chroot - .word _sys_ustat - .word _sys_dup2 - .word _sys_getppid - .word _sys_getpgrp /* 65 */ - .word _sys_setsid - .word _sys_sigaction - .word _sys_sgetmask - .word _sys_ssetmask - .word _sys_setreuid /* 70 */ - .word _sys_setregid - .word _sys_sigsuspend - .word _sys_sigpending - .word _sys_sethostname - .word _sys_setrlimit /* 75 */ - .word _sys_getrlimit - .word _sys_getrusage - .word _sys_gettimeofday - .word _sys_settimeofday - .word _sys_getgroups /* 80 */ - .word _sys_setgroups - .word _sys_select - .word _sys_symlink - .word _sys_lstat - .word _sys_readlink /* 85 */ - .word _sys_uselib - .word _sys_swapon - .word _sys_reboot - .word _sys_readdir - .word _sys_mmap /* 90 */ - .word _sys_munmap - .word _sys_truncate - .word _sys_ftruncate - .word _sys_fchmod - .word _sys_fchown /* 95 */ - .word _sys_getpriority - .word _sys_setpriority - .word _sys_profil - .word _sys_statfs - .word _sys_fstatfs /* 100 */ - .word _sys_ioperm - .word _sys_socketcall - .word _sys_syslog - .word _sys_setitimer - .word _sys_getitimer /* 105 */ - .word _sys_newstat - .word _sys_newlstat - .word _sys_newfstat - .word _sys_uname - .word _sys_iopl /* 110 */ - .word _sys_vhangup - .word _sys_idle - .word _sys_vm86 - .word _sys_word4 - .word _sys_swapoff /* 115 */ - .word _sys_sysinfo - .word _sys_ipc - .word _sys_fsync - .word _sys_sigreturn - .word _sys_clone /* 120 */ - .word _sys_setdomainname - .word _sys_newuname - .word _sys_modify_ldt - .word _sys_adjtimex - .word _sys_mprotect /* 125 */ - .word _sys_sigprocmask - .word _sys_create_module - .word _sys_init_module - .word _sys_delete_module - .word _sys_get_kernel_syms /* 130 */ - .word _sys_quotactl - .word _sys_getpgid - .word _sys_fchdir - .word _sys_bdflush - .word _sys_sysfs /* 135 */ - .word _sys_personality - .word 0 /* for afs_syscall */ - - .space (NR_syscalls-137)*4 diff --git a/arch/mips/irq.c b/arch/mips/irq.c deleted file mode 100644 index 1bce3d07a..000000000 --- a/arch/mips/irq.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * linux/kernel/irq.c - * - * Copyright (C) 1992 Linus Torvalds - * - * This file contains the code used by various IRQ handling routines: - * asking for different IRQ's should be done through these routines - * instead of just grabbing them. Thus setups with different IRQ numbers - * shouldn't result in any weird surprises, and installing new handlers - * should be easier. - */ - -/* - * IRQ's are in fact implemented a bit like signal handlers for the kernel. - * The same sigaction struct is used, and with similar semantics (ie there - * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there - * are similarities. - * - * sa_handler(int irq_NR) is the default function called (0 if no). - * sa_mask is horribly ugly (I won't even mention it) - * sa_flags contains various info: SA_INTERRUPT etc - * sa_restorer is the unused - */ - -#include <linux/ptrace.h> -#include <linux/errno.h> -#include <linux/kernel_stat.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/interrupt.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/irq.h> - -#define CR0_NE 32 - -unsigned char cache_21 = 0xff; -unsigned char cache_A1 = 0xff; - -unsigned long intr_count = 0; -unsigned long spurious_count = 0; -unsigned long bh_active = 0; -unsigned long bh_mask = 0xFFFFFFFF; -struct bh_struct bh_base[32]; - -void disable_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; - } - cli(); - cache_A1 |= mask; - outb(cache_A1,0xA1); - restore_flags(flags); -} - -void enable_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; - } - cli(); - cache_A1 &= mask; - outb(cache_A1,0xA1); - restore_flags(flags); -} - -/* - * do_bottom_half() runs at normal kernel priority: all interrupts - * enabled. do_bottom_half() is atomic with respect to itself: a - * bottom_half handler need not be re-entrant. - */ -asmlinkage void do_bottom_half(void) -{ - unsigned long active; - unsigned long mask, left; - struct bh_struct *bh; - - bh = bh_base; - active = bh_active & bh_mask; - for (mask = 1, left = ~0 ; left & active ; bh++,mask += mask,left += left) { - if (mask & active) { - void (*fn)(void *); - bh_active &= ~mask; - fn = bh->routine; - if (!fn) - goto bad_bh; - fn(bh->data); - } - } - return; -bad_bh: - printk ("irq.c:bad bottom half entry\n"); -} - -/* - * Pointers to the low-level handlers: first the general ones, then the - * fast ones, then the bad ones. - */ -extern void IRQ(void); -extern void fast_IRQ(void); -extern void bad_IRQ(void); - -/* - * Initial irq handlers. - */ -static struct sigaction irq_sigaction[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 } -}; - -int get_irq_list(char *buf) -{ - int i, len = 0; - struct sigaction * sa = irq_sigaction; - - for (i = 0 ; i < 16 ; i++, sa++) { - if (!sa->sa_handler) - continue; - len += sprintf(buf+len, "%2d: %8d %c %s\n", - i, kstat.interrupts[i], - (sa->sa_flags & SA_INTERRUPT) ? '+' : ' ', - (char *) sa->sa_mask); - } - return len; -} - -/* - * do_IRQ handles IRQ's that have been installed without the - * SA_INTERRUPT flag: it uses the full signal-handling return - * and runs with other interrupts enabled. All relatively slow - * IRQ's should use this format: notably the keyboard/timer - * routines. - */ -asmlinkage void do_IRQ(int irq, struct pt_regs * regs) -{ - struct sigaction * sa = irq + irq_sigaction; - - kstat.interrupts[irq]++; - sa->sa_handler((int) regs); -} - -/* - * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return - * stuff - the handler is also running with interrupts disabled unless - * it explicitly enables them later. - */ -asmlinkage void do_fast_IRQ(int irq) -{ - struct sigaction * sa = irq + irq_sigaction; - - kstat.interrupts[irq]++; - sa->sa_handler(irq); -} - -/* - * Using "struct sigaction" is slightly silly, but there - * are historical reasons and it works well, so.. - */ -static int irqaction(unsigned int irq, struct sigaction * new_sa) -{ - struct sigaction * sa; - unsigned long flags; - - if (irq > 15) - return -EINVAL; - sa = irq + irq_sigaction; - if (sa->sa_handler) - return -EBUSY; - if (!new_sa->sa_handler) - return -EINVAL; - save_flags(flags); - cli(); - *sa = *new_sa; - /* - * FIXME: Does the SA_INTERRUPT flag make any sense on the MIPS??? - */ - if (sa->sa_flags & SA_INTERRUPT) - set_intr_gate(irq,fast_IRQ); - else - set_intr_gate(irq,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; -} - -int request_irq(unsigned int irq, void (*handler)(int), - unsigned long flags, const char * devname) -{ - struct sigaction sa; - - sa.sa_handler = handler; - sa.sa_flags = flags; - sa.sa_mask = (unsigned long) devname; - sa.sa_restorer = NULL; - return irqaction(irq,&sa); -} - -void free_irq(unsigned int irq) -{ - struct sigaction * sa = irq + irq_sigaction; - unsigned long flags; - - if (irq > 15) { - printk("Trying to free IRQ%d\n",irq); - return; - } - if (!sa->sa_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(irq,bad_IRQ); - sa->sa_handler = NULL; - sa->sa_flags = 0; - sa->sa_mask = 0; - sa->sa_restorer = NULL; - restore_flags(flags); -} - -#if 0 -/* - * handle fpa errors - */ -static void math_error_irq(int cpl) -{ - if (!hard_math) - return; - handle_fpe(); -} -#endif - -static void no_action(int cpl) { } - -void init_IRQ(void) -{ - int i; - - for (i = 0; i < 16 ; i++) - set_intr_gate(i, bad_IRQ[i]); - if (request_irq(2, no_action, SA_INTERRUPT, "cascade")) - printk("Unable to get IRQ2 for cascade\n"); - - /* initialize the bottom half routines. */ - for (i = 0; i < 32; i++) { - bh_base[i].routine = NULL; - bh_base[i].data = NULL; - } - bh_active = 0; - intr_count = 0; -} diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile new file mode 100644 index 000000000..0086f60cf --- /dev/null +++ b/arch/mips/kernel/Makefile @@ -0,0 +1,94 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) $(ASFLAGS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +OBJS = process.o signal.o entry.o traps.o irq.o ptrace.o vm86.o ioport.o \ + setup.o bios32.o tynedma.o + +include ../../../.config + +# +# Kernel debugging +# + +ifdef CONFIG_REMOTE_DEBUG +OBJS += gdb-low.o gdb-stub.o +endif + +# +# Board specific code +# + +ifdef CONFIG_MIPS_JAZZ +OBJS += jazzdma.o +endif + +ifdef CONFIG_ACER_PICA_61 +OBJS += pica.o +endif + +ifdef CONFIG_DESKSTATION_TYNE +OBJS += tyne.o +endif + +ifdef CONFIG_MIPS_MAGNUM_4000 +OBJS += magnum4000.o +endif + +# +# CPU model specific code +# +ifdef CONFIG_CPU_R4X00 +OBJS += r4xx0.o +endif + +ifdef CONFIG_CPU_R4600 +OBJS += r4xx0.o +endif + +all: kernel.o head.o + +entry.o: entry.S + +head.o: head.S + +magnum4000.o: magnum4000.S + +pica.o: pica.S + +r4xx0.o: r4xx0.S + +tyne.o: tyne.S + +kernel.o: $(OBJS) + $(LD) -r -o kernel.o $(OBJS) + sync + +dep: + $(CPP) -M *.[cS] > .depend + +modules: + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/arch/mips/kernel/bios32.c b/arch/mips/kernel/bios32.c new file mode 100644 index 000000000..1fe61faa0 --- /dev/null +++ b/arch/mips/kernel/bios32.c @@ -0,0 +1,7 @@ +/* + * bios 32 replacement + */ +unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) +{ + return memory_start; +} diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S new file mode 100644 index 000000000..787e2bbf4 --- /dev/null +++ b/arch/mips/kernel/entry.S @@ -0,0 +1,625 @@ +/* + * arch/mips/kernel/entry.S + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * written by Ralf Baechle and Andreas Busse + */ + +/* + * entry.S contains the system-call and fault low-level handling routines. + * This also contains the timer-interrupt handler, as well as all interrupts + * and faults that can result in a task-switch. The ISA dependend TLB + * code is in arch/mips/kernel/tlb.S + */ + +#include <linux/sys.h> + +#include <asm/asm.h> +#include <asm/errno.h> +#include <asm/segment.h> +#include <asm/mipsregs.h> +#include <asm/mipsconfig.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/stackframe.h> +#include <asm/processor.h> + +/* + * These are offsets into the task-struct. + */ +state = 0 +counter = 4 +priority = 8 +signal = 12 +blocked = 16 +flags = 20 +errno = 24 +exec_domain = 60 + + .text + .set noreorder + .align 4 +handle_bottom_half: + lui s0,%hi(intr_count) + lw s1,%lo(intr_count)(s0) + mfc0 s3,CP0_STATUS # Enable IRQs + addiu s2,s1,1 + sw s2,%lo(intr_count)(s0) + ori t0,s3,0x1f + xori t0,0x1e + jal do_bottom_half + mtc0 t0,CP0_STATUS # delay slot + mtc0 s3,CP0_STATUS # Restore old IRQ state + j 9f + sw s1,%lo(intr_count)(s0) # delay slot + +reschedule: + lui ra,%hi(ret_from_sys_call) + j schedule + addiu ra,%lo(ret_from_sys_call) # delay slot + + .align 5 + NESTED(handle_sys, FR_SIZE, sp) + .set noat + SAVE_ALL + STI + .set at + /* + * Compute return address. We assume that syscalls never + * appear in delay slots. For the Linux/MIPS libc this + * assumption is always true. + */ + lw t3,FR_EPC(sp) + lw s1,FR_REG2(sp) + li t0,-ENOSYS + addiu t3,4 + sw t3,FR_EPC(sp) + li t2,NR_syscalls + bge s1,t2,ret_from_sys_call + sw t0,FR_REG2(sp) # delay slot + sll s1,PTRLOG + lw s1,sys_call_table(s1) + lw s0,current + + beqz s1,ret_from_sys_call + lw t0,flags(s0) # delay slot + sll t0,26 # PF_TRACESYS + bltz t0,1f + sw zero,errno(s0) # delay slot + +#if 0 + lw t0,FR_ORIG_REG2(sp) + beq t0,4,1f + nop + la t0,sys_call_names + lw t1,FR_ORIG_REG2(sp) + sll t1,2 + addu t0,t1 + lw a1,(t0) + PRINT("%s(") + lw a1,FR_REG4(sp) + lw a2,FR_REG5(sp) + lw a3,FR_REG6(sp) + PRINT("%08lx, %08lx, %08lx, ") + lw a1,FR_REG7(sp) + lw a2,FR_EPC(sp) + lw a3,FR_REG31(sp) + PRINT("%08lx) epc %08lx ra %08lx ") +1: +#endif + lw a0,FR_REG4(sp) + lw a1,FR_REG5(sp) + lw a2,FR_REG6(sp) + lw a3,FR_REG7(sp) + lw t0,FR_REG3(sp) + jalr s1 # do the real work + sw t0,PTRSIZE*4(sp) # delay slot + +#if 0 + lw t0,FR_ORIG_REG2(sp) + beq t0,4,1f + nop + sw v0,xxx + lw a1,xxx + PRINT("res %08lx\n") + lw v0,xxx + .data +xxx: .word 0 + .text +1: +#endif + + lw t0,errno(s0) + sw v0,FR_REG2(sp) # save return value + subu t0,zero,t0 + beqz t0,ret_from_sys_call + nop # delay slot + /* + * Fixme: should set error flag + */ + j ret_from_sys_call + sw t0,FR_REG2(sp) # delay slot + + .align 4 +1: jal syscall_trace + nop # delay slot + + lw a0,FR_REG4(sp) + lw a1,FR_REG5(sp) + lw a2,FR_REG6(sp) + lw a3,FR_REG7(sp) + lw t0,FR_REG3(sp) + jalr s1 # do the real work + sw t0,PTRSIZE*4(sp) # delay slot + + lw t0,errno(s0) + sw v0,FR_REG2(sp) + subu t0,zero,t0 # delay slot + beqz t0,1f + nop # delay slot + /* + * Fixme: should set error flag + */ +1: jal syscall_trace + sw t0,FR_REG2(sp) # delay slot + + .align 4 + .globl ret_from_sys_call +ret_from_sys_call: + lw t0,intr_count # bottom half + bnez t0,return +9: + lw t0,bh_mask # delay slot + lw t1,bh_active # unused delay slot + and t0,t1 + bnez t0,handle_bottom_half + + lw t0,FR_STATUS(sp) # returning to kernel mode? + andi t1,t0,0x10 + beqz t1,return # -> yes + + mfc0 t0,CP0_STATUS # delay slot + lw t1,need_resched + ori t0,0x1f # enable irqs + xori t0,0x1e + bnez t1,reschedule + mtc0 t0,CP0_STATUS # delay slot + + lw s0,current + lw t0,task + lw t1,state(s0) # state + beq s0,t0,return # task[0] cannot have signals + lw t0,counter(s0) # counter + bnez t1,reschedule # state == 0 ? + lw a0,blocked(s0) + # save blocked in a0 for + # signal handling + beqz t0,reschedule # counter == 0 ? + lw t0,signal(s0) + nor t1,zero,a0 + and t1,t0,t1 + beqz t1,return + nop + + jal do_signal + move a1,sp # delay slot + + .set noat + .globl return +return: RESTORE_ALL + ERET + .set at + END(handle_sys) + +/* + * Beware: interrupt, fast_interrupt and bad_interrupt have unusal + * calling conventions! + * + * t1 - interrupt number + * s2 - destroyed + * return values: + * v0 - return routine + */ + .text + .set at + .align 5 + NESTED(interrupt, FR_SIZE, sp) + move s2,ra + mfc0 t0,CP0_STATUS # enable IRQs + ori t0,0x1f + xori t0,0x1e + mtc0 t0,CP0_STATUS + move a0,t1 + jal do_IRQ + move a1,sp # delay slot + mfc0 t0,CP0_STATUS # disable IRQs + ori t0,1 + xori t0,1 + la v0,ret_from_sys_call + jr s2 + mtc0 t0,CP0_STATUS # delay slot + END(interrupt) + + .align 5 + NESTED(fast_interrupt, FR_SIZE, sp) + move s2,ra + move a0,t1 + jal do_fast_IRQ + move a1,sp # delay slot + lui v0,%hi(return) + jr s2 + addiu v0,%lo(return) # delay slot + END(fast_interrupt) + + LEAF(bad_interrupt) + /* + * Don't return & unblock the pic + */ + j return + nop + END(bad_interrupt) + + .align 5 + LEAF(spurious_interrupt) + /* + * Nothing happened... (whistle) + */ + lui t1,%hi(spurious_count) + lw t0,%lo(spurious_count)(t1) + la v0,return + addiu t0,1 + jr ra + sw t0,%lo(spurious_count)(t1) + END(spurious_interrupt) + + + +/* + * Build a default exception handler for the other R4x00 exceptions + */ +#define BUILD_HANDLER(exception) \ + .align 5; \ + NESTED(handle_##exception, FR_SIZE, sp); \ + .set noat; \ + SAVE_ALL; \ + STI; \ + .set at; \ + la a1,8f; \ + TEXT (#exception); \ + lw a2,FR_EPC(sp); \ + PRINT("Got %s at %08x.\n"); \ + li a0,0; \ + li t0,-1; /* not a sys call */ \ + sw t0,FR_ORIG_REG2(sp); \ + jal do_##exception; \ + move a0,sp; /* delay slot */ \ + j ret_from_sys_call; \ + nop; /* delay slot */ \ + END(handle_##exception) + + BUILD_HANDLER(adel) + BUILD_HANDLER(ades) + BUILD_HANDLER(ibe) + BUILD_HANDLER(dbe) + BUILD_HANDLER(ov) + BUILD_HANDLER(fpe) + BUILD_HANDLER(bp) + BUILD_HANDLER(tr) + BUILD_HANDLER(ri) + BUILD_HANDLER(cpu) + BUILD_HANDLER(vcei) + BUILD_HANDLER(vced) + BUILD_HANDLER(watch) + BUILD_HANDLER(reserved) + + +/* + * Exception handler table with 32 entries. + * This might be extended to handle software exceptions + */ + .bss + .align 2 + EXPORT(exception_handlers) + .fill 32,4,0 + +/* + * Table of syscalls + */ + .data + EXPORT(sys_call_table) + PTR sys_setup /* 0 */ + PTR sys_exit + PTR sys_fork + PTR sys_read + PTR sys_write + PTR sys_open /* 5 */ + PTR sys_close + PTR sys_waitpid + PTR sys_creat + PTR sys_link + PTR sys_unlink /* 10 */ + PTR sys_execve + PTR sys_chdir + PTR sys_time + PTR sys_mknod + PTR sys_chmod /* 15 */ + PTR sys_chown + PTR sys_break + PTR sys_stat + PTR sys_lseek + PTR sys_getpid /* 20 */ + PTR sys_mount + PTR sys_umount + PTR sys_setuid + PTR sys_getuid + PTR sys_stime /* 25 */ + PTR sys_ptrace + PTR sys_alarm + PTR sys_fstat + PTR sys_pause + PTR sys_utime /* 30 */ + PTR sys_stty + PTR sys_gtty + PTR sys_access + PTR sys_nice + PTR sys_ftime /* 35 */ + PTR sys_sync + PTR sys_kill + PTR sys_rename + PTR sys_mkdir + PTR sys_rmdir /* 40 */ + PTR sys_dup + PTR sys_pipe + PTR sys_times + PTR sys_prof + PTR sys_brk /* 45 */ + PTR sys_setgid + PTR sys_getgid + PTR sys_signal + PTR sys_geteuid + PTR sys_getegid /* 50 */ + PTR sys_acct + PTR sys_phys + PTR sys_lock + PTR sys_ioctl + PTR sys_fcntl /* 55 */ + PTR sys_mpx + PTR sys_setpgid + PTR sys_ulimit + PTR sys_olduname + PTR sys_umask /* 60 */ + PTR sys_chroot + PTR sys_ustat + PTR sys_dup2 + PTR sys_getppid + PTR sys_getpgrp /* 65 */ + PTR sys_setsid + PTR sys_sigaction + PTR sys_sgetmask + PTR sys_ssetmask + PTR sys_setreuid /* 70 */ + PTR sys_setregid + PTR sys_sigsuspend + PTR sys_sigpending + PTR sys_sethostname + PTR sys_setrlimit /* 75 */ + PTR sys_getrlimit + PTR sys_getrusage + PTR sys_gettimeofday + PTR sys_settimeofday + PTR sys_getgroups /* 80 */ + PTR sys_setgroups + PTR sys_select + PTR sys_symlink + PTR sys_lstat + PTR sys_readlink /* 85 */ + PTR sys_uselib + PTR sys_swapon + PTR sys_reboot + PTR old_readdir + PTR sys_mmap /* 90 */ + PTR sys_munmap + PTR sys_truncate + PTR sys_ftruncate + PTR sys_fchmod + PTR sys_fchown /* 95 */ + PTR sys_getpriority + PTR sys_setpriority + PTR sys_profil + PTR sys_statfs + PTR sys_fstatfs /* 100 */ + PTR sys_ioperm + PTR sys_socketcall + PTR sys_syslog + PTR sys_setitimer + PTR sys_getitimer /* 105 */ + PTR sys_newstat + PTR sys_newlstat + PTR sys_newfstat + PTR sys_uname + PTR sys_iopl /* 110 */ + PTR sys_vhangup + PTR sys_idle + PTR sys_vm86 + PTR sys_wait4 + PTR sys_swapoff /* 115 */ + PTR sys_sysinfo + PTR sys_ipc + PTR sys_fsync + PTR sys_sigreturn + PTR sys_clone /* 120 */ + PTR sys_setdomainname + PTR sys_newuname + PTR 0 #sys_modify_ldt + PTR sys_adjtimex + PTR sys_mprotect /* 125 */ + PTR sys_sigprocmask + PTR sys_create_module + PTR sys_init_module + PTR sys_delete_module + PTR sys_get_kernel_syms /* 130 */ + PTR sys_quotactl + PTR sys_getpgid + PTR sys_fchdir + PTR sys_bdflush + PTR sys_sysfs /* 135 */ + PTR sys_personality + PTR 0 /* for afs_syscall */ + PTR sys_setfsuid + PTR sys_setfsgid + PTR sys_llseek /* 140 */ + PTR sys_getdents + PTR sys_select + PTR sys_flock + .space (NR_syscalls-140)*4 + + .bss + EXPORT(IRQ_vectors) + .fill 16,4,0 + + .text +sys_call_names: + TTABLE ("setup") + TTABLE ("exit") + TTABLE ("fork") + TTABLE ("read") + TTABLE ("write") + TTABLE ("open") + TTABLE ("close") + TTABLE ("waitpid") + TTABLE ("creat") + TTABLE ("link") + TTABLE ("unlink") + TTABLE ("execve") + TTABLE ("chdir") + TTABLE ("time") + TTABLE ("mknod") + TTABLE ("chmod") + TTABLE ("chown") + TTABLE ("break") + TTABLE ("stat") + TTABLE ("lseek") + TTABLE ("getpid") + TTABLE ("mount") + TTABLE ("umount") + TTABLE ("setuid") + TTABLE ("getuid") + TTABLE ("stime") + TTABLE ("ptrace") + TTABLE ("alarm") + TTABLE ("fstat") + TTABLE ("pause") + TTABLE ("utime") + TTABLE ("stty") + TTABLE ("gtty") + TTABLE ("access") + TTABLE ("nice") + TTABLE ("ftime") + TTABLE ("sync") + TTABLE ("kill") + TTABLE ("rename") + TTABLE ("mkdir") + TTABLE ("rmdir") + TTABLE ("dup") + TTABLE ("pipe") + TTABLE ("times") + TTABLE ("prof") + TTABLE ("brk") + TTABLE ("setgid") + TTABLE ("getgid") + TTABLE ("signal") + TTABLE ("geteuid") + TTABLE ("getegid") + TTABLE ("acct") + TTABLE ("phys") + TTABLE ("lock") + TTABLE ("ioctl") + TTABLE ("fcntl") + TTABLE ("mpx") + TTABLE ("setpgid") + TTABLE ("ulimit") + TTABLE ("olduname") + TTABLE ("umask") + TTABLE ("chroot") + TTABLE ("ustat") + TTABLE ("dup2") + TTABLE ("getppid") + TTABLE ("getpgrp") + TTABLE ("setsid") + TTABLE ("sigaction") + TTABLE ("sgetmask") + TTABLE ("ssetmask") + TTABLE ("setreuid") + TTABLE ("setregid") + TTABLE ("sigsuspend") + TTABLE ("sigpending") + TTABLE ("sethostname") + TTABLE ("setrlimit") + TTABLE ("getrlimit") + TTABLE ("getrusage") + TTABLE ("gettimeofday") + TTABLE ("settimeofday") + TTABLE ("getgroups") + TTABLE ("setgroups") + TTABLE ("select") + TTABLE ("symlink") + TTABLE ("lstat") + TTABLE ("readlink") + TTABLE ("uselib") + TTABLE ("swapon") + TTABLE ("reboot") + TTABLE ("readdir") + TTABLE ("mmap") + TTABLE ("munmap") + TTABLE ("truncate") + TTABLE ("ftruncate") + TTABLE ("fchmod") + TTABLE ("fchown") + TTABLE ("getpriority") + TTABLE ("setpriority") + TTABLE ("profil") + TTABLE ("statfs") + TTABLE ("fstatfs") + TTABLE ("ioperm") + TTABLE ("socketcall") + TTABLE ("syslog") + TTABLE ("setitimer") + TTABLE ("getitimer") + TTABLE ("newstat") + TTABLE ("newlstat") + TTABLE ("newfstat") + TTABLE ("uname") + TTABLE ("iopl") + TTABLE ("vhangup") + TTABLE ("idle") + TTABLE ("vm86") + TTABLE ("wait4") + TTABLE ("swapoff") + TTABLE ("sysinfo") + TTABLE ("ipc") + TTABLE ("fsync") + TTABLE ("sigreturn") + TTABLE ("clone") + TTABLE ("setdomainname") + TTABLE ("newuname") + TTABLE ("modify_ldt (unused)") + TTABLE ("adjtimex") + TTABLE ("mprotect") + TTABLE ("sigprocmask") + TTABLE ("create_module") + TTABLE ("init_module") + TTABLE ("delete_module") + TTABLE ("get_kernel_syms") + TTABLE ("quotactl") + TTABLE ("getpgid") + TTABLE ("fchdir") + TTABLE ("bdflush") + TTABLE ("sysfs") + TTABLE ("personality") + TTABLE ("afs_syscall") /* for afs_syscall */ + TTABLE ("setfsuid") + TTABLE ("setfsgid") + TTABLE ("llseek") + TTABLE ("sys_getdents") + TTABLE ("sys_select") + TTABLE ("sys_flock") diff --git a/arch/mips/kernel/gdb-low.S b/arch/mips/kernel/gdb-low.S new file mode 100644 index 000000000..ea775e732 --- /dev/null +++ b/arch/mips/kernel/gdb-low.S @@ -0,0 +1,300 @@ +/* + * arch/mips/kernel/gdb-low.S + * + * gdb-low.S contains the low-level trap handler for the GDB stub. + * + * Copyright (C) 1995 Andreas Busse + */ + +#include <linux/sys.h> + +#include <asm/asm.h> +#include <asm/segment.h> +#include <asm/mipsregs.h> +#include <asm/mipsconfig.h> +#include <asm/stackframe.h> +#include <asm/gdb-stub.h> + +/* + * The low level trap handler + */ + .align 5 + NESTED(trap_low, GDB_FR_SIZE, sp) + .set noat + .set noreorder + + mfc0 k0,CP0_STATUS + sll k0,3 /* extract cu0 bit */ + bltz k0,1f + move k1,sp + + /* + * Called from user mode, new stack + */ + lui k1,%hi(kernelsp) + lw k1,%lo(kernelsp)(k1) +1: move k0,sp + subu sp,k1,GDB_FR_SIZE + sw k0,GDB_FR_REG29(sp) + sw v0,GDB_FR_REG2(sp) + +/* + * first save the CP0 and special registers + */ + + mfc0 v0,CP0_STATUS + sw v0,GDB_FR_STATUS(sp) + mfc0 v0,CP0_CAUSE + sw v0,GDB_FR_CAUSE(sp) + mfc0 v0,CP0_EPC + sw v0,GDB_FR_EPC(sp) + mfc0 v0,CP0_BADVADDR + sw v0,GDB_FR_BADVADDR(sp) + mfhi v0 + sw v0,GDB_FR_HI(sp) + mflo v0 + sw v0,GDB_FR_LO(sp) + +/* + * Now the integer registers + */ + + sw zero,GDB_FR_REG0(sp) /* I know... */ + sw $1,GDB_FR_REG1(sp) + /* v0 already saved */ + sw v1,GDB_FR_REG3(sp) + sw a0,GDB_FR_REG4(sp) + sw a1,GDB_FR_REG5(sp) + sw a2,GDB_FR_REG6(sp) + sw a3,GDB_FR_REG7(sp) + sw t0,GDB_FR_REG8(sp) + sw t1,GDB_FR_REG9(sp) + sw t2,GDB_FR_REG10(sp) + sw t3,GDB_FR_REG11(sp) + sw t4,GDB_FR_REG12(sp) + sw t5,GDB_FR_REG13(sp) + sw t6,GDB_FR_REG14(sp) + sw t7,GDB_FR_REG15(sp) + sw s0,GDB_FR_REG16(sp) + sw s1,GDB_FR_REG17(sp) + sw s2,GDB_FR_REG18(sp) + sw s3,GDB_FR_REG19(sp) + sw s4,GDB_FR_REG20(sp) + sw s5,GDB_FR_REG21(sp) + sw s6,GDB_FR_REG22(sp) + sw s7,GDB_FR_REG23(sp) + sw t8,GDB_FR_REG24(sp) + sw t9,GDB_FR_REG25(sp) + sw k0,GDB_FR_REG26(sp) + sw k1,GDB_FR_REG27(sp) + sw gp,GDB_FR_REG28(sp) + /* sp already saved */ + sw fp,GDB_FR_REG30(sp) + sw ra,GDB_FR_REG31(sp) + + STI /* disable interrupts */ + +/* + * Followed by the floating point registers + */ + mfc0 v0,CP0_STATUS /* check if the FPU is enabled */ + srl v0,v0,16 + andi v0,v0,(ST0_CU1 >> 16) + beqz v0,2f /* disabled, skip */ + nop + + swc1 $0,GDB_FR_FPR0(sp) + swc1 $1,GDB_FR_FPR1(sp) + swc1 $2,GDB_FR_FPR2(sp) + swc1 $3,GDB_FR_FPR3(sp) + swc1 $4,GDB_FR_FPR4(sp) + swc1 $5,GDB_FR_FPR5(sp) + swc1 $6,GDB_FR_FPR6(sp) + swc1 $7,GDB_FR_FPR7(sp) + swc1 $8,GDB_FR_FPR8(sp) + swc1 $9,GDB_FR_FPR9(sp) + swc1 $10,GDB_FR_FPR10(sp) + swc1 $11,GDB_FR_FPR11(sp) + swc1 $12,GDB_FR_FPR12(sp) + swc1 $13,GDB_FR_FPR13(sp) + swc1 $14,GDB_FR_FPR14(sp) + swc1 $15,GDB_FR_FPR15(sp) + swc1 $16,GDB_FR_FPR16(sp) + swc1 $17,GDB_FR_FPR17(sp) + swc1 $18,GDB_FR_FPR18(sp) + swc1 $19,GDB_FR_FPR19(sp) + swc1 $20,GDB_FR_FPR20(sp) + swc1 $21,GDB_FR_FPR21(sp) + swc1 $22,GDB_FR_FPR22(sp) + swc1 $23,GDB_FR_FPR23(sp) + swc1 $24,GDB_FR_FPR24(sp) + swc1 $25,GDB_FR_FPR25(sp) + swc1 $26,GDB_FR_FPR26(sp) + swc1 $27,GDB_FR_FPR27(sp) + swc1 $28,GDB_FR_FPR28(sp) + swc1 $29,GDB_FR_FPR29(sp) + swc1 $30,GDB_FR_FPR30(sp) + swc1 $31,GDB_FR_FPR31(sp) + +/* + * FPU control registers + */ + + mfc1 v0,CP1_STATUS + sw v0,GDB_FR_FSR(sp) + mfc1 v0,CP1_REVISION + sw v0,GDB_FR_FIR(sp) + +/* + * current stack frame ptr + */ + +2: sw sp,GDB_FR_FRP(sp) + +/* + * CP0 registers (R4000/R4400 unused registers skipped) + */ + + mfc0 v0,CP0_INDEX + sw v0,GDB_FR_CP0_INDEX(sp) + mfc0 v0,CP0_RANDOM + sw v0,GDB_FR_CP0_RANDOM(sp) + mfc0 v0,CP0_ENTRYLO0 + sw v0,GDB_FR_CP0_ENTRYLO0(sp) + mfc0 v0,CP0_ENTRYLO1 + sw v0,GDB_FR_CP0_ENTRYLO1(sp) + mfc0 v0,CP0_PAGEMASK + sw v0,GDB_FR_CP0_PAGEMASK(sp) + mfc0 v0,CP0_WIRED + sw v0,GDB_FR_CP0_WIRED(sp) + mfc0 v0,CP0_ENTRYHI + sw v0,GDB_FR_CP0_ENTRYHI(sp) + mfc0 v0,CP0_PRID + sw v0,GDB_FR_CP0_PRID(sp) + + .set at + +/* + * continue with the higher level handler + */ + + move a0,sp + jal handle_exception + nop + +/* + * restore all writable registers, in reverse order + */ + + .set noat + + lw v0,GDB_FR_CP0_ENTRYHI(sp) + lw v1,GDB_FR_CP0_WIRED(sp) + mtc0 v0,CP0_ENTRYHI + mtc0 v1,CP0_WIRED + lw v0,GDB_FR_CP0_PAGEMASK(sp) + lw v1,GDB_FR_CP0_ENTRYLO1(sp) + mtc0 v0,CP0_PAGEMASK + mtc0 v1,CP0_ENTRYLO1 + lw v0,GDB_FR_CP0_ENTRYLO0(sp) + lw v1,GDB_FR_CP0_INDEX(sp) + mtc0 v0,CP0_ENTRYLO0 + mtc0 v1,CP0_INDEX + +/* + * Next, the floating point registers + */ + mfc0 v0,CP0_STATUS /* check if the FPU is enabled */ + srl v0,v0,16 + andi v0,v0,(ST0_CU1 >> 16) + beqz v0,3f /* disabled, skip */ + nop + + lwc1 $31,GDB_FR_FPR31(sp) + lwc1 $30,GDB_FR_FPR30(sp) + lwc1 $29,GDB_FR_FPR29(sp) + lwc1 $28,GDB_FR_FPR28(sp) + lwc1 $27,GDB_FR_FPR27(sp) + lwc1 $26,GDB_FR_FPR26(sp) + lwc1 $25,GDB_FR_FPR25(sp) + lwc1 $24,GDB_FR_FPR24(sp) + lwc1 $23,GDB_FR_FPR23(sp) + lwc1 $22,GDB_FR_FPR22(sp) + lwc1 $21,GDB_FR_FPR21(sp) + lwc1 $20,GDB_FR_FPR20(sp) + lwc1 $19,GDB_FR_FPR19(sp) + lwc1 $18,GDB_FR_FPR18(sp) + lwc1 $17,GDB_FR_FPR17(sp) + lwc1 $16,GDB_FR_FPR16(sp) + lwc1 $15,GDB_FR_FPR15(sp) + lwc1 $14,GDB_FR_FPR14(sp) + lwc1 $13,GDB_FR_FPR13(sp) + lwc1 $12,GDB_FR_FPR12(sp) + lwc1 $11,GDB_FR_FPR11(sp) + lwc1 $10,GDB_FR_FPR10(sp) + lwc1 $9,GDB_FR_FPR9(sp) + lwc1 $8,GDB_FR_FPR8(sp) + lwc1 $7,GDB_FR_FPR7(sp) + lwc1 $6,GDB_FR_FPR6(sp) + lwc1 $5,GDB_FR_FPR5(sp) + lwc1 $4,GDB_FR_FPR4(sp) + lwc1 $3,GDB_FR_FPR3(sp) + lwc1 $2,GDB_FR_FPR2(sp) + lwc1 $1,GDB_FR_FPR1(sp) + lwc1 $0,GDB_FR_FPR0(sp) + +/* + * Now the CP0 and integer registers + */ + +3: mfc0 t0,CP0_STATUS + ori t0,0x1f + xori t0,0x1f + mtc0 t0,CP0_STATUS + + lw v0,GDB_FR_STATUS(sp) + lw v1,GDB_FR_EPC(sp) + mtc0 v0,CP0_STATUS + mtc0 v1,CP0_EPC + lw v0,GDB_FR_HI(sp) + lw v1,GDB_FR_LO(sp) + mthi v0 + mtlo v0 + lw ra,GDB_FR_REG31(sp) + lw fp,GDB_FR_REG30(sp) + lw gp,GDB_FR_REG28(sp) + lw k1,GDB_FR_REG27(sp) + lw k0,GDB_FR_REG26(sp) + lw t9,GDB_FR_REG25(sp) + lw t8,GDB_FR_REG24(sp) + lw s7,GDB_FR_REG23(sp) + lw s6,GDB_FR_REG22(sp) + lw s5,GDB_FR_REG21(sp) + lw s4,GDB_FR_REG20(sp) + lw s3,GDB_FR_REG19(sp) + lw s2,GDB_FR_REG18(sp) + lw s1,GDB_FR_REG17(sp) + lw s0,GDB_FR_REG16(sp) + lw t7,GDB_FR_REG15(sp) + lw t6,GDB_FR_REG14(sp) + lw t5,GDB_FR_REG13(sp) + lw t4,GDB_FR_REG12(sp) + lw t3,GDB_FR_REG11(sp) + lw t2,GDB_FR_REG10(sp) + lw t1,GDB_FR_REG9(sp) + lw t0,GDB_FR_REG8(sp) + lw a3,GDB_FR_REG7(sp) + lw a2,GDB_FR_REG6(sp) + lw a1,GDB_FR_REG5(sp) + lw a0,GDB_FR_REG4(sp) + lw v1,GDB_FR_REG3(sp) + lw v0,GDB_FR_REG2(sp) + lw $1,GDB_FR_REG1(sp) + lw sp,GDB_FR_REG29(sp) /* Deallocate stack */ + + ERET + .set at + .set reorder + END(trap_low) + +/* end of file gdb-low.S */ diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c new file mode 100644 index 000000000..7708feac0 --- /dev/null +++ b/arch/mips/kernel/gdb-stub.c @@ -0,0 +1,748 @@ +/* + * arch/mips/kernel/gdb-stub.c + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + +#include <linux/string.h> +#include <linux/signal.h> +#include <linux/kernel.h> + +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/segment.h> +#include <asm/cachectl.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> + +/* + * external low-level support routines + */ + +extern int putDebugChar(char c); /* write a single character */ +extern char getDebugChar(void); /* read and return a single char */ +extern void fltr_set_mem_err(void); +extern void trap_low(void); + +/* + * breakpoint and test functions + */ +extern void breakpoint(void); +extern void breakinst(void); +extern void adel(void); + +/* + * local prototypes + */ + +static void getpacket(char *buffer); +static void putpacket(char *buffer); +static void set_mem_fault_trap(int enable); +static int computeSignal(int tt); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault); +void handle_exception(struct gdb_regs *regs); +static void show_gdbregs(struct gdb_regs *regs); + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +static int initialized = 0; /* !0 means we've been initialized */ +static const char hexchars[]="0123456789abcdef"; + + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* + * wait around for the start character, + * ignore all other characters + */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * send the packet in buffer. + */ +static void putpacket(char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* + * $<packet info>#<checksum>. + */ + + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + if (!(putDebugChar(ch))) + return; + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + + } + while ((getDebugChar() & 0x7f) != '+'); +} + + +/* + * Indicate to caller of mem2hex or hex2mem that there + * has been an error. + */ +static volatile int mem_err = 0; + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * If MAY_FAULT is non-zero, then we will handle memory faults by returning + * a 0, else treat a fault like any other fault in the stub. + */ +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault) +{ + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + while (count-- > 0) { + ch = *(mem++); + if (mem_err) + return 0; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + +/* set_mem_fault_trap(0); */ + + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + */ +static char *hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + for (i=0; i<count; i++) + { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + *(mem++) = ch; + if (mem_err) + return 0; + } + +/* set_mem_fault_trap(0); */ + + return mem; +} + +/* + * This table contains the mapping between SPARC hardware trap types, and + * signals, which are primarily what GDB understands. It also indicates + * which hardware traps we need to commandeer when initializing the stub. + */ +static struct hard_trap_info +{ + unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 4, SIGBUS }, /* address error (load) */ + { 5, SIGBUS }, /* address error (store) */ + { 6, SIGBUS }, /* instruction bus error */ + { 7, SIGBUS }, /* data bus error */ + { 9, SIGTRAP }, /* break */ + { 10, SIGILL }, /* reserved instruction */ +/* { 11, SIGILL }, */ /* cpu unusable */ + { 12, SIGFPE }, /* overflow */ + { 13, SIGTRAP }, /* trap */ + { 14, SIGSEGV }, /* virtual instruction cache coherency */ + { 15, SIGFPE }, /* floating point exception */ + { 23, SIGSEGV }, /* watch */ + { 31, SIGSEGV }, /* virtual data cache coherency */ + { 0, 0} /* Must be last */ +}; + + +/* + * Set up exception handlers for tracing and breakpoints + */ +void set_debug_traps(void) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + set_except_vector(ht->tt, trap_low); + + /* + * In case GDB is started before us, ack any packets + * (presumably "$?#xx") sitting there. + */ + + putDebugChar ('+'); + initialized = 1; + + breakpoint(); +} + + +/* + * Trap handler for memory errors. This just sets mem_err to be non-zero. It + * assumes that %l1 is non-zero. This should be safe, as it is doubtful that + * 0 would ever contain code that could mem fault. This routine will skip + * past the faulting instruction after setting mem_err. + */ +extern void fltr_set_mem_err(void) +{ + /* FIXME: Needs to be written... */ +} + + +static void set_mem_fault_trap(int enable) +{ + mem_err = 0; + +#if 0 + if (enable) + exceptionHandler(9, fltr_set_mem_err); + else + exceptionHandler(9, trap_low); +#endif +} + +/* + * Convert the MIPS hardware trap type code to a unix signal number. + */ +static int computeSignal(int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return (numChars); +} + +/* + * This function does all command procesing for interfacing to gdb. It + * returns 1 if you should skip the instruction at the trap address, 0 + * otherwise. + */ +void handle_exception (struct gdb_regs *regs) +{ + int trap; /* Trap type */ + int sigval; + int addr; + int length; + char *ptr; + unsigned long *stack; + +#if 0 + printk("in handle_exception()\n"); + show_gdbregs(regs); +#endif + + /* + * First check trap type. If this is CPU_UNUSABLE and CPU_ID is 1, + * the simply switch the FPU on and return since this is no error + * condition. kernel/traps.c does the same. + * FIXME: This doesn't work yet, so we don't catch CPU_UNUSABLE + * traps for now. + */ + trap = (regs->cp0_cause & 0x7c) >> 2; +/* printk("trap=%d\n",trap); */ + if (trap == 11) { + if (((regs->cp0_cause >> CAUSEB_CE) & 3) == 1) { + regs->cp0_status |= ST0_CU1; + return; + } + } + + /* + * If we're in breakpoint() increment the PC + */ + if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst) + regs->cp0_epc += 4; + + stack = (long *)regs->reg29; /* stack ptr */ + sigval = computeSignal(trap); + + /* + * reply to host that an exception has occurred + */ + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + /* + * Send Error PC + */ + *ptr++ = hexchars[REG_EPC >> 4]; + *ptr++ = hexchars[REG_EPC & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->cp0_epc, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + *ptr++ = hexchars[REG_FP >> 4]; + *ptr++ = hexchars[REG_FP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->reg30, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + *ptr++ = hexchars[REG_SP >> 4]; + *ptr++ = hexchars[REG_SP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->reg29, ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ + + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) + { + case '?': + output_buffer[0] = 'S'; + output_buffer[1] = hexchars[sigval >> 4]; + output_buffer[2] = hexchars[sigval & 0xf]; + output_buffer[3] = 0; + break; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + ptr = output_buffer; + ptr = mem2hex((char *)®s->reg0, ptr, 32*4, 0); /* r0...r31 */ + ptr = mem2hex((char *)®s->cp0_status, ptr, 6*4, 0); /* cp0 */ + ptr = mem2hex((char *)®s->fpr0, ptr, 32*4, 0); /* f0...31 */ + ptr = mem2hex((char *)®s->cp1_fsr, ptr, 2*4, 0); /* cp1 */ + ptr = mem2hex((char *)®s->frame_ptr, ptr, 2*4, 0); /* frp */ + ptr = mem2hex((char *)®s->cp0_index, ptr, 16*4, 0); /* cp0 */ + break; + + /* + * set the value of the CPU registers - return OK + * FIXME: Needs to be written + */ + case 'G': + { +#if 0 + unsigned long *newsp, psr; + + ptr = &input_buffer[1]; + hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */ + + /* + * See if the stack pointer has moved. If so, then copy the + * saved locals and ins to the new location. + */ + + newsp = (unsigned long *)registers[SP]; + if (sp != newsp) + sp = memcpy(newsp, sp, 16 * 4); + +#endif + strcpy(output_buffer,"OK"); + } + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, output_buffer, length, 1)) + break; + strcpy (output_buffer, "E03"); + } else + strcpy(output_buffer,"E01"); + break; + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length, 1)) + strcpy(output_buffer, "OK"); + else + strcpy(output_buffer, "E03"); + } + else + strcpy(output_buffer, "E02"); + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + regs->cp0_epc = addr; + + /* + * Need to flush the instruction cache here, as we may + * have deposited a breakpoint, and the icache probably + * has no way of knowing that a data ref to some location + * may have changed something that is in the instruction + * cache. + * NB: We flush both caches, just to be sure... + */ + + sys_cacheflush((void *)KSEG0,KSEG1-KSEG0,BCACHE); + return; + /* NOTREACHED */ + break; + + + /* + * kill the program + */ + case 'k' : + break; /* do nothing */ + + + /* + * Reset the whole machine (FIXME: system dependent) + */ + case 'r': + break; + + + /* + * Step to next instruction + * FIXME: Needs to be written + */ + case 's': + strcpy (output_buffer, "S01"); + break; + + /* + * Set baud rate (bBB) + * FIXME: Needs to be written + */ + case 'b': + { +#if 0 + int baudrate; + extern void set_timer_3(); + + ptr = &input_buffer[1]; + if (!hexToInt(&ptr, &baudrate)) + { + strcpy(output_buffer,"B01"); + break; + } + + /* Convert baud rate to uart clock divider */ + + switch (baudrate) + { + case 38400: + baudrate = 16; + break; + case 19200: + baudrate = 33; + break; + case 9600: + baudrate = 65; + break; + default: + baudrate = 0; + strcpy(output_buffer,"B02"); + goto x1; + } + + if (baudrate) { + putpacket("OK"); /* Ack before changing speed */ + set_timer_3(baudrate); /* Set it */ + } +#endif + } + break; + + } /* switch */ + + /* + * reply to the request + */ + + putpacket(output_buffer); + + } /* while */ +} + +/* + * This function will generate a breakpoint exception. It is used at the + * beginning of a program to sync up with a debugger and can be used + * otherwise as a quick means to stop program execution and "break" into + * the debugger. + */ +void breakpoint(void) +{ + if (!initialized) + return; + + __asm__ __volatile__(" + .globl breakinst + .set noreorder + nop +breakinst: break + nop + .set reorder + "); +} + +void adel(void) +{ + __asm__ __volatile__(" + .globl adel + la $8,0x80000001 + lw $9,0($8) + "); +} + +/* + * Print registers (on target console) + * Used only to debug the stub... + */ +void show_gdbregs(struct gdb_regs * regs) +{ + /* + * Saved main processor registers + */ + printk("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg0, regs->reg1, regs->reg2, regs->reg3, + regs->reg4, regs->reg5, regs->reg6, regs->reg7); + printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg8, regs->reg9, regs->reg10, regs->reg11, + regs->reg12, regs->reg13, regs->reg14, regs->reg15); + printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg16, regs->reg17, regs->reg18, regs->reg19, + regs->reg20, regs->reg21, regs->reg22, regs->reg23); + printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg24, regs->reg25, regs->reg26, regs->reg27, + regs->reg28, regs->reg29, regs->reg30, regs->reg31); + + /* + * Saved cp0 registers + */ + printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n", + regs->cp0_epc, regs->cp0_status, regs->cp0_cause); +} diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S new file mode 100644 index 000000000..a2cb43de3 --- /dev/null +++ b/arch/mips/kernel/head.S @@ -0,0 +1,412 @@ +/* + * arch/mips/kernel/head.S + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * Written by Ralf Baechle and Andreas Busse + * + * Head.S contains the MIPS exception handler and startup code. + */ +#include <linux/tasks.h> + +#include <asm/asm.h> +#include <asm/segment.h> +#include <asm/cachectl.h> +#include <asm/mipsregs.h> +#include <asm/mipsconfig.h> +#include <asm/stackframe.h> +#include <asm/bootinfo.h> + +#define PAGE_SIZE 0x1000 + +#define MODE_GLOBAL 0x0001 /* shared for all processes */ +#define MODE_ALIAS 0x0016 /* uncachable */ + + .text + .set mips3 +/* + * This is space for the interrupt handlers. + * They are located at virtual address KSEG[01] (physical 0x0) + */ + /* + * TLB refill, EXL == 0 + */ + .set noreorder + .set noat + LEAF(except_vec0) + dmfc0 k1,CP0_CONTEXT + dsra k1,1 + lwu k0,(k1) # May cause another exception + lwu k1,4(k1) + dsrl k0,6 # Convert to EntryLo format + dsrl k1,6 # Convert to EntryLo format + dmtc0 k0,CP0_ENTRYLO0 + dmtc0 k1,CP0_ENTRYLO1 + nop # Needed for R4[04]00 pipeline + tlbwr + nop # Needed for R4[04]00 pipeline + nop + nop + eret + /* + * Workaround for R4000 bug. For explanation see MIPS + * docs. Note that this that obscure that it wont almost + * never happen. Well, but Mips writes about it's bugs. + */ + nop + eret + END(except_vec0) + + /* + * XTLB refill, EXL == 0 + * Should never be reached + */ + .org except_vec0+0x80 + LEAF(except_vec1) + PANIC("XTLB Refill exception.\n") +1: j 1b + nop + END(except_vec1) + + /* + * Cache Error + */ + .org except_vec1+0x80 + LEAF(except_vec2) + /* + * Famous last words: unreached + */ + mfc0 a1,CP0_ERROREPC + PRINT("Cache error exception: c0_errorepc == %08x\n") +1: j 1b + nop + END(except_vec2) + + /* + * General exception vector. + */ + .org except_vec2+0x80 + NESTED(except_vec3, 0, sp) + .set noat + /* + * Register saving is delayed as long as we don't know + * which registers really need to be saved. + */ + mfc0 k1,CP0_CAUSE + la k0,exception_handlers + /* + * Next lines assumes that the used CPU type has max. + * 32 different types of exceptions. We might use this + * to implement software exceptions in the future. + */ + andi k1,0x7c + addu k0,k1 + lw k0,(k0) + NOP + jr k0 + nop + END(except_vec3) + .set at + +/******************************************************************************/ + +/* + * Kernel entry + */ + .set noreorder + NESTED(kernel_entry, 16, sp) + /* + * Clear BSS first so that there are no surprises... + */ + la t0,_edata + la t1,_end + sw zero,(t0) +1: addiu t0,4 + bnel t0,t1,1b + sw zero,(t0) + + /* + * Initialize low level part of memory management + */ + jal tlbflush + mtc0 zero,CP0_WIRED # delay slot + jal wire_mappings + nop + jal tlbflush + nop + + /* + * Stack for kernel and init + */ + la sp,init_user_stack+PAGE_SIZE-24 + la t0,init_kernel_stack+PAGE_SIZE + sw t0,kernelsp + + /* + * Disable coprocessors + */ + mfc0 t0,CP0_STATUS + li t1,~(ST0_CU0|ST0_CU1|ST0_CU2|ST0_CU3) + and t0,t1 + mtc0 t0,CP0_STATUS + +1: jal start_kernel + nop # delay slot + /* + * Main should never return here, but + * just in case, we know what happens. + */ + b 1b + nop # delay slot + END(kernel_entry) + +/* + * wire_mappings - used to map hardware registers + */ + LEAF(wire_mappings) + /* + * Get base address of map0 table for the + * the board we're running on + */ + la t0,boot_info + lw t1,OFFSET_BOOTINFO_MACHTYPE(t0) + la t0,map0table + sll t1,PTRLOG # machtype used as index + addu t0,t1 + lw t0,(t0) # get base address + + /* + * Get number of wired TLB entries and + * loop over selected map0 table. + */ + lw t1,(t0) # number of wired TLB entries + move t2,zero # TLB entry counter + addiu t3,t1,1 # wire one additional entry + beqz t1,2f # null, exit + mtc0 t3,CP0_WIRED # delay slot + addiu t0,8 +1: lw t4,24(t0) # PageMask + ld t5,0(t0) # entryHi + ld t6,8(t0) # entryLo0 + ld t7,16(t0) # entryLo1 + addiu t2,1 # increment ctr + mtc0 t2,CP0_INDEX # set TLB entry + mtc0 t4,CP0_PAGEMASK + dmtc0 t5,CP0_ENTRYHI + dmtc0 t6,CP0_ENTRYLO0 + dmtc0 t7,CP0_ENTRYLO1 + addiu t0,32 + bne t1,t2,1b # next TLB entry + tlbwi # delay slot + + /* + * We use only 4k pages. Therefore the PageMask register + * is expected to be setup for 4k pages. + */ +2: li t0,PM_4K + mtc0 t0,CP0_PAGEMASK + + /* + * Now map the pagetables + */ + mtc0 zero,CP0_INDEX + la t0,TLB_ROOT + dmtc0 t0,CP0_ENTRYHI + la t0,swapper_pg_dir-KSEG1 + srl t0,6 + ori t0,(MODE_ALIAS|MODE_GLOBAL) # uncachable, dirty, valid + dmtc0 t0,CP0_ENTRYLO0 + li t0,MODE_GLOBAL + dmtc0 t0,CP0_ENTRYLO1 + nop + tlbwi # delayed + + /* + * Load the context register with a value that allows + * it to be used as fast as possible in tlb exceptions. + * It is expected that this register's content will + * NEVER be changed. + */ + li t0,TLBMAP + dsll t0,1 + dmtc0 t0,CP0_CONTEXT + jr ra # delay slot + nop + END(wire_mappings) + +/* + * Just for debugging... + */ + .set noreorder + LEAF(beep) + lw t0,beepflag + nop + bnez t0,1f + lbu t0,0xe2000061 + xori t0,3 + sb t0,0xe2000061 + li t0,1 + sw t0,beepflag +1: jr ra + nop + END(beep) + + .bss +beepflag: .word 0 + .text + +/* + * Compute kernel code checksum to check kernel code against corruption + */ + LEAF(csum) + jal sys_cacheflush + move t8,ra # delay slot + li t0,KSEG1 + la t1,final + li t2,KSEG1 + or t0,t2 + or t1,t2 + move v0,zero +1: lw t2,(t0) + addiu t0,4 + bne t0,t1,1b + xor v0,t2 + jr t8 + nop + END(csum) +final: + + .data +/* + * Build an entry for table of wired entries + */ +#define MAPDATA(q1,q2,q3,w1) \ + .quad q1; \ + .quad q2; \ + .quad q3; \ + .word w1; \ + .word 0 + +/* + * Initial mapping tables for supported Mips boards. + * First item is always the number of wired TLB entries, + * following by EntryHi/EntryLo pairs and page mask. + * Since everything must be quad-aligned (8) we insert + * some dummy zeros. + */ + +/* + * Address table of mapping tables for supported Mips boards. + * Add your own stuff here but don't forget to define your + * target system in bootinfo.h + */ + +map0table: PTR map0_dummy # machtype = unknown + PTR map0_rpc # Deskstation rPC44 + PTR map0_tyne # Deskstation Tyne + PTR map0_pica61 # Acer Pica-61 + PTR map0_magnum4000 # MIPS Magnum 4000PC (RC4030) + +map0_dummy: .word 0 # 0 entries + + .align 3 +/* + * Initial mappings for Deskstation rPC boards. + * RB: Untested goodie - I don't have such a board. + */ +map0_rpc: .word 2 # no. of wired TLB entries + .word 0 # pad for alignment + +MAPDATA(0xffffffffe0000000, 0x04020017, 0x00000001, PM_1M) # VESA DMA cache +MAPDATA(0xffffffffe2000000, 0x24000017, 0x04000017, PM_16M) # VESA I/O and memory space + +/* + * Initial mappings for Deskstation Tyne boards. + */ +map0_tyne: .word 2 # no. of wired TLB entries + .word 0 # pad for alignment + +MAPDATA(0xffffffffe0000000, 0x04020017, 0x00000001, PM_1M) # VESA DMA cache +MAPDATA(0xffffffffe2000000, 0x24000017, 0x04000017, PM_16M) # VESA I/O and memory space + +/* + * Initial mapping for ACER PICA-61 boards. + * FIXME: These are rather preliminary since many drivers, such as serial, + * parallel, scsi and ethernet need some changes to distinguish between "local" + * (built-in) and "optional" (ISA/PCI) I/O hardware. Local video ram is mapped + * to the same location as the bios maps it to. Console driver has been changed + * accordingly (new video type: VIDEO_TYPE_PICA_S3). + * FIXME: Remove or merge some of the mappings. + */ +map0_pica61: .word 7 # no. wired TLB entries + .word 0 # dummy + +MAPDATA(0xffffffffe0000000, 0x02000017, 0x00000001, PM_64K) # Local I/O space +MAPDATA(0xffffffffe0100000, 0x03c00017, 0x00000001, PM_4K) # Interrupt source register +MAPDATA(0xffffffffe0200000, 0x01800017, 0x01804017, PM_1M) # Local video control +MAPDATA(0xffffffffe0400000, 0x01808017, 0x0180c017, PM_1M) # Extended video control +MAPDATA(0xffffffffe0800000, 0x01000017, 0x01010017, PM_4M) # Local video memory (BIOS mapping) +MAPDATA(0xffffffffe2000000, 0x02400017, 0x02440017, PM_16M) # ISA I/O and ISA memory space (both 16M) +MAPDATA(0xffffffffffffe000, 0x00000001, 0x0001ffd7, PM_4K) # PCR (???) + +/* + * Initial mapping for Mips Magnum 4000PC systems. + * Do you believe me now that the Acer and Mips boxes are nearly the same ? :-) + * FIXME: Remove or merge some of the mappings. + */ + +map0_magnum4000: + .word 8 # no. wired TLB entries + .word 0 # dummy + +MAPDATA(0xffffffffe1000000, 0x03ffc013, 0x00000001, 0x7e000) # 0 +MAPDATA(0xffffffffe0000000, 0x02000017, 0x00000001, 0x1e000) # 1 local I/O +MAPDATA(0xffffffffe0100000, 0x03c00017, 0x00000001, 0) # 2 IRQ source +MAPDATA(0xffffffffe0200000, 0x01800017, 0x01804017, 0x1fe000) # 3 local video ctrl +MAPDATA(0xffffffffe0400000, 0x01808017, 0x0180c017, 0x1fe000) # 4 ext. video ctrl +MAPDATA(0xffffffffe0800000, 0x01000017, 0x01010017, 0x7fe000) # 5 local video mem. +MAPDATA(0xffffffffe2000000, 0x02400017, 0x02440017, 0x1ffe000) # 6 ISA I/O and mem. +MAPDATA(0xffffffffffffe000, 0x00000001, 0x0001ffd7, 0) # 7 PCR + + + .text + + .org 0x1000 + .globl swapper_pg_dir +swapper_pg_dir = . + (KSEG1-KSEG0) + +/* + * The page tables are initialized to only 4MB here - the final page + * tables are set up later depending on memory size. + */ + .org 0x2000 + EXPORT(pg0) + + .org 0x3000 + EXPORT(empty_bad_page) + + .org 0x4000 + EXPORT(empty_bad_page_table) + + .org 0x5000 + EXPORT(empty_zero_page) + + .org 0x6000 + EXPORT(invalid_pte_table) + + .org 0x7000 + +/* + * 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). + */ + EXPORT(floppy_track_buffer) + .fill 512*2*36,1,0 + + EXPORT(cache_error_buffer) + .fill 32*4,1,0 + + .data + EXPORT(kernelsp) + PTR 0 diff --git a/arch/mips/kernel/ioport.c b/arch/mips/kernel/ioport.c new file mode 100644 index 000000000..ff6c0d518 --- /dev/null +++ b/arch/mips/kernel/ioport.c @@ -0,0 +1,36 @@ +/* + * linux/arch/mips/kernel/ioport.c + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/ioport.h> + +/* + * This changes the io permissions bitmap in the current task. + */ +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) +{ + return -ENOSYS; +} + +unsigned int *stack; + +/* + * sys_iopl has to be used when you want to access the IO ports + * beyond the 0x3ff range: to get the full 65536 ports bitmapped + * you'd need 8kB of bitmaps/process, which is a bit excessive. + * + * Here we just change the eflags value on the stack: we allow + * only the super-user to do it. This depends on the stack-layout + * on system-call entry - see also fork() and the signal handling + * code. + */ +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) +{ + return -ENOSYS; +} diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c new file mode 100644 index 000000000..608a0b431 --- /dev/null +++ b/arch/mips/kernel/irq.c @@ -0,0 +1,334 @@ +/* + * linux/arch/mips/kernel/irq.c + * + * Copyright (C) 1992 Linus Torvalds + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +/* + * IRQ's are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +/* + * Mips support by Ralf Baechle and Andreas Busse + * + * The Deskstation Tyne is almost completely like an IBM compatible PC with + * another type of microprocessor. Therefore this code is almost completely + * the same. More work needs to be done to support Acer PICA and other + * machines. + */ + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/timex.h> + +#include <asm/bitops.h> +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mipsregs.h> +#include <asm/jazz.h> +#include <asm/system.h> + +unsigned char cache_21 = 0xff; +unsigned char cache_A1 = 0xff; + +unsigned long spurious_count = 0; + +void disable_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; + } + cli(); + cache_A1 |= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +void enable_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; + } + cli(); + cache_A1 &= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +/* + * Pointers to the low-level handlers: first the general ones, then the + * fast ones, then the bad ones. + */ +extern void interrupt(void); +extern void fast_interrupt(void); +extern void bad_interrupt(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 } +}; + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct irqaction * action = irq_action; + + for (i = 0 ; i < 16 ; i++, action++) { + if (!action->handler) + continue; + len += sprintf(buf+len, "%2d: %8d %c %s\n", + i, kstat.interrupts[i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + } + return len; +} + +/* + * do_IRQ handles IRQ's that have been installed without the + * SA_INTERRUPT flag: it uses the full signal-handling return + * and runs with other interrupts enabled. All relatively slow + * IRQ's should use this format: notably the keyboard/timer + * routines. + */ +asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +{ + struct irqaction * action = irq + irq_action; +#if 0 +if (irq > 0) { + printk("in do_IRQ with irq=%d\n",irq); +} +#endif + kstat.interrupts[irq]++; + action->handler(irq, regs); +} + +/* + * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return + * stuff - the handler is also running with interrupts disabled unless + * it explicitly enables them later. + */ +asmlinkage void do_fast_IRQ(int irq) +{ + struct irqaction * action = irq + irq_action; + + kstat.interrupts[irq]++; + action->handler(irq, NULL); +} + +#define SA_PROBE SA_ONESHOT + +int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), + unsigned long irqflags, const char * devname) +{ + 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->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */ + /* + * FIXME: Does the SA_INTERRUPT flag make any sense on MIPS??? + */ + if (action->flags & SA_INTERRUPT) + set_int_vector(irq,fast_interrupt); + else + set_int_vector(irq,interrupt); + } + 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; +} + +void free_irq(unsigned int irq) +{ + struct irqaction * action = irq + irq_action; + 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_int_vector(irq,bad_interrupt); + action->handler = NULL; + action->flags = 0; + action->mask = 0; + action->name = NULL; + restore_flags(flags); +} + +static void no_action(int cpl, struct pt_regs * regs) { } + +unsigned int probe_irq_on (void) +{ + unsigned int i, irqs = 0, irqmask; + unsigned long delay; + + /* first, snaffle up any unassigned irqs */ + for (i = 15; i > 0; i--) { + if (!request_irq(i, no_action, SA_PROBE, "probe")) { + enable_irq(i); + irqs |= (1 << i); + } + } + + /* wait for spurious interrupts to mask themselves out again */ + for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms 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; +} + +int probe_irq_off (unsigned int 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); +#endif + irqs &= irqmask; + if (!irqs) + return 0; + i = ffz(~irqs); + if (irqs != (irqs & (1 << i))) + i = -i; + return i; +} + +void init_IRQ(void) +{ + int i; + + switch (boot_info.machtype) { + case MACH_MIPS_MAGNUM_4000: + case MACH_ACER_PICA_61: + r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, + JAZZ_IE_ETHERNET | + JAZZ_IE_SERIAL1 | + JAZZ_IE_SERIAL2 | + JAZZ_IE_PARALLEL | + JAZZ_IE_FLOPPY); + r4030_read_reg16(JAZZ_IO_IRQ_SOURCE); /* clear pending IRQs */ + set_cp0_status(ST0_IM, IE_IRQ4 | IE_IRQ1); + /* set the clock to 100 Hz */ + r4030_write_reg32(JAZZ_TIMER_INTERVAL, 9); + break; + case MACH_DESKSTATION_TYNE: + /* set the clock to 100 Hz */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + + if (request_irq(2, no_action, SA_INTERRUPT, "cascade")) + printk("Unable to get IRQ2 for cascade\n"); + break; + default: + panic("Unknown machtype in init_IRQ"); + } + + for (i = 0; i < 16 ; i++) + set_int_vector(i, bad_interrupt); + + /* initialize the bottom half routines. */ + for (i = 0; i < 32; i++) { + bh_base[i].routine = NULL; + bh_base[i].data = NULL; + } + bh_active = 0; + intr_count = 0; +} diff --git a/arch/mips/kernel/jazzdma.c b/arch/mips/kernel/jazzdma.c new file mode 100644 index 000000000..1d535e716 --- /dev/null +++ b/arch/mips/kernel/jazzdma.c @@ -0,0 +1,518 @@ +/* + * jazzdma.c + * + * Mips Jazz DMA controller support + * (C) 1995 Andreas Busse + * + * NOTE: Some of the argument checkings could be removed when + * things have settled down. Also, instead of returning 0xffffffff + * on failure of vdma_alloc() one could leave page #0 unused + * and return the more usual NULL pointer as logical address. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <asm/mipsregs.h> +#include <asm/mipsconfig.h> +#include <asm/jazz.h> +#include <asm/io.h> +#include <asm/segment.h> +#include <asm/dma.h> +#include <asm/jazzdma.h> + + +static unsigned long vdma_pagetable_start = 0; +static unsigned long vdma_pagetable_end = 0; + +/* + * Debug stuff + */ + +#define DEBUG_VDMA 0 +#define vdma_debug ((DEBUG_VDMA) ? debuglvl : 0) + +static int debuglvl = 3; + +/* + * Local prototypes + */ + +static void vdma_pgtbl_init(void); + + +/* + * Initialize the Jazz R4030 dma controller + */ + +unsigned long vdma_init(unsigned long memory_start, unsigned long memory_end) +{ + + /* + * Allocate 32k of memory for DMA page tables. + * This needs to be page aligned and should be + * uncached to avoid cache flushing after every + * update. + */ + + vdma_pagetable_start = KSEG1ADDR((memory_start + 4095) & ~ 4095); + vdma_pagetable_end = vdma_pagetable_start + VDMA_PGTBL_SIZE; + + + /* + * Clear the R4030 translation table + */ + + vdma_pgtbl_init(); + + r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE,PHYSADDR(vdma_pagetable_start)); + r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM,VDMA_PGTBL_SIZE); + r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0); + + printk("VDMA: R4030 DMA pagetables initialized.\n"); + return(KSEG0ADDR(vdma_pagetable_end)); +} + +/* + * Allocate DMA pagetables using a simple first-fit algorithm + */ + +unsigned long vdma_alloc(unsigned long paddr, unsigned long size) +{ + VDMA_PGTBL_ENTRY *entry = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + int first; + int last; + int pages; + unsigned int frame; + unsigned long laddr; + int i; + + /* check arguments */ + + if (paddr > 0x1fffffff) { + if (vdma_debug) + printk("vdma_alloc: Invalid physical address: %08lx\n",paddr); + return (VDMA_ERROR); /* invalid physical address */ + } + if (size > 0x400000 || size == 0) { + if (vdma_debug) + printk("vdma_alloc: Invalid size: %08lx\n",size); + return (VDMA_ERROR); /* invalid physical address */ + } + + /* find free chunk */ + + pages = (size + 4095) >> 12; /* no. of pages to allocate */ + first = 0; + while (1) { + while (entry[first].owner != VDMA_PAGE_EMPTY && first < VDMA_PGTBL_ENTRIES) + first++; + if (first+pages > VDMA_PGTBL_ENTRIES) /* nothing free */ + return (VDMA_ERROR); + + last = first+1; + while (entry[last].owner == VDMA_PAGE_EMPTY && last-first < pages) + last++; + + if (last-first == pages) + break; /* found */ + } + + /* mark pages as allocated */ + + laddr = (first << 12) + (paddr & (VDMA_PAGESIZE-1)); + frame = paddr & ~(VDMA_PAGESIZE-1); + + for (i=first; i<last; i++) { + entry[i].frame = frame; + entry[i].owner = laddr; + frame += VDMA_PAGESIZE; + } + + /* + * update translation table and + * return logical start address + */ + + r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0); + + if (vdma_debug > 1) + printk("vdma_alloc: Allocated %d pages starting from %08lx\n", + pages,laddr); + + if (vdma_debug > 2) { + printk("LADDR: "); + for (i=first; i<last; i++) + printk("%08x ",i<<12); + printk("\nPADDR: "); + for (i=first; i<last; i++) + printk("%08x ",entry[i].frame); + printk("\nOWNER: "); + for (i=first; i<last; i++) + printk("%08x ",entry[i].owner); + printk("\n"); + } + + return(laddr); +} + + +/* + * Free previously allocated dma translation pages + * Note that this does NOT change the translation table, + * it just marks the free'd pages as unused! + */ + +int vdma_free(unsigned long laddr) +{ + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + int i; + + i = laddr >> 12; + + if (pgtbl[i].owner != laddr) { + printk("vdma_free: trying to free other's dma pages, laddr=%8lx\n",laddr); + return -1; + } + + while (pgtbl[i].owner == laddr && i < VDMA_PGTBL_ENTRIES) { + pgtbl[i].owner = VDMA_PAGE_EMPTY; + i++; + } + + if (vdma_debug > 1) + printk("vdma_free: freed %ld pages starting from %08lx\n", + i-(laddr>>12),laddr); + + return 0; +} + +/* + * Map certain page(s) to another physical address. + * Caller must have allocated the page(s) before. + */ + +int vdma_remap(unsigned long laddr, unsigned long paddr, unsigned long size) +{ + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + int first; + int pages; + + if (laddr > 0xffffff) { + if (vdma_debug) + printk("vdma_map: Invalid logical address: %08lx\n",laddr); + return -EINVAL; /* invalid logical address */ + } + if (paddr > 0x1fffffff) { + if (vdma_debug) + printk("vdma_map: Invalid physical address: %08lx\n",paddr); + return -EINVAL; /* invalid physical address */ + } + + pages = (((paddr & (VDMA_PAGESIZE-1)) + size) >> 12) + 1; + first = laddr >> 12; + if (vdma_debug) + printk("vdma_remap: first=%x, pages=%x\n",first,pages); + if (first+pages > VDMA_PGTBL_ENTRIES) { + if (vdma_debug) + printk("vdma_alloc: Invalid size: %08lx\n",size); + return -EINVAL; + } + + paddr &= ~(VDMA_PAGESIZE-1); + while (pages > 0 && first < VDMA_PGTBL_ENTRIES) { + if (pgtbl[first].owner != laddr) { + if (vdma_debug) + printk("Trying to remap other's pages.\n"); + return -EPERM; /* not owner */ + } + pgtbl[first].frame = paddr; + paddr += VDMA_PAGESIZE; + first++; + pages--; + } + + /* update translation table */ + + r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0); + + + if (vdma_debug > 2) { + int i; + pages = (((paddr & (VDMA_PAGESIZE-1)) + size) >> 12) + 1; + first = laddr >> 12; + printk("LADDR: "); + for (i=first; i<first+pages; i++) + printk("%08x ",i<<12); + printk("\nPADDR: "); + for (i=first; i<first+pages; i++) + printk("%08x ",pgtbl[i].frame); + printk("\nOWNER: "); + for (i=first; i<first+pages; i++) + printk("%08x ",pgtbl[i].owner); + printk("\n"); + } + + return 0; +} + +/* + * Translate a physical address to a logical address. + * This will return the logical address of the first + * match. + */ + +unsigned long vdma_phys2log(unsigned long paddr) +{ + int i; + int frame; + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + + frame = paddr & ~(VDMA_PAGESIZE-1); + + for (i=0; i<VDMA_PGTBL_ENTRIES; i++) { + if (pgtbl[i].frame == frame) + break; + } + + if (i == VDMA_PGTBL_ENTRIES) + return(0xffffffff); + + return((i<<12) + (paddr & (VDMA_PAGESIZE-1))); +} + +/* + * Translate a logical DMA address to a physical address + */ +unsigned long vdma_log2phys(unsigned long laddr) +{ + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + + return(pgtbl[laddr >> 12].frame + (laddr & (VDMA_PAGESIZE-1))); +} + + +/* + * initialize the pagetable with a one-to-one mapping of + * the first 16 Mbytes of main memory and declare all + * entries to be unused. Using this method will at least + * allow some early device driver operations to work. + */ + +static void vdma_pgtbl_init(void) +{ + int i; + unsigned long paddr = 0; + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + + for (i=0; i<VDMA_PGTBL_ENTRIES; i++) { + pgtbl[i].frame = paddr; + pgtbl[i].owner = VDMA_PAGE_EMPTY; + paddr += VDMA_PAGESIZE; + } + +/* vdma_stats(); */ +} + +/* + * Print DMA statistics + */ + +void vdma_stats(void) +{ + int i; + + printk("vdma_stats: CONFIG: %08x\n", + r4030_read_reg32(JAZZ_R4030_CONFIG)); + printk("R4030 translation table base: %08x\n", + r4030_read_reg32(JAZZ_R4030_TRSTBL_BASE)); + printk("R4030 translation table limit: %08x\n", + r4030_read_reg32(JAZZ_R4030_TRSTBL_LIM)); + printk("vdma_stats: INV_ADDR: %08x\n", + r4030_read_reg32(JAZZ_R4030_INV_ADDR)); + printk("vdma_stats: R_FAIL_ADDR: %08x\n", + r4030_read_reg32(JAZZ_R4030_R_FAIL_ADDR)); + printk("vdma_stats: M_FAIL_ADDR: %08x\n", + r4030_read_reg32(JAZZ_R4030_M_FAIL_ADDR)); + printk("vdma_stats: IRQ_SOURCE: %08x\n", + r4030_read_reg32(JAZZ_R4030_IRQ_SOURCE)); + printk("vdma_stats: I386_ERROR: %08x\n", + r4030_read_reg32(JAZZ_R4030_I386_ERROR)); + printk("vdma_chnl_modes: "); + for (i=0; i<8; i++) + printk("%04x ",(unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_MODE+(i<<5))); + printk("\n"); + printk("vdma_chnl_enables: "); + for (i=0; i<8; i++) + printk("%04x ",(unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(i<<5))); + printk("\n"); +} + + +/* + * DMA transfer functions + */ + +/* + * Enable a DMA channel. Also clear any error conditions. + */ +void vdma_enable(int channel) +{ + int status; + + if (vdma_debug) + printk("vdma_enable: channel %d\n",channel); + + /* + * Check error conditions first + */ + status = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)); + if (status & 0x400) + printk("VDMA: Channel %d: Address error!\n",channel); + if (status & 0x200) + printk("VDMA: Channel %d: Memory error!\n",channel); + + /* + * Clear all interrupt flags + */ + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + R4030_TC_INTR | R4030_MEM_INTR | R4030_ADDR_INTR); + + /* + * Enable the desired channel + */ + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) | + R4030_CHNL_ENABLE); +} + +/* + * Disable a DMA channel + */ +void vdma_disable(int channel) +{ + if (vdma_debug) + { + int status = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)); + + printk("vdma_disable: channel %d\n",channel); + printk("VDMA: channel %d status: %04x (%s) mode: %02x addr: %06x count: %06x\n", + channel,status,((status & 0x600) ? "ERROR" : "OK"), + (unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_MODE+(channel<<5)), + (unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_ADDR+(channel<<5)), + (unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_COUNT+(channel<<5))); + } + + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) & + ~R4030_CHNL_ENABLE); + + /* + * After disabling a DMA channel a remote bus register should be + * read to ensure that the current DMA acknowledge cycle is completed. + */ + + *((volatile unsigned int *)JAZZ_DUMMY_DEVICE); +} + +/* + * Set DMA mode. This function accepts the mode values used + * to set a PC-style DMA controller. For the SCSI and FDC + * channels, we also set the default modes each time we're + * called. + * NOTE: The FAST and BURST dma modes are supported by the + * R4030 Rev. 2 and PICA chipsets only. I leave them disabled + * for now. + */ +void vdma_set_mode(int channel, int mode) +{ + if (vdma_debug) + printk("vdma_set_mode: channel %d, mode 0x%x\n",channel,mode); + + switch(channel) + { + case JAZZ_SCSI_DMA: /* scsi */ + r4030_write_reg32(JAZZ_R4030_CHNL_MODE+(channel<<5), +/* R4030_MODE_FAST | */ +/* R4030_MODE_BURST | */ + R4030_MODE_INTR_EN | + R4030_MODE_WIDTH_16 | + R4030_MODE_ATIME_80); + break; + + case JAZZ_FLOPPY_DMA: /* floppy */ + r4030_write_reg32(JAZZ_R4030_CHNL_MODE+(channel<<5), +/* R4030_MODE_FAST | */ +/* R4030_MODE_BURST | */ + R4030_MODE_INTR_EN | + R4030_MODE_WIDTH_8 | + R4030_MODE_ATIME_120); + break; + + case JAZZ_AUDIOL_DMA: + case JAZZ_AUDIOR_DMA: + printk("VDMA: Audio DMA not supported yet.\n"); + break; + + default: + printk("VDMA: vdma_set_mode() called with unsupported channel %d!\n",channel); + } + + switch(mode) + { + case DMA_MODE_READ: + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) & + ~R4030_CHNL_WRITE); + break; + + case DMA_MODE_WRITE: + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) | + R4030_CHNL_WRITE); + break; + + default: + printk("VDMA: vdma_set_mode() called with unknown dma mode 0x%x\n",mode); + } +} + +/* + * Set Transfer Address + */ +void vdma_set_addr(int channel, long addr) +{ + if (vdma_debug) + printk("vdma_set_addr: channel %d, addr %lx\n",channel,addr); + + r4030_write_reg32(JAZZ_R4030_CHNL_ADDR+(channel<<5),addr); +} + +/* + * Set Transfer Count + */ +void vdma_set_count(int channel, int count) +{ + if (vdma_debug) + printk("vdma_set_count: channel %d, count %08x\n",channel,(unsigned)count); + + r4030_write_reg32(JAZZ_R4030_CHNL_COUNT+(channel<<5),count); +} + +/* + * Get Residual + */ +int vdma_get_residue(int channel) +{ + int residual; + + residual = r4030_read_reg32(JAZZ_R4030_CHNL_COUNT+(channel<<5)); + + if (vdma_debug) + printk("vdma_get_residual: channel %d: residual=%d\n",channel,residual); + + return(residual); +} + + +/* end of file jazzdma.h */ diff --git a/arch/mips/kernel/magnum4000.S b/arch/mips/kernel/magnum4000.S new file mode 100644 index 000000000..45e62d88d --- /dev/null +++ b/arch/mips/kernel/magnum4000.S @@ -0,0 +1,261 @@ +/* + * arch/mips/kernel/magnum4000.S + * + * Copyright (C) 1995 Waldorf Electronics + * written by Ralf Baechle and Andreas Busse + */ +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/jazz.h> +#include <asm/stackframe.h> + +/* + * mips_magnum_4000_handle_int: Interrupt handler for Mips Magnum 4000 + */ + .set noreorder + + NESTED(mips_magnum_4000_handle_int, FR_SIZE, ra) + .set noat + SAVE_ALL + CLI + .set at + + /* + * Get pending interrupts + */ + mfc0 t0,CP0_CAUSE # get pending interrupts + mfc0 t1,CP0_STATUS # get enabled interrupts + and t0,t1 # isolate allowed ones + andi t0,0xff00 # isolate pending bits + beqz t0,spurious_interrupt + sll t0,16 # delay slot + + /* + * Find irq with highest priority + * FIXME: This is slow + */ + la t1,ll_vectors +1: bltz t0,2f # found pending irq + sll t0,1 + b 1b + subu t1,PTRSIZE # delay slot + + /* + * Do the low-level stuff + */ +2: lw t0,(t1) + jr t0 + nop # delay slot + END(mips_magnum_4000_handle_int) + +/* + * Used for keyboard driver's fake_keyboard_interrupt() + */ +ll_sw0: li s1,~IE_SW0 + mfc0 t0,CP0_CAUSE + and t0,s1 + mtc0 t0,CP0_CAUSE + PRINT("sw0 received...\n") + li t1,1 + b call_real + li t3,PTRSIZE # delay slot, re-map to irq level 1 + +ll_sw1: li s1,~IE_SW1 + PANIC("Unimplemented sw1 handler") + +ll_local_dma: li s1,~IE_IRQ0 + PANIC("Unimplemented local_dma handler") + +ll_local_dev: lbu t0,JAZZ_IO_IRQ_SOURCE +#if __mips == 3 + dsll t0,1 + ld t0,local_vector(t0) +#else /* 32 bit */ + lw t0,local_vector(t0) +#endif + jr t0 + nop + + +loc_no_irq: PANIC("Unimplemented loc_no_irq handler") +loc_sound: PANIC("Unimplemented loc_sound handler") +loc_video: PANIC("Unimplemented loc_video handler") +loc_scsi: PANIC("Unimplemented loc_scsi handler") + +/* + * Keyboard interrupt handler + */ +loc_keyboard: li s1,~JAZZ_IE_KEYBOARD + li t1,JAZZ_KEYBOARD_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_KEYBOARD_IRQ # delay slot + +/* + * Ethernet interrupt handler, remapped to level 2 + */ +loc_ethernet: /* PRINT ("ethernet IRQ\n"); */ + li s1,~JAZZ_IE_ETHERNET + li t1,JAZZ_ETHERNET_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_ETHERNET_IRQ # delay slot + + +loc_mouse: PANIC("Unimplemented loc_mouse handler") + +/* + * Serial port 1 IRQ, remapped to level 3 + */ +loc_serial1: li s1,~JAZZ_IE_SERIAL1 + li t1,JAZZ_SERIAL1_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_SERIAL1_IRQ # delay slot + +/* + * Serial port 2 IRQ, remapped to level 4 + */ +loc_serial2: li s1,~JAZZ_IE_SERIAL2 + li t1,JAZZ_SERIAL2_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_SERIAL2_IRQ # delay slot + +/* + * Parallel port IRQ, remapped to level 5 + */ +loc_parallel: li s1,~JAZZ_IE_PARALLEL + li t1,JAZZ_PARALLEL_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_PARALLEL_IRQ # delay slot + +/* + * Floppy IRQ, remapped to level 6 + */ +loc_floppy: li s1,~JAZZ_IE_FLOPPY + li t1,JAZZ_FLOPPY_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_FLOPPY_IRQ # delay slot + +/* + * Now call the real handler + */ +loc_call: lui s3,%hi(intr_count) + lw t2,%lo(intr_count)(s3) + la t0,IRQ_vectors # delay slot + addiu t2,1 + sw t2,%lo(intr_count)(s3) + + /* + * Temporarily disable interrupt source + */ + lhu t2,JAZZ_IO_IRQ_ENABLE + addu t0,t3 # make ptr to IRQ handler + lw t0,(t0) + and t2,s1 # delay slot + sh t2,JAZZ_IO_IRQ_ENABLE + jalr t0 # call IRQ handler + nor s1,zero,s1 # delay slot + + /* + * Reenable interrupt + */ + lhu t2,JAZZ_IO_IRQ_ENABLE + lw t1,%lo(intr_count)(s3) # delay slot + or t2,s1 + sh t2,JAZZ_IO_IRQ_ENABLE + + subu t1,1 + jr v0 + sw t1,%lo(intr_count)(s3) + +ll_eisa_irq: li s1,~IE_IRQ2 + PANIC("Unimplemented eisa_irq handler") + +ll_eisa_nmi: li s1,~IE_IRQ3 + PANIC("Unimplemented eisa_nmi handler") + +/* + * Timer IRQ + * We remap the timer irq to be more similar to a IBM compatible + */ +ll_timer: lw t0,JAZZ_TIMER_REGISTER # timer irq cleared on read + li s1,~IE_IRQ4 + li t1,0 + b call_real + li t3,0 # delay slot, re-map to irq level 0 + +/* + * CPU count/compare IRQ (unused) + */ +ll_count: j return + mtc0 zero,CP0_COMPARE + +/* + * Now call the real handler + */ +call_real: lui s3,%hi(intr_count) + lw t2,%lo(intr_count)(s3) + la t0,IRQ_vectors # delay slot + addiu t2,1 + sw t2,%lo(intr_count)(s3) + + /* + * temporarily disable interrupt + */ + mfc0 t2,CP0_STATUS + and t2,s1 + + addu t0,t3 + lw t0,(t0) + mtc0 t2,CP0_STATUS # delay slot + jalr t0 + nor s1,zero,s1 # delay slot + + /* + * reenable interrupt + */ + mfc0 t2,CP0_STATUS + or t2,s1 + mtc0 t2,CP0_STATUS + + lw t2,%lo(intr_count)(s3) + subu t2,1 + + jr v0 + sw t2,%lo(intr_count)(s3) + +/* + * Just for debugging... + */ + LEAF(drawline) + li t1,0xffffffff + li t2,0x100 +1: sw t1,(a0) + addiu a0,a0,4 + addiu t2,t2,-1 + bnez t2,1b + nop + jr ra + nop + END(drawline) + + + .data + PTR ll_sw0 # SW0 + PTR ll_sw1 # SW1 + PTR ll_local_dma # Local DMA + PTR ll_local_dev # Local devices + PTR ll_eisa_irq # EISA IRQ + PTR ll_eisa_nmi # EISA NMI + PTR ll_timer # Timer +ll_vectors: PTR ll_count # Count/Compare IRQ + +local_vector: PTR loc_no_irq + PTR loc_parallel + PTR loc_floppy + PTR loc_sound + PTR loc_video + PTR loc_ethernet + PTR loc_scsi + PTR loc_keyboard + PTR loc_mouse + PTR loc_serial1 + PTR loc_serial2 diff --git a/arch/mips/kernel/pica.S b/arch/mips/kernel/pica.S new file mode 100644 index 000000000..036aa3139 --- /dev/null +++ b/arch/mips/kernel/pica.S @@ -0,0 +1,252 @@ +/* + * arch/mips/kernel/pica.S + * + * Copyright (C) 1995 Waldorf Electronics + * written by Ralf Baechle and Andreas Busse + * + * Acer PICA 61 specific stuff + */ +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/jazz.h> +#include <asm/pica.h> +#include <asm/stackframe.h> + +/* + * acer_pica_61_handle_int: Interrupt handler for the ACER Pica-61 boards + * FIXME: this is *very* experimental! + */ + .set noreorder + + NESTED(acer_pica_61_handle_int, FR_SIZE, ra) + .set noat + SAVE_ALL + CLI + .set at + + /* + * Get pending interrupts + */ + mfc0 t0,CP0_CAUSE # get pending interrupts + mfc0 t1,CP0_STATUS # get enabled interrupts + and t0,t1 # isolate allowed ones + andi t0,0xff00 # isolate pending bits + beqz t0,spurious_interrupt + sll t0,16 # delay slot + + /* + * Find irq with highest priority + * FIXME: This is slow - use binary search + */ + la t1,ll_vectors +1: bltz t0,2f # found pending irq + sll t0,1 + b 1b + subu t1,PTRSIZE # delay slot + + /* + * Do the low-level stuff + */ +2: lw t0,(t1) + jr t0 + nop # delay slot + END(acer_pica_61_handle_int) + +/* + * Used for keyboard driver's fake_keyboard_interrupt() + */ +ll_sw0: li s1,~IE_SW0 + mfc0 t0,CP0_CAUSE + and t0,s1 + mtc0 t0,CP0_CAUSE + PRINT("sw0 received...\n") + li t1,1 + b call_real + li t3,PTRSIZE # delay slot, re-map to irq level 1 + +ll_sw1: li s1,~IE_SW1 + PANIC("Unimplemented sw1 handler") + +ll_local_dma: li s1,~IE_IRQ0 + PANIC("Unimplemented local_dma handler") + +ll_local_dev: lbu t0,JAZZ_IO_IRQ_SOURCE +#if __mips == 3 + dsll t0,1 + ld t0,local_vector(t0) +#else /* 32 bit */ + lw t0,local_vector(t0) +#endif + jr t0 + nop + + +loc_no_irq: PANIC("Unimplemented loc_no_irq handler") +/* + * Parallel port IRQ, remapped to level 5 + */ +loc_parallel: li s1,~JAZZ_IE_PARALLEL + li t1,JAZZ_PARALLEL_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_PARALLEL_IRQ # delay slot + +/* + * Floppy IRQ, remapped to level 6 + */ +loc_floppy: li s1,~JAZZ_IE_FLOPPY + li t1,JAZZ_FLOPPY_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_FLOPPY_IRQ # delay slot + +/* + * Now call the real handler + */ +loc_call: lui s3,%hi(intr_count) + lw t2,%lo(intr_count)(s3) + la t0,IRQ_vectors # delay slot + addiu t2,1 + sw t2,%lo(intr_count)(s3) + + /* + * Temporarily disable interrupt source + */ + lhu t2,JAZZ_IO_IRQ_ENABLE + addu t0,t3 # make ptr to IRQ handler + lw t0,(t0) + and t2,s1 # delay slot + sh t2,JAZZ_IO_IRQ_ENABLE + jalr t0 # call IRQ handler + nor s1,zero,s1 # delay slot + + /* + * Reenable interrupt + */ + lhu t2,JAZZ_IO_IRQ_ENABLE + lw t1,%lo(intr_count)(s3) # delay slot + or t2,s1 + sh t2,JAZZ_IO_IRQ_ENABLE + + subu t1,1 + jr v0 + sw t1,%lo(intr_count)(s3) # delay slot + +ll_isa_irq: li s1,~IE_IRQ2 + PANIC("Unimplemented isa_irq handler") + +ll_isa_nmi: li s1,~IE_IRQ3 + PANIC("Unimplemented isa_nmi handler") + +/* + * Timer IRQ + * We remap the timer irq to be more similar to an IBM compatible + */ +ll_timer: lw zero,JAZZ_TIMER_REGISTER # timer irq cleared on read + li s1,~IE_IRQ4 + li t1,0 + b call_real + li t3,0 # delay slot, re-map to irq level 0 + +/* + * CPU count/compare IRQ (unused) + */ +ll_count: j return + mtc0 zero,CP0_COMPARE + +/* + * Now call the real handler + */ +call_real: lui s3,%hi(intr_count) + lw t2,%lo(intr_count)(s3) + la t0,IRQ_vectors + addiu t2,1 + sw t2,%lo(intr_count)(s3) + + /* + * temporarily disable interrupt + */ + mfc0 t2,CP0_STATUS + and t2,s1 + + addu t0,t3 + lw t0,(t0) + mtc0 t2,CP0_STATUS # delay slot + jalr t0 + nor s1,zero,s1 # delay slot + + /* + * reenable interrupt + */ + mfc0 t2,CP0_STATUS + or t2,s1 + mtc0 t2,CP0_STATUS + + lw t2,%lo(intr_count)(s3) + subu t2,1 + + jr v0 + sw t2,%lo(intr_count)(s3) + + .data + PTR ll_sw0 # SW0 + PTR ll_sw1 # SW1 + PTR ll_local_dma # Local DMA + PTR ll_local_dev # Local devices + PTR ll_isa_irq # ISA IRQ + PTR ll_isa_nmi # ISA NMI + PTR ll_timer # Timer +ll_vectors: PTR ll_count # Count/Compare IRQ + + +/* + * Sound? What sound hardware (whistle) ??? + */ +loc_sound: PANIC("Unimplemented loc_sound handler") +loc_video: PANIC("Unimplemented loc_video handler") + +/* + * Ethernet interrupt handler, remapped to level 2 + */ +loc_ethernet: li s1,~JAZZ_IE_ETHERNET + li t1,JAZZ_ETHERNET_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_ETHERNET_IRQ # delay slot + +loc_scsi: PANIC("Unimplemented loc_scsi handler") + +/* + * Keyboard interrupt handler + */ +loc_keyboard: li s1,~JAZZ_IE_KEYBOARD + li t1,JAZZ_KEYBOARD_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_KEYBOARD_IRQ # re-map to irq level 1 + +loc_mouse: PANIC("Unimplemented loc_mouse handler") + +/* + * Serial port 1 IRQ, remapped to level 3 + */ +loc_serial1: li s1,~JAZZ_IE_SERIAL1 + li t1,JAZZ_SERIAL1_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_SERIAL1_IRQ # delay slot + +/* + * Serial port 2 IRQ, remapped to level 4 + */ +loc_serial2: li s1,~JAZZ_IE_SERIAL2 + li t1,JAZZ_SERIAL2_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_SERIAL2_IRQ # delay slot + +local_vector: PTR loc_no_irq + PTR loc_parallel + PTR loc_floppy + PTR loc_sound + PTR loc_video + PTR loc_ethernet + PTR loc_scsi + PTR loc_keyboard + PTR loc_mouse + PTR loc_serial1 + PTR loc_serial2 diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c new file mode 100644 index 000000000..dd69c3208 --- /dev/null +++ b/arch/mips/kernel/process.c @@ -0,0 +1,216 @@ +/* + * linux/arch/mips/kernel/process.c + * + * Copyright (C) 1995 Ralf Baechle + * written by Ralf Baechle + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#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 <asm/bootinfo.h> +#include <asm/segment.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/mipsregs.h> +#include <asm/processor.h> +#include <asm/stackframe.h> +#include <asm/io.h> + +asmlinkage void ret_from_sys_call(void); + +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_fs_long(fd[0],0+fildes); + put_fs_long(fd[1],1+fildes); + return 0; +} + +asmlinkage int sys_idle(void) +{ + if (current->pid != 0) + return -EPERM; + + /* endless idle loop with no priority at all */ + current->counter = -100; + for (;;) { + /* + * R4[26]00 have wait, R4[04]00 don't. + */ + if (wait_available && !need_resched) + __asm__(".set\tmips3\n\t" + "wait\n\t" + ".set\tmips0\n\t"); + schedule(); + } +} + +/* + * 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. + * Should be ok for Deskstation Tynes. Reseting others needs to be + * investigated... + */ +static inline void kb_wait(void) +{ + int i; + + for (i=0; i<0x10000; i++) + if ((inb_p(0x64) & 0x02) == 0) + break; +} + +/* + * Hard reset for Deskstation Tyne + * No hint how this works on Pica boards. + */ +void hard_reset_now(void) +{ + int i, j; + + sti(); + for (;;) { + for (i=0; i<100; i++) { + kb_wait(); + for(j = 0; j < 100000 ; j++) + /* nothing */; + outb(0xfe,0x64); /* pulse reset low */ + } + } +} + +void show_regs(struct pt_regs * regs) +{ + /* + * Saved main processor registers + */ + printk("$0 : %08x %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + 0, regs->reg1, regs->reg2, regs->reg3, + regs->reg4, regs->reg5, regs->reg6, regs->reg7); + printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg8, regs->reg9, regs->reg10, regs->reg11, + regs->reg12, regs->reg13, regs->reg14, regs->reg15); + printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg16, regs->reg17, regs->reg18, regs->reg19, + regs->reg20, regs->reg21, regs->reg22, regs->reg23); + printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg24, regs->reg25, regs->reg28, regs->reg29, + regs->reg30, regs->reg31); + + /* + * Saved cp0 registers + */ + printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n", + regs->cp0_epc, regs->cp0_status, regs->cp0_cause); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + /* + * Nothing to do + */ +} + +void flush_thread(void) +{ + /* + * Nothing to do + */ +} + +void copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs * childregs; + + /* + * set up new TSS + */ + childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1; + *childregs = *regs; + childregs->reg2 = 0; + regs->reg2 = p->pid; + childregs->reg29 = usp; + p->tss.ksp = (p->kernel_stack_page + PAGE_SIZE - 8); + p->tss.reg29 = (unsigned long) childregs; /* new sp */ + p->tss.reg31 = (unsigned long) ret_from_sys_call; + + /* + * New tasks loose permission to use the fpu. This accelerates context + * switching for most programs since they don't use the fpu. + */ + p->tss.cp0_status = read_32bit_cp0_register(CP0_STATUS) & + ~(ST0_CU3|ST0_CU2|ST0_CU1|ST0_KSU|ST0_ERL|ST0_EXL); + childregs->cp0_status &= ~(ST0_CU3|ST0_CU2|ST0_CU1); +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + /* + * To do... + */ +} + +asmlinkage int sys_fork(struct pt_regs regs) +{ + return do_fork(COPYVM | SIGCHLD, regs.reg29, ®s); +} + +asmlinkage int sys_clone(struct pt_regs regs) +{ + unsigned long clone_flags; + unsigned long newsp; + + newsp = regs.reg4; + clone_flags = regs.reg5; + if (!newsp) + newsp = regs.reg29; + if (newsp == regs.reg29) + clone_flags |= COPYVM; + return do_fork(clone_flags, newsp, ®s); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(struct pt_regs regs) +{ + int error; + char * filename; + + error = getname((char *) regs.reg4, &filename); + if (error) + return error; + error = do_execve(filename, (char **) regs.reg5, (char **) regs.reg6, ®s); + putname(filename); + return error; +} diff --git a/arch/mips/ptrace.c b/arch/mips/kernel/ptrace.c index 0a42b9c38..6f35ceb67 100644 --- a/arch/mips/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -11,6 +11,7 @@ #include <linux/user.h> #include <asm/segment.h> +#include <asm/pgtable.h> #include <asm/system.h> #if 0 @@ -84,23 +85,30 @@ static inline int put_stack_long(struct task_struct *task, int offset, */ static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr) { + pgd_t * pgdir; + pte_t * pgtable; unsigned long page; repeat: - page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr); - if (page & PAGE_PRESENT) { - page &= PAGE_MASK; - page += PAGE_PTR(addr); - page = *((unsigned long *) page); + pgdir = PAGE_DIR_OFFSET(vma->vm_task, addr); + if (pgd_none(*pgdir)) { + do_no_page(vma, addr, 0); + goto repeat; + } + if (pgd_bad(*pgdir)) { + printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); + pgd_clear(pgdir); + return 0; } - if (!(page & PAGE_PRESENT)) { + pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir)); + if (!pte_present(*pgtable)) { do_no_page(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) return 0; - page &= PAGE_MASK; page += addr & ~PAGE_MASK; return *(unsigned long *) page; } @@ -117,52 +125,50 @@ repeat: static void put_long(struct vm_area_struct * vma, unsigned long addr, unsigned long data) { - unsigned long page, pte = 0; - int readonly = 0; + pgd_t *pgdir; + pte_t *pgtable; + unsigned long page; repeat: - page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr); - if (page & PAGE_PRESENT) { - page &= PAGE_MASK; - page += PAGE_PTR(addr); - pte = page; - page = *((unsigned long *) page); + pgdir = PAGE_DIR_OFFSET(vma->vm_task, addr); + if (!pgd_present(*pgdir)) { + do_no_page(vma, addr, 1); + goto repeat; + } + if (pgd_bad(*pgdir)) { + printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); + pgd_clear(pgdir); + return; } - if (!(page & PAGE_PRESENT)) { - do_no_page(vma, addr, 0 /* PAGE_RW */); + pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir)); + if (!pte_present(*pgtable)) { + do_no_page(vma, addr, 1); goto repeat; } - if (!(page & PAGE_RW)) { - if (!(page & PAGE_COW)) - readonly = 1; - do_wp_page(vma, addr, PAGE_RW | PAGE_PRESENT); + page = pte_page(*pgtable); + if (!pte_write(*pgtable)) { + do_wp_page(vma, addr, 1); goto repeat; } /* this is a hack for non-kernel-mapped video buffers and similar */ - if (page >= high_memory) - return; + if (page < high_memory) { + page += addr & ~PAGE_MASK; + *(unsigned long *) page = data; + } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ - *(unsigned long *) pte |= (PAGE_DIRTY|PAGE_COW); - page &= PAGE_MASK; - page += addr & ~PAGE_MASK; - *(unsigned long *) page = data; - if (readonly) { - *(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW); - invalidate(); - } +/* this should also re-instate whatever read-only mode there was before */ + *pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot)); + invalidate(); } -static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr) +static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr) { struct vm_area_struct * vma; addr &= PAGE_MASK; - for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return NULL; - if (vma->vm_end > addr) - break; - } + vma = find_vma(tsk, addr); + if (!vma) + return NULL; if (vma->vm_start <= addr) return vma; if (!(vma->vm_flags & VM_GROWSDOWN)) @@ -181,7 +187,7 @@ static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long static int read_long(struct task_struct * tsk, unsigned long addr, unsigned long * result) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; @@ -223,7 +229,7 @@ static int read_long(struct task_struct * tsk, unsigned long addr, static int write_long(struct task_struct * tsk, unsigned long addr, unsigned long data) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; diff --git a/arch/mips/kernel/r4xx0.S b/arch/mips/kernel/r4xx0.S new file mode 100644 index 000000000..a68b32243 --- /dev/null +++ b/arch/mips/kernel/r4xx0.S @@ -0,0 +1,732 @@ +/* + * arch/mips/kernel/r4xx0.S + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * Written by Ralf Baechle and Andreas Busse + * + * This file contains most of the R4xx0 specific routines. + * + * This code is evil magic. Read appendix f (coprozessor 0 hazards) of + * all R4xx0 manuals and think about that MIPS means "Microprocessor without + * Interlocked Pipeline Stages" before you even think about changing this code! + */ +#include <linux/autoconf.h> + +#include <asm/asm.h> +#include <asm/bootinfo.h> +#include <asm/cachectl.h> +#include <asm/mipsconfig.h> +#include <asm/mipsregs.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/mipsregs.h> +#include <asm/segment.h> +#include <asm/stackframe.h> + +MODE_ALIAS = 0x0016 # uncachable + + .text + .set mips3 + .set noreorder + + .align 5 + NESTED(handle_tlbl, FR_SIZE, sp) + .set noat + /* + * Check whether this is a refill or an invalid exception + * + * NOTE: Some MIPS manuals say that the R4x00 sets the + * BadVAddr only when EXL == 0. This is wrong - BadVAddr + * is being set for all Reload, Invalid and Modified + * exceptions. + */ + mfc0 k0,CP0_BADVADDR + mfc0 k1,CP0_ENTRYHI + ori k0,0x1fff + xori k0,0x1fff + andi k1,0xff + or k0,k1 + mfc0 k1,CP0_ENTRYHI + mtc0 k0,CP0_ENTRYHI + nop # for R4[04]00 pipeline + nop + nop + tlbp + nop # for R4[04]00 pipeline + nop + mfc0 k0,CP0_INDEX + bgez k0,invalid_tlbl # bad addr in c0_badvaddr + mtc0 k1,CP0_ENTRYHI # delay slot + /* + * Damn... The next nop is required on my R4400PC V5.0, but + * I don't know why - at least there is no documented + * reason as for the others :-( + */ + nop + +#ifdef CONFIG_DEBUG_TLB + /* + * OK, this is a double fault. Let's see whether this is + * due to an invalid entry in the page_table. + */ + dmfc0 k0,CP0_BADVADDR + srl k0,12 + sll k0,2 + lui k1,%HI(TLBMAP) + addu k0,k1 + lw k1,(k0) + andi k1,(_PAGE_PRESENT|_PAGE_ACCESSED) + bnez k1,reload_pgd_entries + nop # delay slot + + .set noat + SAVE_ALL + .set at + PRINT("Double fault caused by invalid entries in pgd:\n") + dmfc0 a1,CP0_BADVADDR + PRINT("Double fault address : %08lx\n") + dmfc0 a1,CP0_EPC + PRINT("c0_epc : %08lx\n") + jal show_regs + move a0,sp + .set noat + STI + .set at + PANIC("Corrupted pagedir") + .set noat + +reload_pgd_entries: +#endif /* CONFIG_DEBUG_TLB */ + + /* + * Load missing pair of entries from the pgd and return. + */ + dmfc0 k1,CP0_CONTEXT + dsra k1,1 + lwu k0,(k1) # Never causes nested exception + lwu k1,4(k1) + dsrl k0,6 # Convert to EntryLo format + dsrl k1,6 # Convert to EntryLo format + dmtc0 k0,CP0_ENTRYLO0 + dmtc0 k1,CP0_ENTRYLO1 + nop # for R4[04]00 pipeline + tlbwr + nop # for R4[04]00 pipeline + nop + nop + /* + * We don't know whether the original access was read or + * write, so return and see what happens... + */ + eret + + /* + * Handle invalid exception + * + * There are two possible causes for an invalid (tlbl) + * exception: + * 1) pages with present bit set but the valid bit clear + * 2) nonexistant pages + * Case one needs fast handling, therefore don't save + * registers yet. + * + * k0 contains c0_index. + */ +invalid_tlbl: /* + * Remove entry so we don't need to care later + * For sake of the R4000 V2.2 pipeline the tlbwi insn + * has been moved down. Moving it around is juggling with + * explosives... + */ + lui k1,0x0008 + or k0,k1 + dsll k0,13 + dmtc0 k0,CP0_ENTRYHI + dmtc0 zero,CP0_ENTRYLO0 + dmtc0 zero,CP0_ENTRYLO1 + /* + * Test present bit in entry + */ + dmfc0 k0,CP0_BADVADDR + srl k0,12 + sll k0,2 + tlbwi # do not move! + lui k1,%HI(TLBMAP) + addu k0,k1 + lw k1,(k0) + andi k1,(_PAGE_PRESENT|_PAGE_READ) + xori k1,(_PAGE_PRESENT|_PAGE_READ) + bnez k1,nopage_tlbl + /* + * Present and read bits are set -> set valid and accessed bits + */ + lw k1,(k0) # delay slot + ori k1,(_PAGE_VALID|_PAGE_ACCESSED) + sw k1,(k0) + eret + + /* + * Page doesn't exist. Lots of work which is less important + * for speed needs to be done, so hand it all over to the + * kernel memory management routines. + */ +nopage_tlbl: SAVE_ALL + STI + .set at + /* + * a0 (struct pt_regs *) regs + * a1 (unsigned long) 0 for read access + */ + move a0,sp + jal do_page_fault + li a1,0 # delay slot + j ret_from_sys_call + nop # delay slot + END(handle_tlbl) + + .text + .align 5 + NESTED(handle_tlbs, FR_SIZE, sp) + .set noat + /* + * It is impossible that is a nested reload exception. + * Therefore this must be a invalid exception. + * Two possible cases: + * 1) Page exists but not dirty. + * 2) Page doesn't exist yet. Hand over to the kernel. + * + * Test whether present bit in entry is set + */ + dmfc0 k0,CP0_BADVADDR + srl k0,12 + sll k0,2 + lui k1,%HI(TLBMAP) + addu k0,k1 + lw k1,(k0) + tlbp # find faulting entry + andi k1,(_PAGE_PRESENT|_PAGE_WRITE) + xori k1,(_PAGE_PRESENT|_PAGE_WRITE) + bnez k1,nopage_tlbs + /* + * Present and writable bits set: set accessed and dirty bits. + */ + lw k1,(k0) # delay slot + ori k1,k1,(_PAGE_ACCESSED|_PAGE_MODIFIED| \ + _PAGE_VALID|_PAGE_DIRTY) + sw k1,(k0) + /* + * Now reload the entry into the TLB + */ + ori k0,0x0004 + xori k0,0x0004 + lw k1,4(k0) + lw k0,(k0) + srl k1,6 + srl k0,6 + dmtc0 k1,CP0_ENTRYLO1 + dmtc0 k0,CP0_ENTRYLO0 + nop # for R4[04]00 pipeline + tlbwi + nop # for R4[04]00 pipeline + nop + nop + eret + + /* + * Page doesn't exist. Lots of work which is less important + * for speed needs to be done, so hand it all over to the + * kernel memory management routines. + */ +nopage_tlbs: +#if 0 + .set mips3 + SAVE_ALL + .set mips0 + PRINT("nopage_tlbs\n") + .set mips3 + RESTORE_ALL + .set mips3 + j 1f + nop +#endif +nowrite_mod: +#if 0 + .set mips3 + SAVE_ALL + .set mips0 + PRINT("nopage_mod\n") + .set mips3 + RESTORE_ALL + .set mips3 + j 1f + nop +1: +#endif + /* + * Remove entry so we don't need to care later + */ + mfc0 k0,CP0_INDEX +#ifdef CONFIG_DEBUG_TLB + bgez k0,2f + nop + /* + * We got a tlbs exception but found no matching entry in + * the tlb. This should never happen. Paranoia makes us + * check it, though. + */ + SAVE_ALL + jal show_regs + move a0,sp + .set at + mfc0 a1,CP0_BADVADDR + PRINT("c0_badvaddr == %08lx\n") + mfc0 a1,CP0_INDEX + PRINT("c0_index == %08x\n") + mfc0 a1,CP0_ENTRYHI + PRINT("c0_entryhi == %08x\n") + jal dump_tlb_nonwired + nop + .set noat + STI + .set at + PANIC("Tlbs or tlbm exception with no matching entry in tlb") +1: j 1b + nop +2: +#endif /* CONFIG_DEBUG_TLB */ + lui k1,0x0008 + or k0,k1 + dsll k0,13 + dmtc0 k0,CP0_ENTRYHI + dmtc0 zero,CP0_ENTRYLO0 + dmtc0 zero,CP0_ENTRYLO1 + nop # for R4[04]00 pipeline + nop # R4000 V2.2 requires 4 NOPs + nop + nop + tlbwi + .set noat + SAVE_ALL + STI + .set at + /* + * a0 (struct pt_regs *) regs + * a1 (unsigned long) 1 for write access + */ + move a0,sp + jal do_page_fault + li a1,1 # delay slot + j ret_from_sys_call + nop # delay slot + END(handle_tlbs) + + .align 5 + NESTED(handle_mod, FR_SIZE, sp) + .set noat + /* + * Two possible cases: + * 1) Page is writable but not dirty -> set dirty and return + * 2) Page is not writable -> call C handler + */ + dmfc0 k0,CP0_BADVADDR + srl k0,12 + sll k0,2 + lui k1,%HI(TLBMAP) + addu k0,k1 + lw k1,(k0) + tlbp # find faulting entry + andi k1,_PAGE_WRITE + beqz k1,nowrite_mod + /* + * Present and writable bits set: set accessed and dirty bits. + */ + lw k1,(k0) # delay slot + ori k1,(_PAGE_ACCESSED|_PAGE_DIRTY) + sw k1,(k0) + /* + * Now reload the entry into the tlb + */ + ori k0,0x0004 + xori k0,0x0004 + lw k1,4(k0) + lw k0,(k0) + srl k1,6 + srl k0,6 + dmtc0 k1,CP0_ENTRYLO1 + dmtc0 k0,CP0_ENTRYLO0 + nop # for R4[04]00 pipeline + nop + nop + tlbwi + nop # for R4[04]00 pipeline + nop + nop + eret + END(handle_mod) + .set at + + .set noreorder + LEAF(tlbflush) + li t0,PM_4K + mtc0 t0,CP0_PAGEMASK + la t0,boot_info + lw t0,OFFSET_BOOTINFO_TLB_ENTRIES(t0) + dmtc0 zero,CP0_ENTRYLO0 + dmtc0 zero,CP0_ENTRYLO1 + mfc0 t2,CP0_WIRED +1: subu t0,1 + mtc0 t0,CP0_INDEX + lui t1,0x0008 + or t1,t0,t1 + dsll t1,13 + dmtc0 t1,CP0_ENTRYHI + bne t2,t0,1b + tlbwi # delay slot + jr ra + nop + END(tlbflush) + + /* + * Code necessary to switch tasks on an Linux/MIPS machine. + */ + .align 5 + LEAF(resume) + /* + * Current task's task_struct + */ + lui t5,%hi(current) + lw t0,%lo(current)(t5) + + /* + * Save status register + */ + mfc0 t1,CP0_STATUS + addu t0,a1 # Add tss offset + sw t1,TOFF_CP0_STATUS(t0) + + /* + * Disable interrupts + */ + ori t2,t1,0x1f + xori t2,0x1e + mtc0 t2,CP0_STATUS + + /* + * Save non-scratch registers + * All other registers have been saved on the kernel stack + */ + sw s0,TOFF_REG16(t0) + sw s1,TOFF_REG17(t0) + sw s2,TOFF_REG18(t0) + sw s3,TOFF_REG19(t0) + sw s4,TOFF_REG20(t0) + sw s5,TOFF_REG21(t0) + sw s6,TOFF_REG22(t0) + sw s7,TOFF_REG23(t0) + sw gp,TOFF_REG28(t0) + sw sp,TOFF_REG29(t0) + sw fp,TOFF_REG30(t0) + + /* + * Save floating point state + */ + sll t2,t1,2 + bgez t2,2f + sw ra,TOFF_REG31(t0) # delay slot + sll t2,t1,5 + bgez t2,1f + sdc1 $f0,(TOFF_FPU+0)(t0) # delay slot + /* + * Store the 16 odd double precision registers + */ + sdc1 $f1,(TOFF_FPU+8)(t0) + sdc1 $f3,(TOFF_FPU+24)(t0) + sdc1 $f5,(TOFF_FPU+40)(t0) + sdc1 $f7,(TOFF_FPU+56)(t0) + sdc1 $f9,(TOFF_FPU+72)(t0) + sdc1 $f11,(TOFF_FPU+88)(t0) + sdc1 $f13,(TOFF_FPU+104)(t0) + sdc1 $f15,(TOFF_FPU+120)(t0) + sdc1 $f17,(TOFF_FPU+136)(t0) + sdc1 $f19,(TOFF_FPU+152)(t0) + sdc1 $f21,(TOFF_FPU+168)(t0) + sdc1 $f23,(TOFF_FPU+184)(t0) + sdc1 $f25,(TOFF_FPU+200)(t0) + sdc1 $f27,(TOFF_FPU+216)(t0) + sdc1 $f29,(TOFF_FPU+232)(t0) + sdc1 $f31,(TOFF_FPU+248)(t0) + + /* + * Store the 16 even double precision registers + */ +1: cfc1 t1,$31 + sdc1 $f2,(TOFF_FPU+16)(t0) + sdc1 $f4,(TOFF_FPU+32)(t0) + sdc1 $f6,(TOFF_FPU+48)(t0) + sdc1 $f8,(TOFF_FPU+64)(t0) + sdc1 $f10,(TOFF_FPU+80)(t0) + sdc1 $f12,(TOFF_FPU+96)(t0) + sdc1 $f14,(TOFF_FPU+112)(t0) + sdc1 $f16,(TOFF_FPU+128)(t0) + sdc1 $f18,(TOFF_FPU+144)(t0) + sdc1 $f20,(TOFF_FPU+160)(t0) + sdc1 $f22,(TOFF_FPU+176)(t0) + sdc1 $f24,(TOFF_FPU+192)(t0) + sdc1 $f26,(TOFF_FPU+208)(t0) + sdc1 $f28,(TOFF_FPU+224)(t0) + sdc1 $f30,(TOFF_FPU+240)(t0) + sw t1,(TOFF_FPU+256)(t0) + + /* + * Switch current task + */ +2: sw a0,%lo(current)(t5) + addu a0,a1 # Add tss offset + + /* + * Switch address space + */ + + /* + * (Choose new ASID for process) + * This isn't really required, but would speed up + * context switching. + */ + + /* + * Switch the root pointer + */ + lw t0,TOFF_PG_DIR(a0) + li t1,TLB_ROOT + mtc0 t1,CP0_ENTRYHI + mtc0 zero,CP0_INDEX + srl t0,6 + ori t0,MODE_ALIAS + mtc0 t0,CP0_ENTRYLO0 + mtc0 zero,CP0_ENTRYLO1 + lw a2,TOFF_CP0_STATUS(a0) + + /* + * Flush tlb + * (probably not needed, doesn't clobber a0-a3) + */ + jal tlbflush + tlbwi # delay slot + + /* + * Restore fpu state: + * - cp0 status register bits + * - fp gp registers + * - cp1 status/control register + */ + ori t1,a2,1 # pipeline magic + xori t1,1 + mtc0 t1,CP0_STATUS + sll t0,a2,2 + bgez t0,2f + sll t0,a2,5 # delay slot + bgez t0,1f + ldc1 $f0,(TOFF_FPU+0)(a0) # delay slot + /* + * Restore the 16 odd double precision registers only + * when enabled in the cp0 status register. + */ + ldc1 $f1,(TOFF_FPU+8)(a0) + ldc1 $f3,(TOFF_FPU+24)(a0) + ldc1 $f5,(TOFF_FPU+40)(a0) + ldc1 $f7,(TOFF_FPU+56)(a0) + ldc1 $f9,(TOFF_FPU+72)(a0) + ldc1 $f11,(TOFF_FPU+88)(a0) + ldc1 $f13,(TOFF_FPU+104)(a0) + ldc1 $f15,(TOFF_FPU+120)(a0) + ldc1 $f17,(TOFF_FPU+136)(a0) + ldc1 $f19,(TOFF_FPU+152)(a0) + ldc1 $f21,(TOFF_FPU+168)(a0) + ldc1 $f23,(TOFF_FPU+184)(a0) + ldc1 $f25,(TOFF_FPU+200)(a0) + ldc1 $f27,(TOFF_FPU+216)(a0) + ldc1 $f29,(TOFF_FPU+232)(a0) + ldc1 $f31,(TOFF_FPU+248)(a0) + + /* + * Restore the 16 even double precision registers + * when cp1 was enabled in the cp0 status register. + */ +1: lw t0,(TOFF_FPU+256)(a0) + ldc1 $f2,(TOFF_FPU+16)(a0) + ldc1 $f4,(TOFF_FPU+32)(a0) + ldc1 $f6,(TOFF_FPU+48)(a0) + ldc1 $f8,(TOFF_FPU+64)(a0) + ldc1 $f10,(TOFF_FPU+80)(a0) + ldc1 $f12,(TOFF_FPU+96)(a0) + ldc1 $f14,(TOFF_FPU+112)(a0) + ldc1 $f16,(TOFF_FPU+128)(a0) + ldc1 $f18,(TOFF_FPU+144)(a0) + ldc1 $f20,(TOFF_FPU+160)(a0) + ldc1 $f22,(TOFF_FPU+176)(a0) + ldc1 $f24,(TOFF_FPU+192)(a0) + ldc1 $f26,(TOFF_FPU+208)(a0) + ldc1 $f28,(TOFF_FPU+224)(a0) + ldc1 $f30,(TOFF_FPU+240)(a0) + ctc1 t0,$31 + + /* + * Restore non-scratch registers + */ +2: lw s0,TOFF_REG16(a0) + lw s1,TOFF_REG17(a0) + lw s2,TOFF_REG18(a0) + lw s3,TOFF_REG19(a0) + lw s4,TOFF_REG20(a0) + lw s5,TOFF_REG21(a0) + lw s6,TOFF_REG22(a0) + lw s7,TOFF_REG23(a0) + lw gp,TOFF_REG28(a0) + lw sp,TOFF_REG29(a0) + lw fp,TOFF_REG30(a0) + lw ra,TOFF_REG31(a0) + + /* + * Restore status register + */ + lw t0,TOFF_KSP(a0) + sw t0,kernelsp + + jr ra + mtc0 a2,CP0_STATUS # delay slot + END(resume) + +/* + * Some bits in the config register + */ +#define CONFIG_DB (1<<4) +#define CONFIG_IB (1<<5) + +/* + * Flush instruction/data caches + * + * Parameters: a0 - starting address to flush + * a1 - size of area to be flushed + * a2 - which caches to be flushed + * + * FIXME: - ignores parameters in a0/a1 + * - doesn't know about second level caches + */ + .set noreorder + LEAF(sys_cacheflush) + andi t1,a2,DCACHE + beqz t1,do_icache + li t0,KSEG0 # delay slot + + /* + * Writeback data cache, even lines + */ + li t1,CACHELINES-1 +1: cache Index_Writeback_Inv_D,0(t0) + cache Index_Writeback_Inv_D,32(t0) + cache Index_Writeback_Inv_D,64(t0) + cache Index_Writeback_Inv_D,96(t0) + cache Index_Writeback_Inv_D,128(t0) + cache Index_Writeback_Inv_D,160(t0) + cache Index_Writeback_Inv_D,192(t0) + cache Index_Writeback_Inv_D,224(t0) + cache Index_Writeback_Inv_D,256(t0) + cache Index_Writeback_Inv_D,288(t0) + cache Index_Writeback_Inv_D,320(t0) + cache Index_Writeback_Inv_D,352(t0) + cache Index_Writeback_Inv_D,384(t0) + cache Index_Writeback_Inv_D,416(t0) + cache Index_Writeback_Inv_D,448(t0) + cache Index_Writeback_Inv_D,480(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + + /* + * Writeback data cache, odd lines + * Only needed for 16 byte line size + */ + mfc0 t1,CP0_CONFIG + andi t1,CONFIG_DB + bnez t1,do_icache + li t1,CACHELINES-1 +1: cache Index_Writeback_Inv_D,16(t0) + cache Index_Writeback_Inv_D,48(t0) + cache Index_Writeback_Inv_D,80(t0) + cache Index_Writeback_Inv_D,112(t0) + cache Index_Writeback_Inv_D,144(t0) + cache Index_Writeback_Inv_D,176(t0) + cache Index_Writeback_Inv_D,208(t0) + cache Index_Writeback_Inv_D,240(t0) + cache Index_Writeback_Inv_D,272(t0) + cache Index_Writeback_Inv_D,304(t0) + cache Index_Writeback_Inv_D,336(t0) + cache Index_Writeback_Inv_D,368(t0) + cache Index_Writeback_Inv_D,400(t0) + cache Index_Writeback_Inv_D,432(t0) + cache Index_Writeback_Inv_D,464(t0) + cache Index_Writeback_Inv_D,496(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + +do_icache: andi t1,a2,ICACHE + beqz t1,done + + /* + * Flush instruction cache, even lines + */ + lui t0,0x8000 + li t1,CACHELINES-1 +1: cache Index_Invalidate_I,0(t0) + cache Index_Invalidate_I,32(t0) + cache Index_Invalidate_I,64(t0) + cache Index_Invalidate_I,96(t0) + cache Index_Invalidate_I,128(t0) + cache Index_Invalidate_I,160(t0) + cache Index_Invalidate_I,192(t0) + cache Index_Invalidate_I,224(t0) + cache Index_Invalidate_I,256(t0) + cache Index_Invalidate_I,288(t0) + cache Index_Invalidate_I,320(t0) + cache Index_Invalidate_I,352(t0) + cache Index_Invalidate_I,384(t0) + cache Index_Invalidate_I,416(t0) + cache Index_Invalidate_I,448(t0) + cache Index_Invalidate_I,480(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + + /* + * Flush instruction cache, even lines + * Only needed for 16 byte line size + */ + mfc0 t1,CP0_CONFIG + andi t1,CONFIG_IB + bnez t1,done + li t1,CACHELINES-1 +1: cache Index_Invalidate_I,16(t0) + cache Index_Invalidate_I,48(t0) + cache Index_Invalidate_I,80(t0) + cache Index_Invalidate_I,112(t0) + cache Index_Invalidate_I,144(t0) + cache Index_Invalidate_I,176(t0) + cache Index_Invalidate_I,208(t0) + cache Index_Invalidate_I,240(t0) + cache Index_Invalidate_I,272(t0) + cache Index_Invalidate_I,304(t0) + cache Index_Invalidate_I,336(t0) + cache Index_Invalidate_I,368(t0) + cache Index_Invalidate_I,400(t0) + cache Index_Invalidate_I,432(t0) + cache Index_Invalidate_I,464(t0) + cache Index_Invalidate_I,496(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + +done: j ra + nop + END(sys_cacheflush) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c new file mode 100644 index 000000000..f5037fbd7 --- /dev/null +++ b/arch/mips/kernel/setup.c @@ -0,0 +1,207 @@ +/* + * linux/arch/mips/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 1995 Ralf Baechle + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/string.h> +#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 <asm/asm.h> +#include <asm/bootinfo.h> +#include <asm/vector.h> +#include <asm/segment.h> +#include <asm/stackframe.h> +#include <asm/system.h> + +/* + * How to handle the machine's features + */ +struct feature *feature; + +#ifdef CONFIG_ACER_PICA_61 +void acer_pica_61_handle_int(void); +struct feature acer_pica_61_feature = { + acer_pica_61_handle_int +}; +#endif +#ifdef CONFIG_DECSTATION +void decstation_handle_handle_int(void); +struct feature decstation_feature = { + decstation_handle_handle_int +}; +#endif +#ifdef CONFIG_DESKSTATION_RPC44 +void deskstation_rpc44_handle_int(void); +struct feature deskstation_rpc44_feature = { + deskstation_rpc44_handle_int +}; +#endif +#ifdef CONFIG_DESKSTATION_TYNE +void deskstation_tyne_handle_int(void); +struct feature deskstation_tyne_feature = { + deskstation_tyne_handle_int +}; +#endif +#ifdef CONFIG_MIPS_MAGNUM_4000 +void mips_magnum_4000_handle_int(void); +struct feature mips_magnum_4000_feature = { + mips_magnum_4000_handle_int +}; +#endif + +/* + * Tell us the machine setup.. + */ +char wait_available; /* set if the "wait" instruction available */ + +/* + * Bus types .. + */ +int EISA_bus = 0; + +/* + * Setup options + */ +struct drive_info_struct drive_info; +struct screen_info screen_info = SCREEN_INFO; + +unsigned char aux_device_present; +extern int ramdisk_size; +extern int root_mountflags; +extern int _end; + +extern char empty_zero_page[PAGE_SIZE]; + +/* + * Initialise this structure so that it will be placed in the + * .data section of the object file + */ +struct bootinfo boot_info = BOOT_INFO; + +/* + * This is set up by the setup-routine at boot-time + */ +#define PARAM empty_zero_page +#if 0 +#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) +#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) +#endif + +static char command_line[CL_SIZE] = { 0, }; + +#if 0 +/* + * Code for easy access to new style bootinfo + * + * Parameter: tag -- taglist entry + * + * returns : (tag *) -- pointer to taglist entry, NULL for not found + */ +tag * +bi_TagFind(enum bi_tag tag) +{ + /* TBD */ + return 0; +} + +/* + * Only for taglist creators (bootloaders) + * + * Parameter: tag -- (enum bi_tag) taglist entry + * + * returns : 1 -- success + * 0 -- failure + */ +int +bi_TagAdd(enum bi_tag tag, unsigned long size, void *tagdata) +{ + /* TBD */ + return 0; +} +#endif /* 0 */ + +void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + unsigned long memory_start, memory_end; + + switch(boot_info.machtype) + { +#ifdef CONFIG_ACER_PICA_61 + case MACH_ACER_PICA_61: + feature = &acer_pica_61_feature; + break; +#endif +#ifdef CONFIG_DECSTATION + case MACH_DECSTATION: + feature = &decstation_feature; + break; +#endif +#ifdef CONFIG_DESKSTATION_RPC + case MACH_DESKSTATION_RPC: + feature = &deskstation_rpc44_feature; + break; +#endif +#ifdef CONFIG_DESKSTATION_TYNE + case MACH_DESKSTATION_TYNE: + feature = &deskstation_tyne_feature; + break; +#endif +#ifdef CONFIG_MIPS_MAGNUM_4000 + case MACH_MIPS_MAGNUM_4000: + feature = &mips_magnum_4000_feature; + break; +#endif + default: + panic("Unsupported architecture"); + } + +#if 0 + ROOT_DEV = ORIG_ROOT_DEV; +#else + ROOT_DEV = 0x021c; /* fd0H1440 */ +/* ROOT_DEV = 0x0101; */ /* ram */ +/* ROOT_DEV = 0x00ff; */ /* NFS */ +#endif + memcpy(&drive_info, &boot_info.drive_info, sizeof(drive_info)); +#if 0 + aux_device_present = AUX_DEVICE_INFO; +#endif + memory_end = boot_info.memupper; + memory_end &= PAGE_MASK; + ramdisk_size = boot_info.ramdisk_size; + if (boot_info.mount_root_rdonly) + root_mountflags |= MS_RDONLY; + + memory_start = (unsigned long) &_end; + memory_start += (ramdisk_size << 10); + + *cmdline_p = command_line; + *memory_start_p = memory_start; + *memory_end_p = memory_end; + +#if 0 + /* + * Check that struct pt_regs is defined properly + * (Should be optimized away, but gcc 2.6.3 is too bad..) + */ + if (FR_SIZE != sizeof(struct pt_regs) || + FR_SIZE & 7) + { + panic("Check_definition_of_struct_pt_regs\n"); + } +#endif +} diff --git a/arch/mips/signal.c b/arch/mips/kernel/signal.c index ef3246ee0..ea00551a9 100644 --- a/arch/mips/signal.c +++ b/arch/mips/kernel/signal.c @@ -1,10 +1,11 @@ /* - * linux/kernel/signal.c + * linux/arch/mips/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds */ #include <linux/sched.h> +#include <linux/mm.h> #include <linux/kernel.h> #include <linux/signal.h> #include <linux/errno.h> @@ -19,63 +20,7 @@ #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs); - -asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset) -{ - sigset_t new_set, old_set = current->blocked; - int error; - - if (set) { - error = verify_area(VERIFY_READ, set, sizeof(sigset_t)); - if (error) - return error; - new_set = get_fs_long((unsigned long *) set) & _BLOCKABLE; - switch (how) { - case SIG_BLOCK: - current->blocked |= new_set; - break; - case SIG_UNBLOCK: - current->blocked &= ~new_set; - break; - case SIG_SETMASK: - current->blocked = new_set; - break; - default: - return -EINVAL; - } - } - if (oset) { - error = verify_area(VERIFY_WRITE, oset, sizeof(sigset_t)); - if (error) - return error; - put_fs_long(old_set, (unsigned long *) oset); - } - return 0; -} - -asmlinkage int sys_sgetmask(void) -{ - return current->blocked; -} - -asmlinkage int sys_ssetmask(int newmask) -{ - int old=current->blocked; - - current->blocked = newmask & _BLOCKABLE; - return old; -} - -asmlinkage int sys_sigpending(sigset_t *set) -{ - int error; - /* fill in "set" with signals pending but blocked. */ - error = verify_area(VERIFY_WRITE, set, 4); - if (!error) - put_fs_long(current->blocked & current->signal, (unsigned long *)set); - return error; -} +asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); /* * atomically swap in the new signal mask, and wait for a signal. @@ -87,11 +32,7 @@ asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long mask = current->blocked; current->blocked = set & _BLOCKABLE; -#if defined (__i386__) - regs->eax = -EINTR; -#elif defined (__mips__) regs->reg2 = -EINTR; -#endif while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); @@ -101,99 +42,6 @@ asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long } /* - * POSIX 3.3.1.3: - * "Setting a signal action to SIG_IGN for a signal that is pending - * shall cause the pending signal to be discarded, whether or not - * it is blocked" (but SIGCHLD is unspecified: linux leaves it alone). - * - * "Setting a signal action to SIG_DFL for a signal that is pending - * and whose default action is to ignore the signal (for example, - * SIGCHLD), shall cause the pending signal to be discarded, whether - * or not it is blocked" - * - * Note the silly behaviour of SIGCHLD: SIG_IGN means that the signal - * isn't actually ignored, but does automatic child reaping, while - * SIG_DFL is explicitly said by POSIX to force the signal to be ignored.. - */ -static void check_pending(int signum) -{ - struct sigaction *p; - - p = signum - 1 + current->sigaction; - if (p->sa_handler == SIG_IGN) { - if (signum == SIGCHLD) - return; - current->signal &= ~_S(signum); - return; - } - if (p->sa_handler == SIG_DFL) { - if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH) - return; - current->signal &= ~_S(signum); - return; - } -} - -asmlinkage int sys_signal(int signum, unsigned long handler) -{ - struct sigaction tmp; - - if (signum<1 || signum>32) - return -EINVAL; - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - if (handler >= TASK_SIZE) - return -EFAULT; - tmp.sa_handler = (void (*)(int)) handler; - tmp.sa_mask = 0; - tmp.sa_flags = SA_ONESHOT | SA_NOMASK; - tmp.sa_restorer = NULL; - handler = (long) current->sigaction[signum-1].sa_handler; - current->sigaction[signum-1] = tmp; - check_pending(signum); - return handler; -} - -asmlinkage int sys_sigaction(int signum, const struct sigaction * action, - struct sigaction * oldaction) -{ - struct sigaction new_sa, *p; - - if (signum<1 || signum>32) - return -EINVAL; - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - p = signum - 1 + current->sigaction; - if (action) { - int err = verify_area(VERIFY_READ, action, sizeof(*action)); - if (err) - return err; - memcpy_fromfs(&new_sa, action, sizeof(struct sigaction)); - if (new_sa.sa_flags & SA_NOMASK) - new_sa.sa_mask = 0; - else { - new_sa.sa_mask |= _S(signum); - new_sa.sa_mask &= _BLOCKABLE; - } - if (TASK_SIZE <= (unsigned long) new_sa.sa_handler) - return -EFAULT; - } - if (oldaction) { - int err = verify_area(VERIFY_WRITE, oldaction, sizeof(*oldaction)); - if (err) - return err; - memcpy_tofs(oldaction, p, sizeof(struct sigaction)); - } - if (action) { - *p = new_sa; - check_pending(signum); - } - return 0; -} - -asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); - -/* * This sets regs->reg29 even though we don't actually use sigstacks yet.. */ asmlinkage int sys_sigreturn(unsigned long __unused) @@ -221,9 +69,25 @@ asmlinkage int sys_sigreturn(unsigned long __unused) regs->reg13 = context.sc_t5; regs->reg14 = context.sc_t6; regs->reg15 = context.sc_t7; + regs->reg16 = context.sc_s0; + regs->reg17 = context.sc_s1; + regs->reg18 = context.sc_s2; + regs->reg19 = context.sc_s3; + regs->reg20 = context.sc_s4; + regs->reg21 = context.sc_s5; + regs->reg22 = context.sc_s6; + regs->reg23 = context.sc_s7; regs->reg24 = context.sc_t8; regs->reg25 = context.sc_t9; + /* + * Skip k0/k1 + */ + regs->reg28 = context.sc_gp; regs->reg29 = context.sc_sp; + regs->reg30 = context.sc_fp; + regs->reg31 = context.sc_ra; + regs->cp0_epc = context.sc_epc; + regs->cp0_cause = context.sc_cause; /* * disable syscall checks @@ -244,11 +108,11 @@ static void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long * frame; frame = *fp; - frame -= 21; + frame -= 32; if (verify_area(VERIFY_WRITE,frame,21*4)) do_exit(SIGSEGV); /* - * set up the "normal" stack seen by the signal handler (iBCS2) + * set up the "normal" stack seen by the signal handler */ put_fs_long(regs->reg1 , frame ); put_fs_long(regs->reg2 , frame+ 1); @@ -258,34 +122,49 @@ static void setup_frame(struct sigaction * sa, unsigned long ** fp, put_fs_long(regs->reg6 , frame+ 5); put_fs_long(regs->reg7 , frame+ 6); put_fs_long(regs->reg8 , frame+ 7); - put_fs_long(regs->reg10, frame+ 8); - put_fs_long(regs->reg11, frame+ 9); - put_fs_long(regs->reg12, frame+10); - put_fs_long(regs->reg13, frame+11); - put_fs_long(regs->reg14, frame+12); - put_fs_long(regs->reg15, frame+13); - put_fs_long(regs->reg16, frame+14); - put_fs_long(regs->reg17, frame+15); - put_fs_long(regs->reg24, frame+16); - put_fs_long(regs->reg25, frame+17); - put_fs_long(regs->reg29, frame+18); - put_fs_long(pc , frame+19); - put_fs_long(oldmask , frame+20); + put_fs_long(regs->reg9 , frame+ 8); + put_fs_long(regs->reg10, frame+ 9); + put_fs_long(regs->reg11, frame+10); + put_fs_long(regs->reg12, frame+11); + put_fs_long(regs->reg13, frame+12); + put_fs_long(regs->reg14, frame+13); + put_fs_long(regs->reg15, frame+14); + put_fs_long(regs->reg16, frame+15); + put_fs_long(regs->reg17, frame+16); + put_fs_long(regs->reg18, frame+17); + put_fs_long(regs->reg19, frame+18); + put_fs_long(regs->reg20, frame+19); + put_fs_long(regs->reg21, frame+20); + put_fs_long(regs->reg22, frame+21); + put_fs_long(regs->reg23, frame+22); + put_fs_long(regs->reg24, frame+23); + put_fs_long(regs->reg25, frame+24); + /* + * Don't copy k0/k1 + */ + put_fs_long(regs->reg28, frame+25); + put_fs_long(regs->reg29, frame+26); + put_fs_long(regs->reg30, frame+27); + put_fs_long(regs->reg31, frame+28); + put_fs_long(pc , frame+29); + put_fs_long(oldmask , frame+30); /* * set up the return code... * + * .set noreorder * .set noat * syscall - * li $1,__NR_sigreturn + * li v0,__NR_sigreturn * .set at + * .set reorder */ - put_fs_long(0x24010077, frame+20); /* li $1,119 */ - put_fs_long(0x000000c0, frame+21); /* syscall */ + put_fs_long(0x24020077, frame+31); /* li $2,119 */ + put_fs_long(0x000000c0, frame+32); /* syscall */ *fp = frame; /* * Flush caches so the instructions will be correctly executed. */ - cacheflush(frame, 21*4, BCACHE); + sys_cacheflush(frame, 32*4, BCACHE); } /* @@ -307,30 +186,8 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) struct sigaction * sa; while ((signr = current->signal & mask)) { -#if defined (__i386__) - __asm__("bsf %2,%1\n\t" - "btrl %1,%0" - :"=m" (current->signal),"=r" (signr) - :"1" (signr)); -#elif defined (__mips__) - __asm__(".set\tnoreorder\n\t" - ".set\tnoat\n\t" - "li\t%1,1\n" - "1:\tand\t$1,%2,%1\n\t" - "beq\t$0,$1,2f\n\t" - "sll\t%2,%2,1\n\t" - "bne\t$0,%2,1b\n\t" - "add\t%0,%0,1\n" - "2:\tli\t%2,-2\n\t" - "sllv\t%2,%2,%0\n\t" - "and\t%1,%1,%2\n\t" - ".set\tat\n\t" - ".set\treorder\n" - "2:\n\t" - :"=r" (signr),"=r" (current->signal),"=r" (mask) - :"0" (signr),"1" (current->signal) - :"$1"); -#endif + signr = ffz(~signr); + clear_bit(signr, ¤t->signal); sa = current->sigaction + signr; signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { @@ -376,7 +233,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) continue; case SIGQUIT: case SIGILL: case SIGTRAP: - case SIGIOT: case SIGFPE: case SIGSEGV: + case SIGIOT: case SIGFPE: case SIGSEGV: case SIGBUS: if (current->binfmt && current->binfmt->core_dump) { if (current->binfmt->core_dump(signr, regs)) signr |= 0x80; @@ -404,7 +261,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) regs->reg2 == -ERESTARTSYS || regs->reg2 == -ERESTARTNOINTR)) { regs->reg2 = regs->orig_reg2; - regs->cp0_epc -= 2; + regs->cp0_epc -= 4; } if (!handler_signal) /* no handler will be called - return 0 */ return 0; @@ -425,16 +282,14 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) * force a kernel-mode page-in of the signal * handler to reduce races */ - __asm__(".set\tnoat\n\t" - "lwu\t$1,(%0)\n\t" - ".set\tat\n\t" - : - :"r" ((char *) pc) - :"$1"); + __asm__("lw\t$0,(%0)" + : /* no output */ + :"r" ((char *) pc)); current->blocked |= sa->sa_mask; oldmask |= sa->sa_mask; } regs->reg29 = (unsigned long) frame; regs->cp0_epc = pc; /* "return" to the first handler */ + return 1; } diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c new file mode 100644 index 000000000..36e8a31e8 --- /dev/null +++ b/arch/mips/kernel/traps.c @@ -0,0 +1,438 @@ +/* + * arch/mips/kernel/traps.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +/* + * 'traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. Currently mostly a debugging-aid, will be extended + * to mainly kill the offending process (probably by giving it a signal, + * but possibly by killing it outright if necessary). + * + * FIXME: This is the place for a fpu emulator. + */ +#include <linux/head.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/config.h> +#include <linux/timer.h> +#include <linux/mm.h> + +#include <asm/vector.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/mipsregs.h> +#include <asm/bootinfo.h> + +static inline void console_verbose(void) +{ + extern int console_loglevel; + console_loglevel = 15; +} + +/* + * Machine specific interrupt handlers + */ +extern asmlinkage void acer_pica_61_handle_int(void); +extern asmlinkage void decstation_handle_int(void); +extern asmlinkage void deskstation_rpc44_handle_int(void); +extern asmlinkage void deskstation_tyne_handle_int(void); +extern asmlinkage void mips_magnum_4000_handle_int(void); + +extern asmlinkage void handle_mod(void); +extern asmlinkage void handle_tlbl(void); +extern asmlinkage void handle_tlbs(void); +extern asmlinkage void handle_adel(void); +extern asmlinkage void handle_ades(void); +extern asmlinkage void handle_ibe(void); +extern asmlinkage void handle_dbe(void); +extern asmlinkage void handle_sys(void); +extern asmlinkage void handle_bp(void); +extern asmlinkage void handle_ri(void); +extern asmlinkage void handle_cpu(void); +extern asmlinkage void handle_ov(void); +extern asmlinkage void handle_tr(void); +extern asmlinkage void handle_vcei(void); +extern asmlinkage void handle_fpe(void); +extern asmlinkage void handle_vced(void); +extern asmlinkage void handle_watch(void); +extern asmlinkage void handle_reserved(void); + +static char *cpu_names[] = CPU_NAMES; + +/* + * Fix address errors. This is slow, so try not to use it. This is + * disabled by default, anyway. + */ +int fix_ade_enabled = 0; +unsigned long page_colour_mask; + +int kstack_depth_to_print = 24; + +/* + * These constant is for searching for possible module text segments. + * MODULE_RANGE is a guess of how much space is likely to be vmalloced. + */ +#define MODULE_RANGE (8*1024*1024) + +void die_if_kernel(char * str, struct pt_regs * regs, long err) +{ + int i; + int *stack; + u32 *sp, *pc, addr, module_start, module_end; + extern char start_kernel, _etext; + + if ((regs->cp0_status & (ST0_ERL|ST0_EXL)) == 0) + return; + + sp = (u32 *)regs->reg29; + pc = (u32 *)regs->cp0_epc; + + console_verbose(); + printk("%s: %08lx\n", str, err ); + + show_regs(regs); + + /* + * Some goodies... + */ + printk("Int : %ld\n", regs->interrupt); + + /* + * Dump the stack + */ + if (STACK_MAGIC != *(u32 *)current->kernel_stack_page) + printk("Corrupted stack page\n"); + printk("Process %s (pid: %d, stackpage=%08lx)\nStack: ", + current->comm, current->pid, current->kernel_stack_page); + for(i=0;i<5;i++) + printk("%08x ", *sp++); + stack = (int *) sp; + for(i=0; i < kstack_depth_to_print; i++) { + if (((u32) stack & (PAGE_SIZE -1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", get_user_long(stack++)); + } + printk("\nCall Trace: "); + stack = (int *)sp; + i = 1; + module_start = VMALLOC_START; + module_end = module_start + MODULE_RANGE; + while (((u32)stack & (PAGE_SIZE -1)) != 0) { + addr = get_user_long(stack++); + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (u32) &start_kernel) && + (addr <= (u32) &_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08x ", addr); + i++; + } + } + + printk("\nCode : "); + for(i=0;i<5;i++) + printk("%08x ", *pc++); + printk("\n"); + do_exit(SIGSEGV); +} + +static void +fix_ade(struct pt_regs *regs, int write) +{ + printk("Received address error (ade%c)\n", write ? 's' : 'l'); + panic("Fixing address errors not implemented yet"); +} + +void do_adel(struct pt_regs *regs) +{ + unsigned long pc = regs->cp0_epc; + int i; + + if(fix_ade_enabled) + { + fix_ade(regs, 0); + return; + } +#if 0 + for(i=0; i<NR_TASKS;i++) + if(task[i] && task[i]->pid >= 2) + { + printk("Process %d\n", task[i]->pid); + dump_list_process(task[i], pc); + } +#endif + show_regs(regs); +while(1); + dump_tlb_nonwired(); + send_sig(SIGSEGV, current, 1); +} + +void do_ades(struct pt_regs *regs) +{ + unsigned long pc = regs->cp0_epc; + int i; + + if(fix_ade_enabled) + { + fix_ade(regs, 1); + return; + } +while(1); + for(i=0; i<NR_TASKS;i++) + if(task[i] && task[i]->pid >= 2) + { + printk("Process %d\n", task[i]->pid); + dump_list_process(task[i], pc); + } + show_regs(regs); + dump_tlb_nonwired(); + send_sig(SIGSEGV, current, 1); +} + +/* + * The ibe/dbe exceptions are signaled by onboard hardware and should get + * a board specific handlers to get maximum available information. Bus + * errors are always symptom of hardware malfunction or a kernel error. + * + * FIXME: Linux/68k sends a SIGSEGV for a buserror which seems to be wrong. + * This is certainly wrong. Actually, all hardware errors (ades,adel,ibe,dbe) + * are bus errors and therefor should send a SIGBUS! (Andy) + */ +void do_ibe(struct pt_regs *regs) +{ +while(1); + send_sig(SIGBUS, current, 1); +} + +void do_dbe(struct pt_regs *regs) +{ +while(1); + send_sig(SIGBUS, current, 1); +} + +void do_ov(struct pt_regs *regs) +{ +while(1); + send_sig(SIGFPE, current, 1); +} + +void do_fpe(struct pt_regs *regs) +{ +while(1); + send_sig(SIGFPE, current, 1); +} + +void do_bp(struct pt_regs *regs) +{ +while(1); + send_sig(SIGILL, current, 1); +} + +void do_tr(struct pt_regs *regs) +{ +while(1); + send_sig(SIGILL, current, 1); +} + +void do_ri(struct pt_regs *regs) +{ + int i; + + for(i=0; i<NR_TASKS;i++) + if(task[i] && task[i]->pid >= 2) + { + printk("Process %d\n", task[i]->pid); + dump_list_process(task[i], 0x7ffff000); + } + show_regs(regs); +while(1); + send_sig(SIGILL, current, 1); +} + +void do_cpu(struct pt_regs *regs) +{ + unsigned int cpid; + + cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; + switch(cpid) + { + case 1: + regs->cp0_status |= ST0_CU1; + break; + case 3: + /* + * This is a guess how to handle MIPS IV - + * I don't have a manual. + */ + if((boot_info.cputype == CPU_R8000) || + (boot_info.cputype == CPU_R10000)) + { + regs->cp0_status |= ST0_CU3; + break; + } + case 0: + /* + * CPU for cp0 can only happen in user mode + */ + case 2: + send_sig(SIGILL, current, 1); + break; + } +} + +void do_vcei(struct pt_regs *regs) +{ + /* + * Only possible on R4[04]00[SM]C. No handler because + * I don't have such a cpu. + */ + panic("Caught VCEI exception - can't handle yet\n"); +} + +void do_vced(struct pt_regs *regs) +{ + /* + * Only possible on R4[04]00[SM]C. No handler because + * I don't have such a cpu. + */ + panic("Caught VCED exception - can't handle yet\n"); +} + +void do_watch(struct pt_regs *regs) +{ + panic("Caught WATCH exception - can't handle yet\n"); +} + +void do_reserved(struct pt_regs *regs) +{ + /* + * Game over - no way to handle this if it ever occurs. + * Most probably caused by a new unknown cpu type or + * after another deadly hard/software error. + */ + panic("Caught reserved exception - can't handle.\n"); +} + +void trap_init(void) +{ + unsigned long i; + + if(boot_info.machtype == MACH_MIPS_MAGNUM_4000) + EISA_bus = 1; + + /* + * Setup default vectors + */ + for (i=0;i<=31;i++) + set_except_vector(i, handle_reserved); + + /* + * Handling the following exceptions depends mostly of the cpu type + */ + switch(boot_info.cputype) { + case CPU_R4000MC: + case CPU_R4400MC: + case CPU_R4000SC: + case CPU_R4400SC: + /* + * Handlers not implemented yet. If should every be used + * it's a bug in the Linux/MIPS kernel, anyway. + */ + set_except_vector(14, handle_vcei); + set_except_vector(31, handle_vced); + case CPU_R4000PC: + case CPU_R4400PC: + case CPU_R4200: + /* case CPU_R4300: */ + /* + * Use watch exception to trap on access to address zero + */ + set_except_vector(23, handle_watch); + watch_set(KSEG0, 3); + case CPU_R4600: + set_except_vector(1, handle_mod); + set_except_vector(2, handle_tlbl); + set_except_vector(3, handle_tlbs); + set_except_vector(4, handle_adel); + set_except_vector(5, handle_ades); + /* + * The following two are signaled by onboard hardware and + * should get board specific handlers to get maximum + * available information. + */ + set_except_vector(6, handle_ibe); + set_except_vector(7, handle_dbe); + + set_except_vector(8, handle_sys); + set_except_vector(9, handle_bp); + set_except_vector(10, handle_ri); + set_except_vector(11, handle_cpu); + set_except_vector(12, handle_ov); + set_except_vector(13, handle_tr); + set_except_vector(15, handle_fpe); + + /* + * Compute mask for page_colour(). This is based on the + * size of the data cache. Does the size of the icache + * need to be accounted for? + */ + i = read_32bit_cp0_register(CP0_CONFIG); + i = (i >> 26) & 7; + page_colour_mask = 1 << (12 + i); + break; + case CPU_R2000: + case CPU_R3000: + case CPU_R3000A: + case CPU_R3041: + case CPU_R3051: + case CPU_R3052: + case CPU_R3081: + case CPU_R3081E: + case CPU_R6000: + case CPU_R6000A: + case CPU_R8000: + printk("Detected unsupported CPU type %s.\n", + cpu_names[boot_info.cputype]); + panic("Can't handle CPU\n"); + break; + + /* + * The R10000 is in most aspects similar to the R4400. It however + * should get some special optimizations. + */ + case CPU_R10000: + write_32bit_cp0_register(CP0_FRAMEMASK, 0); + panic("CPU too expensive - making holiday in the ANDES!"); + break; + case CPU_UNKNOWN: + default: + panic("Unknown CPU type"); + } + + /* + * The interrupt handler depends most of the board type. + */ + set_except_vector(0, feature->handle_int); +} diff --git a/arch/mips/kernel/tyne.S b/arch/mips/kernel/tyne.S new file mode 100644 index 000000000..912f6d414 --- /dev/null +++ b/arch/mips/kernel/tyne.S @@ -0,0 +1,114 @@ +/* + * arch/mips/kernel/tyne.S + * + * Deskstation Tyne specific Assembler code + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * written by Ralf Baechle and Andreas Busse + */ +#include <asm/asm.h> +#include <asm/mipsconfig.h> +#include <asm/mipsregs.h> +#include <asm/stackframe.h> + +/* + * Deskstation Tyne interrupt handler + */ + .text + .set noreorder + .set noat + .align 5 + NESTED(deskstation_tyne_handle_int, FR_SIZE, sp) + SAVE_ALL + CLI + .set at + lui s0,%hi(PORT_BASE) + li t1,0x0f + sb t1,%lo(PORT_BASE+0x20)(s0) # poll command + lb t1,%lo(PORT_BASE+0x20)(s0) # read result + li s1,1 + bgtz t1,Lpoll_second + andi t1,t1,7 + /* + * Acknowledge first pic + */ + lb t2,%lo(PORT_BASE+0x21)(s0) + lui s4,%hi(cache_21) + lb t0,%lo(cache_21)(s4) + sllv s1,s1,t1 + or t0,t0,s1 + sb t0,%lo(cache_21)(s4) + sb t0,%lo(PORT_BASE+0x21)(s0) + lui s3,%hi(intr_count) + lw t0,%lo(intr_count)(s3) + li t2,0x20 + sb t2,%lo(PORT_BASE+0x20)(s0) + /* + * Now call the real handler + */ + la t3,IRQ_vectors + sll t2,t1,2 + addu t3,t3,t2 + lw t3,(t3) + addiu t0,t0,1 + jalr t3 + sw t0,%lo(intr_count)(s3) # delay slot + lw t0,%lo(intr_count)(s3) + /* + * Unblock first pic + */ + lbu t1,%lo(PORT_BASE+0x21)(s0) + lb t1,%lo(cache_21)(s4) + subu t0,t0,1 + sw t0,%lo(intr_count)(s3) + nor s1,zero,s1 + and t1,t1,s1 + sb t1,%lo(cache_21)(s4) + jr v0 + sb t1,%lo(PORT_BASE+0x21)(s0) # delay slot + + .align 5 +Lpoll_second: li t1,0x0f + sb t1,%lo(PORT_BASE+0xa0)(s0) # poll command + lb t1,%lo(PORT_BASE+0xa0)(s0) # read result + lui s4,%hi(cache_A1) + bgtz t1,spurious_interrupt + andi t1,t1,7 + /* + * Acknowledge second pic + */ + lbu t2,%lo(PORT_BASE+0xa1)(s0) + lb t3,%lo(cache_A1)(s4) + sllv s1,s1,t1 + or t3,t3,s1 + sb t3,%lo(cache_A1)(s4) + sb t3,%lo(PORT_BASE+0xa1)(s0) + li t3,0x20 + sb t3,%lo(PORT_BASE+0xa0)(s0) + lui s3,%hi(intr_count) + lw t0,%lo(intr_count)(s3) + sb t3,%lo(PORT_BASE+0x20)(s0) + /* + * Now call the real handler + */ + la t0,IRQ_vectors + sll t2,t1,2 + addu t0,t0,t2 + lw t0,32(t0) + addiu t0,t0,1 + jalr t0 + sw t0,%lo(intr_count)(s3) # delay slot + lw t0,%lo(intr_count)(s3) + /* + * Unblock second pic + */ + lb t1,%lo(PORT_BASE+0xa1)(s0) + lb t1,%lo(cache_A1)(s4) + subu t0,t0,1 + lw t0,%lo(intr_count)(s3) + nor s1,zero,s1 + and t1,t1,s1 + sb t1,%lo(cache_A1)(s4) + jr v0 + sb t1,%lo(PORT_BASE+0xa1)(s0) # delay slot + END(deskstation_tyne_handle_int) diff --git a/arch/mips/kernel/tynedma.c b/arch/mips/kernel/tynedma.c new file mode 100644 index 000000000..04846cddd --- /dev/null +++ b/arch/mips/kernel/tynedma.c @@ -0,0 +1,36 @@ +/* + * Tiny Tyne DMA buffer allocator + * + * Copyright (C) 1995 Ralf Baechle + */ +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/bootinfo.h> + +#ifdef CONFIG_DESKSTATION_TYNE + +static unsigned long allocated; + +/* + * Not very sophisticated, but should suffice for now... + */ +unsigned long deskstation_tyne_dma_alloc(size_t size) +{ + unsigned long ret = allocated; + allocated += size; + if (allocated > boot_info.dma_cache_size) + ret = -1; + return ret; +} + +void deskstation_tyne_dma_init(void) +{ + if (boot_info.machtype != MACH_DESKSTATION_TYNE) + return; + allocated = 0; + printk ("Deskstation Tyne DMA (%luk) buffer initialized.\n", + boot_info.dma_cache_size >> 10); +} + +#endif /* CONFIG_DESKSTATION_TYNE */ diff --git a/arch/mips/vm86.c b/arch/mips/kernel/vm86.c index 454b35fe0..454b35fe0 100644 --- a/arch/mips/vm86.c +++ b/arch/mips/kernel/vm86.c diff --git a/arch/mips/ldt.c b/arch/mips/ldt.c deleted file mode 100644 index 089605cee..000000000 --- a/arch/mips/ldt.c +++ /dev/null @@ -1,13 +0,0 @@ -/* - * arch/mips/ldt.c - * - * Copyright (C) 1994 by Waldorf GMBH, - * written by Ralf Baechle - */ -#include <linux/linkage.h> -#include <linux/errno.h> - -asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) -{ - return -ENOSYS; -} diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile new file mode 100644 index 000000000..56279fb3e --- /dev/null +++ b/arch/mips/lib/Makefile @@ -0,0 +1,33 @@ +# +# Makefile for MIPS-specific library files.. +# + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) $(ASFLAGS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +OBJS = dump_tlb.o tinycon.o watch.o + +include ../../../.config + +lib.a: $(OBJS) + $(AR) rcs lib.a $(OBJS) + sync + +dep: + $(CPP) -M *.[cS] > .depend + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + diff --git a/arch/mips/lib/dump_tlb.c b/arch/mips/lib/dump_tlb.c new file mode 100644 index 000000000..f730b9fce --- /dev/null +++ b/arch/mips/lib/dump_tlb.c @@ -0,0 +1,161 @@ +/* + * Dump R4x00 TLB for debugging purposes. + * + * Copyright (C) 1994, 1995 by Waldorf Electronics, + * written by Ralf Baechle. + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> + +#include <asm/bootinfo.h> +#include <asm/cachectl.h> +#include <asm/mipsregs.h> + +static void +dump_tlb(int first, int last) +{ + int i; + int wired; + unsigned int pagemask; + unsigned long long entryhi, entrylo0, entrylo1; + + wired = read_32bit_cp0_register(CP0_WIRED); + printk("Wired: %d", wired); + + for(i=first;i<last;i++) + { + write_32bit_cp0_register(CP0_INDEX, i); + __asm__ __volatile__( + ".set\tmips3\n\t" + ".set\tnoreorder\n\t" + "nop;nop;nop;nop\n\t" + "tlbr\n\t" + "nop;nop;nop;nop\n\t" + ".set\treorder\n\t" + ".set\tmips0\n\t"); + pagemask = read_32bit_cp0_register(CP0_PAGEMASK); + entryhi = read_64bit_cp0_register(CP0_ENTRYHI); + entrylo0 = read_64bit_cp0_register(CP0_ENTRYLO0); + entrylo1 = read_64bit_cp0_register(CP0_ENTRYLO1); + + if((entrylo0|entrylo1) & 2) + { + /* + * Only print entries in use + */ + printk("\nIndex: %2d %08x", i, pagemask); + + printk(" %08x %08x", (unsigned int)(entryhi >> 32), + (unsigned int) entryhi); + printk(" %08x %08x", (unsigned int)(entrylo0 >> 32), + (unsigned int) entrylo0); + printk(" %08x %08x", (unsigned int)(entrylo1 >> 32), + (unsigned int) entrylo1); + } + } + printk("\n"); +} + +void +dump_tlb_all(void) +{ + dump_tlb(0, boot_info.tlb_entries - 1); +} + +void +dump_tlb_nonwired(void) +{ + dump_tlb(read_32bit_cp0_register(CP0_WIRED), boot_info.tlb_entries - 1); +} + +#include <linux/kernel.h> +#include <linux/mm.h> + +#include <asm/mipsconfig.h> +#include <asm/page.h> +#include <asm/pgtable.h> + +void +dump_list_process(struct task_struct *t, void *address) +{ + pgd_t *page_dir, *pgd; + pmd_t *pmd; + pte_t *pte, page; + unsigned int addr; + + addr = (unsigned int) address; + printk("Addr == %08x\n", addr); + + printk("tasks->tss.pg_dir == %08x\n", + (unsigned int) t->tss.pg_dir); + + page_dir = pgd_offset(t, 0); + printk("page_dir == %08x\n", (unsigned int) page_dir); + + pgd = pgd_offset(t, addr); + printk("pgd == %08x, ", (unsigned int) pgd); + + pmd = pmd_offset(pgd, addr); + printk("pmd == %08x, ", (unsigned int) pmd); + + pte = pte_offset(pmd, addr); + printk("pte == %08x, ", (unsigned int) pte); + + page = *pte; + printk("page == %08x\n", (unsigned int) pte_val(page)); + +} + +void +dump_list_current(void *address) +{ + unsigned int addr, *pt; + + dump_list_process(current, address); + + addr = (unsigned int) address; + pt = (unsigned int *) TLB_ROOT + (addr >> 22); + printk("L1: *%08x == ", (unsigned int) pt); + printk("%08x, ", *pt); + + pt = (unsigned int *) (TLBMAP + ((addr >> 10) & ~3)); + printk("L2: *%08x == ", (unsigned int) pt); + printk("%08x\n", *pt); +} + +#include <asm/segment.h> + +unsigned int +vtop(void *address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned int addr, paddr; + + addr = (unsigned int) address; + pgd = pgd_offset(current, addr); + pmd = pmd_offset(pgd, addr); + pte = pte_offset(pmd, addr); + paddr = (KSEG1 | (unsigned int) pte_val(*pte)) & PAGE_MASK; + paddr |= (addr & ~PAGE_MASK); + + return paddr; +} + +void +dump16(unsigned long *p) +{ + int i; + + for(i=0;i<8;i++) + { + printk("*%08lx == %08lx, ", + (unsigned long)p, (unsigned long)*p++); + printk("*%08lx == %08lx\n", + (unsigned long)p, (unsigned long)*p++); + } +} + diff --git a/arch/mips/lib/tinycon.c b/arch/mips/lib/tinycon.c new file mode 100644 index 000000000..4410986d8 --- /dev/null +++ b/arch/mips/lib/tinycon.c @@ -0,0 +1,133 @@ +/* ---------------------------------------------------------------------- + * console.c + * + * Copyright (C) 1994 by Waldorf Electronic, + * written by Ralf Baechle and Andreas Busse + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * ---------------------------------------------------------------------- */ +/* + * FIXME: This file is hacked to be hardwired for the Deskstation + * Only thought as a debugging console output + */ + +#include <linux/tty.h> +#include <asm/bootinfo.h> + +static unsigned int size_x; +static unsigned int size_y; +static unsigned short cursor_x; +static unsigned short cursor_y; +static volatile unsigned short *vram_addr; +static int console_needs_init = 1; + +extern struct bootinfo boot_info; +extern struct screen_info screen_info; + +/* ---------------------------------------------------------------------- + * init_console() + * ---------------------------------------------------------------------- */ + +void init_console(void) +{ + size_x = 80; + size_y = 50; + cursor_x = 0; + cursor_y = 0; + + vram_addr = (unsigned short *)0xe10b8000; + + console_needs_init = 0; +} + +void +set_size_x(unsigned int x) +{ + size_x = x; +} + +void +set_size_y(unsigned int y) +{ + size_y = y; +} + +void +set_vram(unsigned short *vram) +{ + vram_addr = vram; +} + +void +set_cursor(unsigned int x, unsigned int y) +{ + cursor_x = x; + cursor_y = y; +} + +void +print_char(unsigned int x, unsigned int y, unsigned char c) +{ + volatile unsigned short *caddr; + + caddr = vram_addr + (y * size_x) + x; + *caddr = (*caddr & 0xff00) | 0x0f00 | (unsigned short) c; +} + +static void +scroll(void) +{ + volatile unsigned short *caddr; + register int i; + + caddr = vram_addr; + for(i=0; i<size_x * (size_y-1); i++) + *(caddr++) = *(caddr + size_x); + + /* blank last line */ + + caddr = vram_addr + (size_x * (size_y-1)); + for(i=0; i<size_x; i++) + *(caddr++) = (*caddr & 0xff00) | (unsigned short) ' '; +} + +void print_string(const unsigned char *str) +{ + unsigned char c; + + if (console_needs_init) + init_console(); + + while((c = *str++)) + switch(c) + { + case '\n': + cursor_x = 0; + cursor_y++; + if(cursor_y == size_y) + { + scroll(); + cursor_y = size_y - 1; + } + break; + + default: + print_char(cursor_x, cursor_y, c); + cursor_x++; + if(cursor_x == size_x) + { + cursor_x = 0; + cursor_y++; + if(cursor_y == size_y) + { + scroll(); + cursor_y = size_y - 1; + } + } + break; + } +} + +/* end of file */ diff --git a/arch/mips/lib/watch.S b/arch/mips/lib/watch.S new file mode 100644 index 000000000..36b54d5c0 --- /dev/null +++ b/arch/mips/lib/watch.S @@ -0,0 +1,102 @@ +/* + * Kernel debug stuff to use the Watch registers. + * Usefull to find stack overflows etc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1995 Ralf Baechle + */ +#include <asm/asm.h> +#include <asm/mipsregs.h> + + .set noreorder +/* + * Parameter: a0 - logic address to watch + * Currently only KSEG0 addresses are allowed! + * a1 - set bit #1 to trap on load references + * bit #0 to trap on store references + * Results : none + */ + LEAF(watch_set) + li t0,0x80000000 + subu a0,t0 + ori a0,7 + xori a0,7 + or a0,a1 + mtc0 a0,CP0_WATCHLO + jr ra + mtc0 zero,CP0_WATCHHI + END(watch_set) + +/* + * The stuff below are just some kernel debugging gadgets. It will + * go away. + */ + +/* + * Parameter: none + * Results : none + */ + LEAF(watch_clear) + jr ra + mtc0 zero,CP0_WATCHLO + END(watch_clear) + +/* + * Parameter: none + * Results : none + */ + LEAF(get_sp) + jr ra + move v0,sp + END(get_sp) + +/* + * Parameter: none + * Results : none + */ + LEAF(get_ra) + jr ra + move v0,ra + END(get_ra) + +/* + * Parameter: none + * Results : none + */ + LEAF(get_status) + jr ra + mfc0 v0,CP0_STATUS + END(get_status) + +/* + * Parameter: none + * Results : none + */ + NESTED(print_sp, 24, sp) + .mask 0x80000000,16 + subu sp,24 + sw ra,16(sp) + move a1,sp + PRINT("$sp == %08lx\n") + lw ra,16(sp) + jr ra + addiu sp,24 + END(print_sp) + +/* + * Parameter: none + * Results : none + */ + NESTED(print_st, 24, sp) + .mask 0x80000000,16 + subu sp,24 + sw ra,16(sp) + mfc0 a1,CP0_STATUS + PRINT("cp0_status == %08lx\n") + lw ra,16(sp) + jr ra + addiu sp,24 + END(print_st) diff --git a/arch/mips/main.c b/arch/mips/main.c deleted file mode 100644 index 8cb92d5f2..000000000 --- a/arch/mips/main.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * arch/mips/main.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * MIPSified by Ralf Baechle - */ - -#include <stdarg.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/bootinfo.h> - -#include <linux/types.h> -#include <linux/fcntl.h> -#include <linux/config.h> -#include <linux/sched.h> -#include <linux/tty.h> -#include <linux/head.h> -#include <linux/unistd.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/ctype.h> -#include <linux/delay.h> -#include <linux/utsname.h> -#include <linux/ioport.h> - -extern unsigned long * prof_buffer; -extern unsigned long prof_len; -extern char edata, end; -extern char *linux_banner; - -/* - * we need this inline - forking from kernel space will result - * in NO COPY ON WRITE (!!!), until an execve is executed. This - * is no problem, but for the stack. This is handled by not letting - * main() use the stack at all after fork(). Thus, no function - * calls - which means inline code for fork too, as otherwise we - * would use the stack upon exit from 'fork()'. - * - * Actually only pause and fork are needed inline, so that there - * won't be any messing with the stack from main(), but we define - * some others too. - */ -#define __NR__exit __NR_exit -static inline _syscall0(int,idle) -static inline _syscall0(int,fork) - -extern int console_loglevel; - -extern char empty_zero_page[PAGE_SIZE]; -extern void init(void); -extern void init_IRQ(void); -extern void init_modules(void); -extern long console_init(long, long); -extern long kmalloc_init(long,long); -extern long blk_dev_init(long,long); -extern long chr_dev_init(long,long); -extern void floppy_init(void); -extern void sock_init(void); -extern long rd_init(long mem_start, int length); -unsigned long net_dev_init(unsigned long, unsigned long); -#if 0 -extern long bios32_init(long, long); -#endif - -extern void hd_setup(char *str, int *ints); -extern void bmouse_setup(char *str, int *ints); -extern void eth_setup(char *str, int *ints); -extern void xd_setup(char *str, int *ints); -extern void mcd_setup(char *str, int *ints); -extern void st_setup(char *str, int *ints); -extern void st0x_setup(char *str, int *ints); -extern void tmc8xx_setup(char *str, int *ints); -extern void t128_setup(char *str, int *ints); -extern void pas16_setup(char *str, int *ints); -extern void generic_NCR5380_setup(char *str, int *intr); -extern void aha152x_setup(char *str, int *ints); -extern void aha1542_setup(char *str, int *ints); -extern void aha274x_setup(char *str, int *ints); -extern void scsi_luns_setup(char *str, int *ints); -extern void sound_setup(char *str, int *ints); -#ifdef CONFIG_SBPCD -extern void sbpcd_setup(char *str, int *ints); -#endif CONFIG_SBPCD -#ifdef CONFIG_CDU31A -extern void cdu31a_setup(char *str, int *ints); -#endif CONFIG_CDU31A -void ramdisk_setup(char *str, int *ints); - -#ifdef CONFIG_SYSVIPC -extern void ipc_init(void); -#endif -#ifdef CONFIG_SCSI -extern unsigned long scsi_dev_init(unsigned long, unsigned long); -#endif - -/* - * This is set up by the setup-routine at boot-time - */ -#define PARAM empty_zero_page -#define EXT_MEM_K (*(unsigned short *) (PARAM+2)) -#define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) -#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) -#define RAMDISK_SIZE (*(unsigned short *) (PARAM+0x1F8)) -#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) -#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) - -/* - * Defaults, may be overwritten by milo - */ -#define SCREEN_INFO {0,0,{0,0},52,3,80,4626,3,9,50} - -/* - * Information passed by milo - */ -struct bootinfo boot_info; -struct screen_info screen_info = SCREEN_INFO; - -/* - * Boot command-line arguments - */ -extern void copy_options(char * to, char * from); -void parse_options(char *line); -#define MAX_INIT_ARGS 8 -#define MAX_INIT_ENVS 8 -#define COMMAND_LINE ((char *) (PARAM+2048)) -#define COMMAND_LINE_SIZE 256 - -extern void time_init(void); - -static unsigned long memory_start = 0; /* After mem_init, stores the */ - /* amount of free user memory */ -/* static */ unsigned long memory_end = 0; -/* static unsigned long low_memory_start = 0; */ - -int rows, cols; - -struct drive_info_struct { char dummy[32]; } drive_info; - -unsigned char aux_device_present; -int ramdisk_size; -int root_mountflags; - -static char command_line[COMMAND_LINE_SIZE] = { 0, }; - -struct { - char *str; - void (*setup_func)(char *, int *); -} bootsetups[] = { - { "ramdisk=", ramdisk_setup }, -#ifdef CONFIG_INET - { "ether=", eth_setup }, -#endif -#ifdef CONFIG_SCSI - { "max_scsi_luns=", scsi_luns_setup }, -#endif -#ifdef CONFIG_BLK_DEV_HD - { "hd=", hd_setup }, -#endif -#ifdef CONFIG_CHR_DEV_ST - { "st=", st_setup }, -#endif -#ifdef CONFIG_BUSMOUSE - { "bmouse=", bmouse_setup }, -#endif -#ifdef CONFIG_SCSI_SEAGATE - { "st0x=", st0x_setup }, - { "tmc8xx=", tmc8xx_setup }, -#endif -#ifdef CONFIG_SCSI_T128 - { "t128=", t128_setup }, -#endif -#ifdef CONFIG_SCSI_PAS16 - { "pas16=", pas16_setup }, -#endif -#ifdef CONFIG_SCSI_GENERIC_NCR5380 - { "ncr5380=", generic_NCR5380_setup }, -#endif -#ifdef CONFIG_SCSI_AHA152X - { "aha152x=", aha152x_setup}, -#endif -#ifdef CONFIG_SCSI_AHA1542 - { "aha1542=", aha1542_setup}, -#endif -#ifdef CONFIG_SCSI_AHA274X - { "aha274x=", aha274x_setup}, -#endif -#ifdef CONFIG_BLK_DEV_XD - { "xd=", xd_setup }, -#endif -#ifdef CONFIG_MCD - { "mcd=", mcd_setup }, -#endif -#ifdef CONFIG_SOUND - { "sound=", sound_setup }, -#endif -#ifdef CONFIG_SBPCD - { "sbpcd=", sbpcd_setup }, -#endif CONFIG_SBPCD -#ifdef CONFIG_CDU31A - { "cdu31a=", cdu31a_setup }, -#endif CONFIG_CDU31A - { 0, 0 } -}; - -void ramdisk_setup(char *str, int *ints) -{ - if (ints[0] > 0 && ints[1] >= 0) - ramdisk_size = ints[1]; -} - -unsigned long loops_per_sec = 1; - -static void calibrate_delay(void) -{ - int ticks; - - printk("Calibrating delay loop.. "); - while (loops_per_sec <<= 1) { - /* wait for "start of" clock tick */ - ticks = jiffies; - while (ticks == jiffies) - /* nothing */; - /* Go .. */ - ticks = jiffies; - __delay(loops_per_sec); - ticks = jiffies - ticks; - if (ticks >= HZ) { - /* - * No assembler - should be ok - */ - loops_per_sec = (loops_per_sec * HZ) / ticks; - printk("ok - %lu.%02lu BogoMips\n", - loops_per_sec/500000, - (loops_per_sec/5000) % 100); - return; - } - } - printk("failed\n"); -} - -int parse_machine_options(char *line) -{ - /* - * No special MIPS options yet - */ - return 0; -} - -asmlinkage void start_kernel(void) -{ - /* - * Interrupts are still disabled. Do necessary setups, then - * enable them - */ - ROOT_DEV = ORIG_ROOT_DEV; - drive_info = DRIVE_INFO; - aux_device_present = AUX_DEVICE_INFO; -#if 0 - memory_end = (1<<20) + (EXT_MEM_K<<10); -#else - memory_end = 0x80800000; -#endif - memory_end &= PAGE_MASK; - ramdisk_size = RAMDISK_SIZE; - copy_options(command_line,COMMAND_LINE); - - if (MOUNT_ROOT_RDONLY) - root_mountflags |= MS_RDONLY; - memory_start = 0x7fffffff & (unsigned long) &end; - - memory_start = paging_init(memory_start,memory_end); - trap_init(); - init_IRQ(); - sched_init(); - parse_options(command_line); - init_modules(); -#ifdef CONFIG_PROFILE - prof_buffer = (unsigned long *) memory_start; - prof_len = (unsigned long) &end; - prof_len >>= 2; - memory_start += prof_len * sizeof(unsigned long); -#endif - memory_start = console_init(memory_start,memory_end); - memory_start = kmalloc_init(memory_start,memory_end); - memory_start = chr_dev_init(memory_start,memory_end); - memory_start = blk_dev_init(memory_start,memory_end); - sti(); - calibrate_delay(); -#ifdef CONFIG_SCSI - memory_start = scsi_dev_init(memory_start,memory_end); -#endif -#ifdef CONFIG_INET - memory_start = net_dev_init(memory_start,memory_end); -#endif -while(1); - memory_start = inode_init(memory_start,memory_end); - memory_start = file_table_init(memory_start,memory_end); - memory_start = name_cache_init(memory_start,memory_end); - mem_init(memory_start,memory_end); - buffer_init(); - time_init(); - floppy_init(); - sock_init(); -#ifdef CONFIG_SYSVIPC - ipc_init(); -#endif - sti(); - - /* - * Get CPU type - * FIXME: Not implemented yet - */ - - printk(linux_banner); - - move_to_user_mode(); - if (!fork()) /* we count on this going ok */ - init(); -/* - * task[0] is meant to be used as an "idle" task: it may not sleep, but - * it might do some general things like count free pages or it could be - * used to implement a reasonable LRU algorithm for the paging routines: - * anything that can be useful, but shouldn't take time from the real - * processes. - * - * Right now task[0] just does a infinite idle loop. - */ - for(;;) - idle(); -} diff --git a/arch/mips/mkdisk b/arch/mips/mkdisk new file mode 100644 index 000000000..468d34727 --- /dev/null +++ b/arch/mips/mkdisk @@ -0,0 +1,5 @@ +#!/bin/sh +cp vmlinux vmlinux.tmp +mipsel-linux-strip -g -x vmlinux.tmp +mwrite -n vmlinux.tmp a:vmlinux +rm -f vmlinux.tmp diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile index 5063d60c2..6ff21fafd 100644 --- a/arch/mips/mm/Makefile +++ b/arch/mips/mm/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the linux memory manager. +# Makefile for the linux mips-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here @@ -14,11 +14,13 @@ .c.s: $(CC) $(CFLAGS) -S $< -OBJS = memory.o swap.o mmap.o mprotect.o kmalloc.o vmalloc.o +OBJS = fault.o init.o mm.o: $(OBJS) $(LD) -r -o mm.o $(OBJS) +modules: + dep: $(CPP) -M *.c > .depend diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c new file mode 100644 index 000000000..9256025d9 --- /dev/null +++ b/arch/mips/mm/fault.c @@ -0,0 +1,92 @@ +/* + * arch/mips/mm/fault.c + * + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * Ported to MIPS by Ralf Baechle + */ +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> + +extern void die_if_kernel(char *, struct pt_regs *, long); + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + */ +asmlinkage void +do_page_fault(struct pt_regs *regs, unsigned long writeaccess) +{ + struct vm_area_struct * vma; + unsigned long address; + + /* get the address */ + __asm__(".set\tmips3\n\t" + "dmfc0\t%0,$8\n\t" + ".set\tmips0" + : "=r" (address)); + +#if 0 + printk("do_page_fault() #1: %s %08lx (epc == %08lx)\n", + writeaccess ? "writeaccess to" : "readaccess from", + address, regs->cp0_epc); +#endif + vma = find_vma(current, 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) + 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: + if (writeaccess) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } else { + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + handle_mm_fault(vma, address, writeaccess); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + if (user_mode(regs)) { + current->tss.cp0_badvaddr = address; + current->tss.error_code = writeaccess; + send_sig(SIGSEGV, current, 1); + return; + } + /* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + printk(KERN_ALERT "Unable to handle kernel paging request at virtual address %08lx", + address); + die_if_kernel("Oops", regs, writeaccess); + do_exit(SIGKILL); +} diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c new file mode 100644 index 000000000..37912e2d0 --- /dev/null +++ b/arch/mips/mm/init.c @@ -0,0 +1,299 @@ +/* + * arch/mips/mm/init.c + * + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * Ported to MIPS by Ralf Baechle + */ +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/cachectl.h> +#include <asm/vector.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> + +extern void deskstation_tyne_dma_init(void); +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); + +extern char empty_zero_page[PAGE_SIZE]; + +/* + * 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 + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +pte_t * __bad_pagetable(void) +{ + extern char empty_bad_page_table[PAGE_SIZE]; + unsigned long page; + unsigned long dummy1, dummy2; + + page = ((unsigned long)empty_bad_page_table) + (PT_OFFSET - PAGE_OFFSET); +#ifdef __R4000__ + /* + * Use 64bit code even for Linux/MIPS 32bit on R4000 + */ + __asm__ __volatile__( + ".set\tnoreorder\n" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "dsll32\t$1,%2,0\n\t" + "dsrl32\t%2,$1,0\n\t" + "or\t%2,$1\n" + "1:\tsd\t%2,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,8\n\t" + ".set\tmips0\n\t" + ".set\tat\n" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"r" (pte_val(BAD_PAGE)), + "0" (page), + "1" (PAGE_SIZE/8)); +#else + __asm__ __volatile__( + ".set\tnoreorder\n" + "1:\tsw\t%2,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,4\n\t" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"r" (pte_val(BAD_PAGE)), + "0" (page), + "1" (PAGE_SIZE/4)); +#endif + + return (pte_t *)page; +} + +static inline void +__zeropage(unsigned long page) +{ + unsigned long dummy1, dummy2; + +#ifdef __R4000__ + /* + * Use 64bit code even for Linux/MIPS 32bit on R4000 + */ + __asm__ __volatile__( + ".set\tnoreorder\n" + ".set\tnoat\n\t" + ".set\tmips3\n" + "1:\tsd\t$0,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,8\n\t" + ".set\tmips0\n\t" + ".set\tat\n" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"0" (page), + "1" (PAGE_SIZE/8)); +#else + __asm__ __volatile__( + ".set\tnoreorder\n" + "1:\tsw\t$0,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,4\n\t" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"0" (page), + "1" (PAGE_SIZE/4)); +#endif +} + +static inline void +zeropage(unsigned long page) +{ + sys_cacheflush((void *)page, PAGE_SIZE, BCACHE); + sync_mem(); + __zeropage(page + (PT_OFFSET - PAGE_OFFSET)); +} + +pte_t __bad_page(void) +{ + extern char empty_bad_page[PAGE_SIZE]; + unsigned long page = (unsigned long)empty_bad_page; + + zeropage(page); + return pte_mkdirty(mk_pte(page, PAGE_SHARED)); +} + +unsigned long __zero_page(void) +{ + unsigned long page = (unsigned long) empty_zero_page; + + zeropage(page); + return page; +} + +/* + * This is horribly inefficient ... + */ +void __copy_page(unsigned long from, unsigned long to) +{ + /* + * Now copy page from uncached KSEG1 to KSEG0. The copy destination + * is in KSEG0 so that we keep stupid L2 caches happy. + */ + if(from == (unsigned long) empty_zero_page) + { + /* + * The page copied most is the COW empty_zero_page. Since we + * know it's contents we can avoid the writeback reading of + * the page. Speeds up the standard case alot. + */ + __zeropage(to); + } + else + { + /* + * Force writeback of old page to memory. We don't know the + * virtual address, so we have to flush the entire cache ... + */ + sys_cacheflush(0, ~0, DCACHE); + sync_mem(); + memcpy((void *) to, + (void *) (from + (PT_OFFSET - PAGE_OFFSET)), PAGE_SIZE); + } + /* + * Now writeback the page again if colour has changed. + * Actually this does a Hit_Writeback, but due to an artifact in + * the R4xx0 implementation this should be slightly faster. + * Then sweep chipset controlled secondary caches and the ICACHE. + */ + if (page_colour(from) != page_colour(to)) + sys_cacheflush(0, ~0, DCACHE); + sys_cacheflush(0, ~0, ICACHE); +} + +void show_mem(void) +{ + int i,free = 0,total = 0; + int shared = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); + i = (high_memory - PAGE_OFFSET) >> PAGE_SHIFT; + while (i-- > 0) { + total++; + if (!mem_map[i]) + free++; + else + shared += mem_map[i]-1; + } + printk("%d pages of RAM\n", total); + printk("%d free pages\n", free); + printk("%d pages shared\n", shared); + show_buffers(); +#ifdef CONFIG_NET + show_net_buffers(); +#endif +} + +extern unsigned long free_area_init(unsigned long, unsigned long); + +unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) +{ + pgd_init((unsigned long)swapper_pg_dir - (PT_OFFSET - PAGE_OFFSET)); + return free_area_init(start_mem, end_mem); +} + +void mem_init(unsigned long start_mem, unsigned long end_mem) +{ + int codepages = 0; + int datapages = 0; + unsigned long tmp; + extern int _etext; + + end_mem &= PAGE_MASK; + high_memory = end_mem; + + /* mark usable pages in the mem_map[] */ + start_mem = PAGE_ALIGN(start_mem); + + tmp = start_mem; + while (tmp < high_memory) { + mem_map[MAP_NR(tmp)] = 0; + tmp += PAGE_SIZE; + } +#ifdef CONFIG_DESKSTATION_TYNE + deskstation_tyne_dma_init(); +#endif +#ifdef CONFIG_SCSI + scsi_mem_init(high_memory); +#endif +#ifdef CONFIG_SOUND + sound_mem_init(); +#endif + for (tmp = PAGE_OFFSET ; tmp < high_memory ; tmp += PAGE_SIZE) { + if (mem_map[MAP_NR(tmp)]) { + if (tmp < (unsigned long) &_etext) + codepages++; + else if (tmp < start_mem) + datapages++; + continue; + } + mem_map[MAP_NR(tmp)] = 1; + free_page(tmp); + } + tmp = nr_free_pages << PAGE_SHIFT; + printk("Memory: %luk/%luk available (%dk kernel code, %dk data)\n", + tmp >> 10, + (high_memory - PAGE_OFFSET) >> 10, + codepages << (PAGE_SHIFT-10), + datapages << (PAGE_SHIFT-10)); + + return; +} + +void si_meminfo(struct sysinfo *val) +{ + int i; + + i = high_memory >> PAGE_SHIFT; + 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) + continue; + val->totalram++; + if (!mem_map[i]) + continue; + val->sharedram += mem_map[i]-1; + } + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + return; +} diff --git a/arch/mips/mm/kmalloc.c b/arch/mips/mm/kmalloc.c deleted file mode 100644 index 018f8db8f..000000000 --- a/arch/mips/mm/kmalloc.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * linux/mm/kmalloc.c - * - * Copyright (C) 1991, 1992 Linus Torvalds & Roger Wolff. - * - * Written by R.E. Wolff Sept/Oct '93. - * - */ - -/* - * Modified by Alex Bligh (alex@cconcepts.co.uk) 4 Apr 1994 to use multiple - * pages. So for 'page' throughout, read 'area'. - */ - -#include <linux/mm.h> -#include <asm/system.h> -#include <linux/delay.h> - -#define GFP_LEVEL_MASK 0xf - -/* I want this low enough for a while to catch errors. - I want this number to be increased in the near future: - loadable device drivers should use this function to get memory */ - -#define MAX_KMALLOC_K ((PAGE_SIZE<<(NUM_AREA_ORDERS-1))>>10) - - -/* This defines how many times we should try to allocate a free page before - giving up. Normally this shouldn't happen at all. */ -#define MAX_GET_FREE_PAGE_TRIES 4 - - -/* Private flags. */ - -#define MF_USED 0xffaa0055 -#define MF_FREE 0x0055ffaa - - -/* - * Much care has gone into making these routines in this file reentrant. - * - * The fancy bookkeeping of nbytesmalloced and the like are only used to - * report them to the user (oooohhhhh, aaaaahhhhh....) are not - * protected by cli(). (If that goes wrong. So what?) - * - * These routines restore the interrupt status to allow calling with ints - * off. - */ - -/* - * A block header. This is in front of every malloc-block, whether free or not. - */ -struct block_header { - unsigned long bh_flags; - union { - unsigned long ubh_length; - struct block_header *fbh_next; - } vp; -}; - - -#define bh_length vp.ubh_length -#define bh_next vp.fbh_next -#define BH(p) ((struct block_header *)(p)) - - -/* - * The page descriptor is at the front of every page that malloc has in use. - */ -struct page_descriptor { - struct page_descriptor *next; - struct block_header *firstfree; - int order; - int nfree; -}; - - -#define PAGE_DESC(p) ((struct page_descriptor *)(((unsigned long)(p)) & PAGE_MASK)) - - -/* - * A size descriptor describes a specific class of malloc sizes. - * Each class of sizes has its own freelist. - */ -struct size_descriptor { - struct page_descriptor *firstfree; - int size; - int nblocks; - - int nmallocs; - int nfrees; - int nbytesmalloced; - int npages; - unsigned long gfporder; /* number of pages in the area required */ -}; - -/* - * For now it is unsafe to allocate bucket sizes between n & n=16 where n is - * 4096 * any power of two - */ - -struct size_descriptor sizes[] = { - { NULL, 32,127, 0,0,0,0, 0}, - { NULL, 64, 63, 0,0,0,0, 0 }, - { NULL, 128, 31, 0,0,0,0, 0 }, - { NULL, 252, 16, 0,0,0,0, 0 }, - { NULL, 508, 8, 0,0,0,0, 0 }, - { NULL,1020, 4, 0,0,0,0, 0 }, - { NULL,2040, 2, 0,0,0,0, 0 }, - { NULL,4096-16, 1, 0,0,0,0, 0 }, - { NULL,8192-16, 1, 0,0,0,0, 1 }, - { NULL,16384-16, 1, 0,0,0,0, 2 }, - { NULL,32768-16, 1, 0,0,0,0, 3 }, - { NULL,65536-16, 1, 0,0,0,0, 4 }, - { NULL,131072-16, 1, 0,0,0,0, 5 }, - { NULL, 0, 0, 0,0,0,0, 0 } -}; - - -#define NBLOCKS(order) (sizes[order].nblocks) -#define BLOCKSIZE(order) (sizes[order].size) -#define AREASIZE(order) (PAGE_SIZE<<(sizes[order].gfporder)) - - -long kmalloc_init (long start_mem,long end_mem) -{ - int order; - -/* - * Check the static info array. Things will blow up terribly if it's - * incorrect. This is a late "compile time" check..... - */ -for (order = 0;BLOCKSIZE(order);order++) - { - if ((NBLOCKS (order)*BLOCKSIZE(order) + sizeof (struct page_descriptor)) > - AREASIZE(order)) - { - printk ("Cannot use %d bytes out of %d in order = %d block mallocs\n", - NBLOCKS (order) * BLOCKSIZE(order) + - sizeof (struct page_descriptor), - (int) AREASIZE(order), - BLOCKSIZE (order)); - panic ("This only happens if someone messes with kmalloc"); - } - } -return start_mem; -} - - - -int get_order (int size) -{ - int order; - - /* Add the size of the header */ - size += sizeof (struct block_header); - for (order = 0;BLOCKSIZE(order);order++) - if (size <= BLOCKSIZE (order)) - return order; - return -1; -} - -void * kmalloc (size_t size, int priority) -{ - unsigned long flags; - int order,tries,i,sz; - struct block_header *p; - struct page_descriptor *page; - -/* Sanity check... */ - if (intr_count && priority != GFP_ATOMIC) { - static int count = 0; - if (++count < 5) { - printk("kmalloc called nonatomically from interrupt %p\n", - __builtin_return_address(0)); - priority = GFP_ATOMIC; - } - } - -order = get_order (size); -if (order < 0) - { - printk ("kmalloc of too large a block (%d bytes).\n",size); - return (NULL); - } - -save_flags(flags); - -/* It seems VERY unlikely to me that it would be possible that this - loop will get executed more than once. */ -tries = MAX_GET_FREE_PAGE_TRIES; -while (tries --) - { - /* Try to allocate a "recently" freed memory block */ - cli (); - if ((page = sizes[order].firstfree) && - (p = page->firstfree)) - { - if (p->bh_flags == MF_FREE) - { - page->firstfree = p->bh_next; - page->nfree--; - if (!page->nfree) - { - sizes[order].firstfree = page->next; - page->next = NULL; - } - restore_flags(flags); - - sizes [order].nmallocs++; - sizes [order].nbytesmalloced += size; - p->bh_flags = MF_USED; /* As of now this block is officially in use */ - p->bh_length = size; - return p+1; /* Pointer arithmetic: increments past header */ - } - printk ("Problem: block on freelist at %08lx isn't free.\n",(long)p); - return (NULL); - } - restore_flags(flags); - - - /* Now we're in trouble: We need to get a new free page..... */ - - sz = BLOCKSIZE(order); /* sz is the size of the blocks we're dealing with */ - - /* This can be done with ints on: This is private to this invocation */ - page = (struct page_descriptor *) __get_free_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder); - if (!page) { - static unsigned long last = 0; - if (last + 10*HZ < jiffies) { - last = jiffies; - printk ("Couldn't get a free page.....\n"); - } - return NULL; - } -#if 0 - printk ("Got page %08x to use for %d byte mallocs....",(long)page,sz); -#endif - sizes[order].npages++; - - /* Loop for all but last block: */ - for (i=NBLOCKS(order),p=BH (page+1);i > 1;i--,p=p->bh_next) - { - p->bh_flags = MF_FREE; - p->bh_next = BH ( ((long)p)+sz); - } - /* Last block: */ - p->bh_flags = MF_FREE; - p->bh_next = NULL; - - page->order = order; - page->nfree = NBLOCKS(order); - page->firstfree = BH(page+1); -#if 0 - printk ("%d blocks per page\n",page->nfree); -#endif - /* Now we're going to muck with the "global" freelist for this size: - this should be uninterruptible */ - cli (); - /* - * sizes[order].firstfree used to be NULL, otherwise we wouldn't be - * here, but you never know.... - */ - page->next = sizes[order].firstfree; - sizes[order].firstfree = page; - restore_flags(flags); - } - -/* Pray that printk won't cause this to happen again :-) */ - -printk ("Hey. This is very funny. I tried %d times to allocate a whole\n" - "new page for an object only %d bytes long, but some other process\n" - "beat me to actually allocating it. Also note that this 'error'\n" - "message is soooo very long to catch your attention. I'd appreciate\n" - "it if you'd be so kind as to report what conditions caused this to\n" - "the author of this kmalloc: wolff@dutecai.et.tudelft.nl.\n" - "(Executive summary: This can't happen)\n", - MAX_GET_FREE_PAGE_TRIES, - size); -return NULL; -} - - -void kfree_s (void *ptr,int size) -{ -unsigned long flags; -int order; -register struct block_header *p=((struct block_header *)ptr) -1; -struct page_descriptor *page,*pg2; - -page = PAGE_DESC (p); -order = page->order; -if ((order < 0) || - (order > sizeof (sizes)/sizeof (sizes[0])) || - (((long)(page->next)) & ~PAGE_MASK) || - (p->bh_flags != MF_USED)) - { - printk ("kfree of non-kmalloced memory: %p, next= %p, order=%d\n", - p, page->next, page->order); - return; - } -if (size && - size != p->bh_length) - { - printk ("Trying to free pointer at %p with wrong size: %d instead of %lu.\n", - p,size,p->bh_length); - return; - } -size = p->bh_length; -p->bh_flags = MF_FREE; /* As of now this block is officially free */ -save_flags(flags); -cli (); -p->bh_next = page->firstfree; -page->firstfree = p; -page->nfree ++; - -if (page->nfree == 1) - { /* Page went from full to one free block: put it on the freelist */ - if (page->next) - { - printk ("Page %p already on freelist dazed and confused....\n", page); - } - else - { - page->next = sizes[order].firstfree; - sizes[order].firstfree = page; - } - } - -/* If page is completely free, free it */ -if (page->nfree == NBLOCKS (page->order)) - { -#if 0 - printk ("Freeing page %08x.\n", (long)page); -#endif - if (sizes[order].firstfree == page) - { - sizes[order].firstfree = page->next; - } - else - { - for (pg2=sizes[order].firstfree; - (pg2 != NULL) && (pg2->next != page); - pg2=pg2->next) - /* Nothing */; - if (pg2 != NULL) - pg2->next = page->next; - else - printk ("Ooops. page %p doesn't show on freelist.\n", page); - } -/* FIXME: I'm sure we should do something with npages here (like npages--) */ - free_pages ((long)page, sizes[order].gfporder); - } -restore_flags(flags); - -/* FIXME: ?? Are these increment & decrement operations guaranteed to be - * atomic? Could an IRQ not occur between the read & the write? - * Maybe yes on a x86 with GCC...?? - */ -sizes[order].nfrees++; /* Noncritical (monitoring) admin stuff */ -sizes[order].nbytesmalloced -= size; -} diff --git a/arch/mips/mm/memory.c b/arch/mips/mm/memory.c deleted file mode 100644 index 5872f8bd5..000000000 --- a/arch/mips/mm/memory.c +++ /dev/null @@ -1,1295 +0,0 @@ -/* - * arch/mips/mm/memory.c - * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - * Ported to MIPS by Ralf Baechle - */ - -/* - * 05.04.94 - Multi-page memory management added for v1.1. - * Idea by Alex Bligh (alex@cconcepts.co.uk) - */ - -#include <linux/config.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/head.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/types.h> -#include <linux/ptrace.h> -#include <linux/mman.h> - -#include <asm/system.h> -#include <asm/segment.h> - -unsigned long high_memory = 0; - -extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */ -extern unsigned long invalid_pg_table[1024]; - -extern void sound_mem_init(void); -extern void die_if_kernel(char *,struct pt_regs *,long); -extern void show_net_buffers(void); - -/* - * The free_area_list arrays point to the queue heads of the free areas - * of different sizes - */ -int nr_swap_pages = 0; -int nr_free_pages = 0; -struct mem_list free_area_list[NR_MEM_LISTS]; -unsigned char * free_area_map[NR_MEM_LISTS]; - -unsigned short * mem_map = NULL; - -/* - * oom() prints a message (so that the user knows why the process died), - * and gives the process an untrappable SIGKILL. - */ -void oom(struct task_struct * task) -{ - printk("\nOut of memory.\n"); - task->sigaction[SIGKILL-1].sa_handler = NULL; - task->blocked &= ~(1<<(SIGKILL-1)); - send_sig(SIGKILL,task,1); -} - -static void free_one_table(unsigned long * page_dir) -{ - int j; - unsigned long pg_table = *page_dir; - unsigned long * page_table; - - if ((long) pg_table & PAGE_MASK != (long) invalid_pg_table & PAGE_MASK ) - return; - *page_dir = PAGE_VALID | (unsigned long) invalid_pg_table; - if (pg_table >= high_memory || !(pg_table & PAGE_VALID)) { - printk("Bad page table: [%p]=%08lx\n",page_dir,pg_table); - return; - } - if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED) - return; - page_table = (unsigned long *) (pg_table & PAGE_MASK); - for (j = 0 ; j < PTRS_PER_PAGE ; j++,page_table++) { - unsigned long pg = *page_table; - - if (!pg) - continue; - *page_table = 0; - if (pg & PAGE_VALID) - free_page(PAGE_MASK & pg); - else - swap_free(pg); - } - free_page(PAGE_MASK & pg_table); -} - -/* - * This function clears all user-level page tables of a process - this - * is needed by execve(), so that old pages aren't in the way. Note that - * unlike 'free_page_tables()', this function still leaves a valid - * page-table-tree in memory: it just removes the user pages. The two - * functions are similar, but there is a fundamental difference. - */ -void clear_page_tables(struct task_struct * tsk) -{ - int i; - unsigned long pg_dir; - unsigned long * page_dir; - - if (!tsk) - return; - if (tsk == task[0]) - panic("task[0] (swapper) doesn't support exec()\n"); - pg_dir = tsk->tss.pg_dir; - page_dir = (unsigned long *) pg_dir; - if (!page_dir || page_dir == swapper_pg_dir) { - printk("Trying to clear kernel page-directory: not good\n"); - return; - } - if (mem_map[MAP_NR(pg_dir)] > 1) { - unsigned long * new_pg; - - if (!(new_pg = (unsigned long*) get_free_page(GFP_KERNEL))) { - oom(tsk); - return; - } - for (i = 768 ; i < 1024 ; i++) - new_pg[i] = page_dir[i]; - free_page(pg_dir); - tsk->tss.pg_dir = (unsigned long) new_pg; - return; - } - for (i = 0 ; i < 768 ; i++,page_dir++) - free_one_table(page_dir); - invalidate(); - return; -} - -/* - * This function frees up all page tables of a process when it exits. - */ -void free_page_tables(struct task_struct * tsk) -{ - int i; - unsigned long pg_dir; - unsigned long * page_dir; - - if (!tsk) - return; - if (tsk == task[0]) { - printk("task[0] (swapper) killed: unable to recover\n"); - panic("Trying to free up swapper memory space"); - } - pg_dir = tsk->tss.pg_dir; - if (!pg_dir || pg_dir == (unsigned long) swapper_pg_dir) { - printk("Trying to free kernel page-directory: not good\n"); - return; - } - tsk->tss.pg_dir = (unsigned long) swapper_pg_dir; - if (mem_map[MAP_NR(pg_dir)] > 1) { - free_page(pg_dir); - return; - } - page_dir = (unsigned long *) pg_dir; - for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++) - free_one_table(page_dir); - free_page(pg_dir); - invalidate(); -} - -/* - * clone_page_tables() clones the page table for a process - both - * processes will have the exact same pages in memory. There are - * probably races in the memory management with cloning, but we'll - * see.. - */ -int clone_page_tables(struct task_struct * tsk) -{ - unsigned long pg_dir; - - pg_dir = current->tss.pg_dir; - mem_map[MAP_NR(pg_dir)]++; - tsk->tss.pg_dir = pg_dir; - return 0; -} - -/* - * copy_page_tables() just copies the whole process memory range: - * note the special handling of RESERVED (ie kernel) pages, which - * means that they are always shared by all processes. - */ -int copy_page_tables(struct task_struct * tsk) -{ - int i; - unsigned long old_pg_dir, *old_page_dir; - unsigned long new_pg_dir, *new_page_dir; - - if (!(new_pg_dir = get_free_page(GFP_KERNEL))) - return -ENOMEM; - old_pg_dir = current->tss.pg_dir; - tsk->tss.pg_dir = new_pg_dir; - old_page_dir = (unsigned long *) old_pg_dir; - new_page_dir = (unsigned long *) new_pg_dir; - for (i = 0 ; i < PTRS_PER_PAGE ; i++,old_page_dir++,new_page_dir++) { - int j; - unsigned long old_pg_table, *old_page_table; - unsigned long new_pg_table, *new_page_table; - - old_pg_table = *old_page_dir; - if (old_pg_table == (unsigned long) invalid_pg_table) - continue; - if (old_pg_table >= high_memory || !(old_pg_table & PAGE_VALID)) { - printk("copy_page_tables: bad page table: " - "probable memory corruption\n"); - *old_page_dir = PAGE_TABLE | (unsigned long)invalid_pg_table; - continue; - } - if (mem_map[MAP_NR(old_pg_table)] & MAP_PAGE_RESERVED) { - *new_page_dir = old_pg_table; - continue; - } - if (!(new_pg_table = get_free_page(GFP_KERNEL))) { - free_page_tables(tsk); - return -ENOMEM; - } - old_page_table = (unsigned long *) (PAGE_MASK & old_pg_table); - new_page_table = (unsigned long *) (PAGE_MASK & new_pg_table); - for (j = 0 ; j < PTRS_PER_PAGE ; j++,old_page_table++,new_page_table++) { - unsigned long pg; - pg = *old_page_table; - if (!pg) - continue; - if (!(pg & PAGE_VALID)) { - *new_page_table = swap_duplicate(pg); - continue; - } - if (pg > high_memory || (mem_map[MAP_NR(pg)] & MAP_PAGE_RESERVED)) { - *new_page_table = pg; - continue; - } - if (pg & PAGE_COW) - pg &= ~PAGE_RW; - if (delete_from_swap_cache(pg)) - pg |= PAGE_DIRTY; - *new_page_table = pg; - *old_page_table = pg; - mem_map[MAP_NR(pg)]++; - } - *new_page_dir = new_pg_table | PAGE_TABLE; - } - invalidate(); - return 0; -} - -/* - * a more complete version of free_page_tables which performs with page - * granularity. - */ -int unmap_page_range(unsigned long from, unsigned long size) -{ - unsigned long page, page_dir; - unsigned long *page_table, *dir; - unsigned long poff, pcnt, pc; - - if (from & ~PAGE_MASK) { - printk("unmap_page_range called with wrong alignment\n"); - return -EINVAL; - } - size = (size + ~PAGE_MASK) >> PAGE_SHIFT; - dir = PAGE_DIR_OFFSET(current->tss.pg_dir,from); - poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if ((pcnt = PTRS_PER_PAGE - poff) > size) - pcnt = size; - - for ( ; size > 0; ++dir, size -= pcnt, - pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size)) { - if (!(page_dir = *dir)) { - poff = 0; - continue; - } - if (!(page_dir & PAGE_VALID)) { - printk("unmap_page_range: bad page directory."); - continue; - } - page_table = (unsigned long *)(PAGE_MASK & page_dir); - if (poff) { - page_table += poff; - poff = 0; - } - for (pc = pcnt; pc--; page_table++) { - if ((page = *page_table) != (unsigned long) invalid_pg_table) { - *page_table = (unsigned long) invalid_pg_table; - if (PAGE_VALID & page) { - if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) - if (current->mm->rss > 0) - --current->mm->rss; - free_page(PAGE_MASK & page); - } else - swap_free(page); - } - } - if (pcnt == PTRS_PER_PAGE) { - *dir = 0; - free_page(PAGE_MASK & page_dir); - } - } - invalidate(); - return 0; -} - -int zeromap_page_range(unsigned long from, unsigned long size, int mask) -{ - unsigned long *page_table, *dir; - unsigned long poff, pcnt; - unsigned long page; - - if (mask) { - if ((mask & (PAGE_MASK|PAGE_VALID)) != PAGE_VALID) { - printk("zeromap_page_range: mask = %08x\n",mask); - return -EINVAL; - } - mask |= ZERO_PAGE; - } - if (from & ~PAGE_MASK) { - printk("zeromap_page_range: from = %08lx\n",from); - return -EINVAL; - } - dir = PAGE_DIR_OFFSET(current->tss.pg_dir,from); - size = (size + ~PAGE_MASK) >> PAGE_SHIFT; - poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if ((pcnt = PTRS_PER_PAGE - poff) > size) - pcnt = size; - - while (size > 0) { - if (PAGE_MASK & *dir == (unsigned long) invalid_pg_table) { - /* clear page needed here? SRB. */ - if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) { - invalidate(); - return -ENOMEM; - } - if (PAGE_MASK & *dir == (unsigned long) invalid_pg_table) { - free_page((unsigned long) page_table); - page_table = (unsigned long *)(PAGE_MASK & *dir++); - } else - *dir++ = ((unsigned long) page_table) | PAGE_TABLE; - } else - page_table = (unsigned long *)(PAGE_MASK & *dir++); - page_table += poff; - poff = 0; - for (size -= pcnt; pcnt-- ;) { - if (PAGE_MASK & (page = *page_table) != (unsigned long) invalid_pg_table) { - *page_table = PAGE_TABLE | (unsigned long) invalid_pg_table; - if (page & PAGE_VALID) { - if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) - if (current->mm->rss > 0) - --current->mm->rss; - free_page(PAGE_MASK & page); - } else - swap_free(page); - } - *page_table++ = mask; - } - pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size); - } - invalidate(); - return 0; -} - -/* - * Maps a range of physical memory into the requested pages. the old - * mappings are removed. Any references to nonexistent pages results - * in null mappings (currently treated as "copy-on-access") - */ -int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask) -{ - unsigned long *page_table, *dir; - unsigned long poff, pcnt; - unsigned long page; - - if (mask) { - if ((mask & (PAGE_MASK|PAGE_VALID)) != PAGE_VALID) { - printk("remap_page_range: mask = %08x\n",mask); - return -EINVAL; - } - } - if ((from & ~PAGE_MASK) || (to & ~PAGE_MASK)) { - printk("remap_page_range: from = %08lx, to=%08lx\n",from,to); - return -EINVAL; - } - dir = PAGE_DIR_OFFSET(current->tss.pg_dir,from); - size = (size + ~PAGE_MASK) >> PAGE_SHIFT; - poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if ((pcnt = PTRS_PER_PAGE - poff) > size) - pcnt = size; - - while (size > 0) { - if (PAGE_MASK & *dir != (unsigned long) invalid_pg_table) { - /* clearing page here, needed? SRB. */ - if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) { - invalidate(); - return -1; - } - *dir++ = ((unsigned long) page_table) | PAGE_TABLE; - } - else - page_table = (unsigned long *)(PAGE_MASK & *dir++); - if (poff) { - page_table += poff; - poff = 0; - } - - for (size -= pcnt; pcnt-- ;) { - if (PAGE_MASK & (page = *page_table) != (unsigned long) invalid_pg_table) { - *page_table = PAGE_TABLE | (unsigned long) invalid_pg_table; - if (PAGE_VALID & page) { - if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) - if (current->mm->rss > 0) - --current->mm->rss; - free_page(PAGE_MASK & page); - } else - swap_free(page); - } - - /* - * the first condition should return an invalid access - * when the page is referenced. current assumptions - * cause it to be treated as demand allocation in some - * cases. - */ - if (!mask) - *page_table++ = 0; /* not present */ - else if (to >= high_memory) - *page_table++ = (to | mask); - else if (!mem_map[MAP_NR(to)]) - *page_table++ = 0; /* not present */ - else { - *page_table++ = (to | mask); - if (!(mem_map[MAP_NR(to)] & MAP_PAGE_RESERVED)) { - ++current->mm->rss; - mem_map[MAP_NR(to)]++; - } - } - to += PAGE_SIZE; - } - pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size); - } - invalidate(); - return 0; -} - -/* - * This function puts a page in memory at the wanted address. - * It returns the physical address of the page gotten, 0 if - * out of memory (either when trying to access page-table or - * page.) - */ -unsigned long put_page(struct task_struct * tsk,unsigned long page, - unsigned long address,int prot) -{ - unsigned long *page_table; - - if ((prot & (PAGE_MASK|PAGE_VALID)) != PAGE_VALID) - printk("put_page: prot = %08x\n",prot); - if (page >= high_memory) { - printk("put_page: trying to put page %08lx at %08lx\n",page,address); - return 0; - } - page_table = PAGE_DIR_OFFSET(tsk->tss.pg_dir,address); - if ((*page_table) & PAGE_VALID) - page_table = (unsigned long *) (PAGE_MASK & *page_table); - else { - printk("put_page: bad page directory entry\n"); - oom(tsk); - *page_table = BAD_PAGETABLE | PAGE_TABLE; - return 0; - } - page_table += (address >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if (*page_table) { - printk("put_page: page already exists\n"); - *page_table = PAGE_TABLE | (unsigned long) invalid_pg_table; - invalidate(); - } - *page_table = page | prot; -/* no need for invalidate */ - return page; -} - -/* - * The previous function doesn't work very well if you also want to mark - * the page dirty: exec.c wants this, as it has earlier changed the page, - * and we want the dirty-status to be correct (for VM). Thus the same - * routine, but this time we mark it dirty too. - */ -unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsigned long address) -{ - unsigned long tmp, *page_table; - - if (page >= high_memory) - printk("put_dirty_page: trying to put page %08lx at %08lx\n",page,address); - if (mem_map[MAP_NR(page)] != 1) - printk("mem_map disagrees with %08lx at %08lx\n",page,address); - page_table = PAGE_DIR_OFFSET(tsk->tss.pg_dir,address); - if (PAGE_MASK & *page_table == (unsigned long) invalid_pg_table) - page_table = (unsigned long *) (PAGE_MASK & *page_table); - else { - if (!(tmp = get_free_page(GFP_KERNEL))) - return 0; - if (PAGE_MASK & *page_table == (unsigned long) invalid_pg_table) { - free_page(tmp); - page_table = (unsigned long *) (PAGE_MASK & *page_table); - } else { - *page_table = tmp | PAGE_TABLE; - page_table = (unsigned long *) tmp; - } - } - page_table += (address >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if (*page_table) { - printk("put_dirty_page: page already exists\n"); - *page_table = PAGE_TABLE | (unsigned long) invalid_pg_table; - invalidate(); - } - *page_table = page | (PAGE_DIRTY | PAGE_PRIVATE); -/* no need for invalidate */ - return page; -} - -/* - * Note that processing of the dirty bit has already been done - * before in the assembler part. That way it's not only faster - - * we also can use the i386 code with very little changes. - * - * This routine handles present pages, when users try to write - * to a shared page. It is done by copying the page to a new address - * and decrementing the shared-page counter for the old page. - * - * Goto-purists beware: the only reason for goto's here is that it results - * in better assembly code.. The "default" path will see no jumps at all. - */ -void do_wp_page(struct vm_area_struct * vma, unsigned long address, - unsigned long error_code) -{ - unsigned long *pde, pte, old_page, prot; - unsigned long new_page; - - new_page = __get_free_page(GFP_KERNEL); - pde = PAGE_DIR_OFFSET(vma->vm_task->tss.pg_dir,address); - pte = *pde; - if (!(pte & PAGE_VALID)) - goto end_wp_page; - if ((pte & PAGE_TABLE) != PAGE_TABLE || pte >= high_memory) - goto bad_wp_pagetable; - pte &= PAGE_MASK; - pte += PAGE_PTR(address); - old_page = *(unsigned long *) pte; - if (!(old_page & PAGE_VALID)) - goto end_wp_page; - if (old_page >= high_memory) - goto bad_wp_page; - if (old_page & PAGE_RW) - goto end_wp_page; - vma->vm_task->mm->min_flt++; - prot = (old_page & ~PAGE_MASK) | PAGE_RW | PAGE_DIRTY; - old_page &= PAGE_MASK; - if (mem_map[MAP_NR(old_page)] != 1) { - if (new_page) { - if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED) - ++vma->vm_task->mm->rss; - copy_page(old_page,new_page); - *(unsigned long *) pte = new_page | prot; - free_page(old_page); - invalidate(); - return; - } - free_page(old_page); - oom(vma->vm_task); - *(unsigned long *) pte = BAD_PAGE | prot; - invalidate(); - return; - } - *(unsigned long *) pte |= PAGE_RW | PAGE_DIRTY; - invalidate(); - if (new_page) - free_page(new_page); - return; -bad_wp_page: - printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page); - *(unsigned long *) pte = BAD_PAGE | PAGE_SHARED; - send_sig(SIGKILL, vma->vm_task, 1); - goto end_wp_page; -bad_wp_pagetable: - printk("do_wp_page: bogus page-table at address %08lx (%08lx)\n",address,pte); - *pde = BAD_PAGETABLE | PAGE_TABLE; - send_sig(SIGKILL, vma->vm_task, 1); -end_wp_page: - if (new_page) - free_page(new_page); - return; -} - -/* - * Ugly, ugly, but the goto's result in better assembly.. - */ -int verify_area(int type, const void * addr, unsigned long size) -{ - struct vm_area_struct * vma; - unsigned long start = (unsigned long) addr; - - /* If the current user space is mapped to kernel space (for the - * case where we use a fake user buffer with get_fs/set_fs()) we - * don't expect to find the address in the user vm map. - */ - if (get_fs() == get_ds()) - return 0; - - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > start) - break; - } - if (vma->vm_start <= start) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (vma->vm_end - start > current->rlim[RLIMIT_STACK].rlim_cur) - goto bad_area; - -good_area: - if (!wp_works_ok && type == VERIFY_WRITE) - goto check_wp_fault_by_hand; - for (;;) { - struct vm_area_struct * next; - if (!(vma->vm_page_prot & PAGE_USER)) - goto bad_area; - if (type != VERIFY_READ && !(vma->vm_page_prot & (PAGE_COW | PAGE_RW))) - goto bad_area; - if (vma->vm_end - start >= size) - return 0; - next = vma->vm_next; - if (!next || vma->vm_end != next->vm_start) - goto bad_area; - vma = next; - } - -check_wp_fault_by_hand: - size--; - size += start & ~PAGE_MASK; - size >>= PAGE_SHIFT; - start &= PAGE_MASK; - - for (;;) { - if (!(vma->vm_page_prot & (PAGE_COW | PAGE_RW))) - goto bad_area; - do_wp_page(vma, start, PAGE_VALID); - if (!size) - return 0; - size--; - start += PAGE_SIZE; - if (start < vma->vm_end) - continue; - vma = vma->vm_next; - if (!vma || vma->vm_start != start) - break; - } - -bad_area: - return -EFAULT; -} - -static inline void get_empty_page(struct task_struct * tsk, unsigned long address) -{ - unsigned long tmp; - - if (!(tmp = get_free_page(GFP_KERNEL))) { - oom(tsk); - tmp = BAD_PAGE; - } - if (!put_page(tsk,tmp,address,PAGE_PRIVATE)) - free_page(tmp); -} - -/* - * try_to_share() checks the page at address "address" in the task "p", - * to see if it exists, and if it is clean. If so, share it with the current - * task. - * - * NOTE! This assumes we have checked that p != current, and that they - * share the same inode and can generally otherwise be shared. - */ -static int try_to_share(unsigned long to_address, struct vm_area_struct * to_area, - unsigned long from_address, struct vm_area_struct * from_area, - unsigned long newpage) -{ - unsigned long from; - unsigned long to; - unsigned long from_page; - unsigned long to_page; - - from_page = (unsigned long)PAGE_DIR_OFFSET(from_area->vm_task->tss.pg_dir,from_address); - to_page = (unsigned long)PAGE_DIR_OFFSET(to_area->vm_task->tss.pg_dir,to_address); -/* is there a page-directory at from? */ - from = *(unsigned long *) from_page; - if (from & PAGE_MASK == (unsigned long) invalid_pg_table) - return 0; - from &= PAGE_MASK; - from_page = from + PAGE_PTR(from_address); - from = *(unsigned long *) from_page; -/* is the page present? */ - if (!(from & PAGE_VALID)) - return 0; -/* if it is private, it must be clean to be shared */ - if (from & PAGE_DIRTY) { - if (from_area->vm_page_prot & PAGE_COW) - return 0; - if (!(from_area->vm_page_prot & PAGE_RW)) - return 0; - } -/* is the page reasonable at all? */ - if (from >= high_memory) - return 0; - if (mem_map[MAP_NR(from)] & MAP_PAGE_RESERVED) - return 0; -/* is the destination ok? */ - to = *(unsigned long *) to_page; - if (to & PAGE_MASK == (unsigned long) invalid_pg_table) - return 0; - to &= PAGE_MASK; - to_page = to + PAGE_PTR(to_address); - if (*(unsigned long *) to_page) - return 0; -/* do we copy? */ - if (newpage) { - if (in_swap_cache(from)) { /* implies PAGE_DIRTY */ - if (from_area->vm_page_prot & PAGE_COW) - return 0; - if (!(from_area->vm_page_prot & PAGE_RW)) - return 0; - } - copy_page((from & PAGE_MASK), newpage); - *(unsigned long *) to_page = newpage | to_area->vm_page_prot; - return 1; - } -/* do a final swap-cache test before sharing them.. */ - if (in_swap_cache(from)) { - if (from_area->vm_page_prot & PAGE_COW) - return 0; - if (!(from_area->vm_page_prot & PAGE_RW)) - return 0; - from |= PAGE_DIRTY; - *(unsigned long *) from_page = from; - delete_from_swap_cache(from); - invalidate(); - } - mem_map[MAP_NR(from)]++; -/* fill in the 'to' field, checking for COW-stuff */ - to = (from & (PAGE_MASK | PAGE_DIRTY)) | to_area->vm_page_prot; - if (to & PAGE_COW) - to &= ~PAGE_RW; - *(unsigned long *) to_page = to; -/* Check if we need to do anything at all to the 'from' field */ - if (!(from & PAGE_RW)) - return 1; - if (!(from_area->vm_page_prot & PAGE_COW)) - return 1; -/* ok, need to mark it read-only, so invalidate any possible old TB entry */ - from &= ~PAGE_RW; - *(unsigned long *) from_page = from; - invalidate(); - return 1; -} - -/* - * share_page() tries to find a process that could share a page with - * the current one. - * - * We first check if it is at all feasible by checking inode->i_count. - * It should be >1 if there are other tasks sharing this inode. - */ -static int share_page(struct vm_area_struct * area, unsigned long address, - unsigned long error_code, unsigned long newpage) -{ - struct inode * inode; - struct task_struct ** p; - unsigned long offset; - unsigned long from_address; - unsigned long give_page; - - if (!area || !(inode = area->vm_inode) || inode->i_count < 2) - return 0; - /* do we need to copy or can we just share? */ - give_page = 0; - if ((area->vm_page_prot & PAGE_COW) && (error_code & PAGE_RW)) { - if (!newpage) - return 0; - give_page = newpage; - } - offset = address - area->vm_start + area->vm_offset; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - struct vm_area_struct * mpnt; - if (!*p) - continue; - if (area->vm_task == *p) - continue; - /* Now see if there is something in the VMM that - we can share pages with */ - for (mpnt = (*p)->mm->mmap; mpnt; mpnt = mpnt->vm_next) { - /* must be same inode */ - if (mpnt->vm_inode != inode) - continue; - /* offsets must be mutually page-aligned */ - if ((mpnt->vm_offset ^ area->vm_offset) & ~PAGE_MASK) - continue; - /* the other area must actually cover the wanted page.. */ - from_address = offset + mpnt->vm_start - mpnt->vm_offset; - if (from_address < mpnt->vm_start || from_address >= mpnt->vm_end) - continue; - /* .. NOW we can actually try to use the same physical page */ - if (!try_to_share(address, area, from_address, mpnt, give_page)) - continue; - /* free newpage if we never used it.. */ - if (give_page || !newpage) - return 1; - free_page(newpage); - return 1; - } - } - return 0; -} - -/* - * fill in an empty page-table if none exists. - */ -static inline unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned long address) -{ - unsigned long page; - unsigned long *p; - - p = PAGE_DIR_OFFSET(tsk->tss.pg_dir,address); - if (*p & PAGE_MASK == (unsigned long) invalid_pg_table) - return *p; - if (*p) { - printk("get_empty_pgtable: bad page-directory entry \n"); - *p = 0; - } - page = get_free_page(GFP_KERNEL); - p = PAGE_DIR_OFFSET(tsk->tss.pg_dir,address); - if (*p & PAGE_MASK == (unsigned long) invalid_pg_table) { - free_page(page); - return *p; - } - if (*p) { - printk("get_empty_pgtable: bad page-directory entry \n"); - *p = 0; - } - if (page) { - *p = page | PAGE_TABLE; - return *p; - } - oom(current); - *p = BAD_PAGETABLE | PAGE_TABLE; - return 0; -} - -static inline void do_swap_page(struct vm_area_struct * vma, - unsigned long address, unsigned long * pge, unsigned long entry) -{ - unsigned long page; - - if (vma->vm_ops && vma->vm_ops->swapin) - page = vma->vm_ops->swapin(vma, entry); - else - page = swap_in(entry); - if (*pge != entry) { - free_page(page); - return; - } - page = page | vma->vm_page_prot; - if (mem_map[MAP_NR(page)] > 1 && (page & PAGE_COW)) - page &= ~PAGE_RW; - ++vma->vm_task->mm->rss; - ++vma->vm_task->mm->maj_flt; - *pge = page; - return; -} - -void do_no_page(struct vm_area_struct * vma, unsigned long address, - unsigned long error_code) -{ - unsigned long page, entry, prot; - - page = get_empty_pgtable(vma->vm_task,address); - if (!page) - return; - page &= PAGE_MASK; - page += PAGE_PTR(address); - entry = *(unsigned long *) page; - if (entry & PAGE_VALID) - return; - if (entry) { - do_swap_page(vma, address, (unsigned long *) page, entry); - return; - } - address &= PAGE_MASK; - - if (!vma->vm_ops || !vma->vm_ops->nopage) { - ++vma->vm_task->mm->rss; - ++vma->vm_task->mm->min_flt; - get_empty_page(vma->vm_task,address); - return; - } - page = get_free_page(GFP_KERNEL); - if (share_page(vma, address, error_code, page)) { - ++vma->vm_task->mm->min_flt; - ++vma->vm_task->mm->rss; - return; - } - if (!page) { - oom(current); - put_page(vma->vm_task, BAD_PAGE, address, PAGE_PRIVATE); - return; - } - ++vma->vm_task->mm->maj_flt; - ++vma->vm_task->mm->rss; - prot = vma->vm_page_prot; - /* - * The fourth argument is "no_share", which tells the low-level code - * to copy, not share the page even if sharing is possible. It's - * essentially an early COW detection ("moo at 5 AM"). - */ - page = vma->vm_ops->nopage(vma, address, page, (error_code & PAGE_RW) && (prot & PAGE_COW)); - if (share_page(vma, address, error_code, 0)) { - free_page(page); - return; - } - /* - * This silly early PAGE_DIRTY setting removes a race - * due to the bad i386 page protection. - */ - if (error_code & PAGE_RW) { - prot |= PAGE_DIRTY; /* can't be COW-shared: see "no_share" above */ - } else if ((prot & PAGE_COW) && mem_map[MAP_NR(page)] > 1) - prot &= ~PAGE_RW; - if (put_page(vma->vm_task, page, address, prot)) - return; - free_page(page); - oom(current); -} - -/* - * This routine handles page faults. It determines the address, - * and the problem, and then passes it off to one of the appropriate - * routines. - */ -asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) -{ - struct vm_area_struct * vma; - unsigned long address; - unsigned long page; - - /* get the address */ - __asm__("dmfc0\t%0,$8":"=r" (address)); - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > address) - break; - } - 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) - 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: -#if 0 - if (regs->eflags & VM_MASK) { - unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT; - if (bit < 32) - current->screen_bitmap |= 1 << bit; - } -#endif - if (!(vma->vm_page_prot & PAGE_USER)) - goto bad_area; - if (error_code & PAGE_VALID) { - if (!(vma->vm_page_prot & (PAGE_RW | PAGE_COW))) - goto bad_area; - do_wp_page(vma, address, error_code); - return; - } - do_no_page(vma, address, error_code); - return; - -/* - * Something tried to access memory that isn't in our memory map.. - * Fix it, but check if it's kernel or user first.. - */ -bad_area: - if (error_code & PAGE_USER) { - current->tss.cp0_badvaddr = address; - current->tss.error_code = error_code; - current->tss.trap_no = 14; - send_sig(SIGSEGV, current, 1); - return; - } -/* - * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice. - */ - if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & PAGE_VALID)) { - wp_works_ok = 1; - pg0[0] = PAGE_SHARED; - printk("This processor honours the WP bit even when in supervisor mode. Good.\n"); - return; - } - if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) { - printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - pg0[0] = PAGE_SHARED; - } else - printk(KERN_ALERT "Unable to handle kernel paging request"); - printk(" at virtual address %08lx\n",address); - printk(KERN_ALERT "current->tss.pg_dir = %08lx\n", current->tss.pg_dir); - page = ((unsigned long *) page)[address >> 22]; - printk(KERN_ALERT "*pde = %08lx\n", page); - if (page & PAGE_VALID) { - page &= PAGE_MASK; - address &= 0x003ff000; - page = ((unsigned long *) page)[address >> PAGE_SHIFT]; - printk(KERN_ALERT "*pte = %08lx\n", page); - } - die_if_kernel("Oops", regs, error_code); - do_exit(SIGKILL); -} - -/* - * 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 - * unused etc.. - * - * BAD_PAGETABLE is the accompanying page-table: it is initialized - * to point to BAD_PAGE entries. - * - * ZERO_PAGE is a special page that is used for zero-initialized - * data and COW. - */ -unsigned long __bad_pagetable(void) -{ - extern char empty_bad_page_table[PAGE_SIZE]; - unsigned long dummy; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "1:\tsw\t%2,(%0)\n\t" - "subu\t%1,%1,1\n\t" - "bne\t$0,%1,1b\n\t" - "addiu\t%0,%0,1\n\t" - ".set\treorder" - :"=r" (dummy), - "=r" (dummy) - :"r" (BAD_PAGE + PAGE_TABLE), - "0" ((long) empty_bad_page_table), - "1" (PTRS_PER_PAGE)); - - return (unsigned long) empty_bad_page_table; -} - -unsigned long __bad_page(void) -{ - extern char empty_bad_page[PAGE_SIZE]; - unsigned long dummy; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "1:\tsw\t$0,(%0)\n\t" - "subu\t%1,%1,1\n\t" - "bne\t$0,%1,1b\n\t" - "addiu\t%0,%0,1\n\t" - ".set\treorder" - :"=r" (dummy), - "=r" (dummy) - :"0" ((long) empty_bad_page), - "1" (PTRS_PER_PAGE)); - - return (unsigned long) empty_bad_page; -} - -unsigned long __zero_page(void) -{ - extern char empty_zero_page[PAGE_SIZE]; - unsigned long dummy; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "1:\tsw\t$0,(%0)\n\t" - "subu\t%1,%1,1\n\t" - "bne\t$0,%1,1b\n\t" - "addiu\t%0,%0,1\n\t" - ".set\treorder" - :"=r" (dummy), - "=r" (dummy) - :"0" ((long) empty_zero_page), - "1" (PTRS_PER_PAGE)); - - return (unsigned long) empty_zero_page; -} - -void show_mem(void) -{ - int i,free = 0,total = 0,reserved = 0; - int shared = 0; - - printk("Mem-info:\n"); - show_free_areas(); - printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = high_memory >> PAGE_SHIFT; - while (i-- > 0) { - total++; - if (mem_map[i] & MAP_PAGE_RESERVED) - reserved++; - else if (!mem_map[i]) - free++; - else - shared += mem_map[i]-1; - } - printk("%d pages of RAM\n",total); - printk("%d free pages\n",free); - printk("%d reserved pages\n",reserved); - printk("%d pages shared\n",shared); - show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif -} - -#if 0 -extern unsigned long free_area_init(unsigned long, unsigned long); - -/* - * paging_init() sets up the page tables - note that the first 4MB are - * already mapped by head.S. - * - * This routines also unmaps the page at virtual kernel address 0, so - * that we can trap those pesky NULL-reference errors in the kernel. - */ -unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) -{ - unsigned long * pg_dir; - unsigned long * pg_table; - unsigned long tmp; - unsigned long address; - - start_mem = PAGE_ALIGN(start_mem); - address = 0; - pg_dir = swapper_pg_dir; - while (address < end_mem) { - /* - * at virtual addr 0xC0000000 - */ - tmp = *(pg_dir + 768); - tmp &= PAGE_MASK; - if (!tmp) { - tmp = start_mem | PAGE_TABLE; - *(pg_dir + 768) = tmp; - start_mem += PAGE_SIZE; - } - /* - * also map it in at 0x0000000 for init - */ - *pg_dir = tmp; - pg_dir++; - pg_table = (unsigned long *) (tmp & PAGE_MASK); - for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) { - if (address < end_mem) - *pg_table = address | PAGE_SHARED; - else - *pg_table = 0; - address += PAGE_SIZE; - } - } - invalidate(); - return free_area_init(start_mem, end_mem); -} -#endif - -void mem_init(unsigned long start_mem, unsigned long end_mem) -{ - int codepages = 0; - int reservedpages = 0; - int datapages = 0; - unsigned long tmp; - extern int etext; - - cli(); - end_mem &= PAGE_MASK; - high_memory = end_mem; - - /* mark usable pages in the mem_map[] */ - start_mem = PAGE_ALIGN(start_mem); - - while (start_mem < high_memory) { - mem_map[MAP_NR(start_mem)] = 0; - start_mem += PAGE_SIZE; - } -#ifdef CONFIG_SOUND - sound_mem_init(); -#endif - for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) { - if (mem_map[MAP_NR(tmp)]) { - /* - * Don't have any reserved pages - */ - if (0) - reservedpages++; - else if (tmp < (0x7fffffff & (unsigned long) &etext)) - codepages++; - else - datapages++; - continue; - } - mem_map[MAP_NR(tmp)] = 1; - 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, - codepages << (PAGE_SHIFT-10), - reservedpages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10)); - -#if 0 - /* - * No need to cope with Intel bugs - */ - wp_works_ok = 1; -#endif - invalidate(); - return; -} - -void si_meminfo(struct sysinfo *val) -{ - int i; - - i = high_memory >> PAGE_SHIFT; - 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) - continue; - val->totalram++; - if (!mem_map[i]) - continue; - val->sharedram += mem_map[i]-1; - } - val->totalram <<= PAGE_SHIFT; - val->sharedram <<= PAGE_SHIFT; - return; -} - - -/* - * This handles a generic mmap of a disk file. - */ -static unsigned long file_mmap_nopage(struct vm_area_struct * area, unsigned long address, - unsigned long page, int no_share) -{ - struct inode * inode = area->vm_inode; - unsigned int block; - int nr[8]; - int i, *p; - - address &= PAGE_MASK; - block = address - area->vm_start + area->vm_offset; - block >>= inode->i_sb->s_blocksize_bits; - i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; - p = nr; - do { - *p = bmap(inode,block); - i--; - block++; - p++; - } while (i > 0); - return bread_page(page, inode->i_dev, nr, inode->i_sb->s_blocksize, no_share); -} - -struct vm_operations_struct file_mmap = { - NULL, /* open */ - NULL, /* close */ - file_mmap_nopage, /* nopage */ - NULL, /* wppage */ - NULL, /* share */ - NULL, /* unmap */ -}; diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c deleted file mode 100644 index dbe63b55e..000000000 --- a/arch/mips/mm/mmap.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * linux/mm/mmap.c - * - * Written by obz. - */ -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/shm.h> -#include <linux/errno.h> -#include <linux/mman.h> -#include <linux/string.h> -#include <linux/malloc.h> - -#include <asm/segment.h> -#include <asm/system.h> - -static int anon_map(struct inode *, struct file *, struct vm_area_struct *); - -/* - * description of effects of mapping type and prot in current implementation. - * this is due to the limited x86 page protection hardware. The expected - * behavior is in parens: - * - * map_type prot - * PROT_NONE PROT_READ PROT_WRITE PROT_EXEC - * MAP_SHARED r: (no) no r: (yes) yes r: (no) yes r: (no) yes - * w: (no) no w: (no) no w: (yes) yes w: (no) no - * x: (no) no x: (no) yes x: (no) yes x: (yes) yes - * - * MAP_PRIVATE r: (no) no r: (yes) yes r: (no) yes r: (no) yes - * w: (no) no w: (no) no w: (copy) copy w: (no) no - * x: (no) no x: (no) yes x: (no) yes x: (yes) yes - * - */ - -int do_mmap(struct file * file, unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, unsigned long off) -{ - int mask, error; - struct vm_area_struct * vma; - - if ((len = PAGE_ALIGN(len)) == 0) - return addr; - - if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len) - return -EINVAL; - - /* offset overflow? */ - if (off + len < off) - return -EINVAL; - - /* - * do simple checking here so the lower-level routines won't have - * to. we assume access permissions have been handled by the open - * of the memory object, so we don't do any here. - */ - - if (file != NULL) { - switch (flags & MAP_TYPE) { - case MAP_SHARED: - if ((prot & PROT_WRITE) && !(file->f_mode & 2)) - return -EACCES; - /* fall through */ - case MAP_PRIVATE: - if (!(file->f_mode & 1)) - return -EACCES; - break; - - default: - return -EINVAL; - } - if ((flags & MAP_DENYWRITE) && (file->f_inode->i_wcount > 0)) - return -ETXTBSY; - } else if ((flags & MAP_TYPE) == MAP_SHARED) - return -EINVAL; - - /* - * obtain the address to map to. we verify (or select) it and ensure - * that it represents a valid section of the address space. - */ - - if (flags & MAP_FIXED) { - if (addr & ~PAGE_MASK) - return -EINVAL; - if (len > TASK_SIZE || addr > TASK_SIZE - len) - return -EINVAL; - } else { - addr = get_unmapped_area(len); - if (!addr) - return -ENOMEM; - } - - /* - * determine the object being mapped and call the appropriate - * specific mapper. the address has already been validated, but - * not unmapped, but the maps are removed from the list. - */ - if (file && (!file->f_op || !file->f_op->mmap)) - return -ENODEV; - mask = PAGE_VALID; - if (prot & (PROT_READ | PROT_EXEC)) - mask |= PAGE_READONLY; - if (prot & PROT_WRITE) - if ((flags & MAP_TYPE) == MAP_PRIVATE) - mask |= PAGE_COPY; - else - mask |= PAGE_SHARED; - - vma = (struct vm_area_struct *)kmalloc(sizeof(struct vm_area_struct), - GFP_KERNEL); - if (!vma) - return -ENOMEM; - - vma->vm_task = current; - vma->vm_start = addr; - vma->vm_end = addr + len; - vma->vm_page_prot = mask; - vma->vm_flags = prot & (VM_READ | VM_WRITE | VM_EXEC); - vma->vm_flags |= flags & (VM_GROWSDOWN | VM_DENYWRITE | VM_EXECUTABLE); - - if (file) { - if (file->f_mode & 1) - vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; - if (flags & MAP_SHARED) { - vma->vm_flags |= VM_SHARED | VM_MAYSHARE; - if (!(file->f_mode & 2)) - vma->vm_flags &= ~VM_MAYWRITE; - } - } else - vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; - vma->vm_ops = NULL; - vma->vm_offset = off; - vma->vm_inode = NULL; - vma->vm_pte = 0; - - do_munmap(addr, len); /* Clear old maps */ - - if (file) - error = file->f_op->mmap(file->f_inode, file, vma); - else - error = anon_map(NULL, NULL, vma); - - if (error) { - kfree(vma); - return error; - } - insert_vm_struct(current, vma); - merge_segments(current->mm->mmap); - return addr; -} - -/* - * Get an address range which is currently unmapped. - * For mmap() without MAP_FIXED and shmat() with addr=0. - * Return value 0 means ENOMEM. - */ -unsigned long get_unmapped_area(unsigned long len) -{ - struct vm_area_struct * vmm; - unsigned long gap_start = 0, gap_end; - - for (vmm = current->mm->mmap; ; vmm = vmm->vm_next) { - if (gap_start < SHM_RANGE_START) - gap_start = SHM_RANGE_START; - if (!vmm || ((gap_end = vmm->vm_start) > SHM_RANGE_END)) - gap_end = SHM_RANGE_END; - gap_start = PAGE_ALIGN(gap_start); - gap_end &= PAGE_MASK; - if ((gap_start <= gap_end) && (gap_end - gap_start >= len)) - return gap_start; - if (!vmm) - return 0; - gap_start = vmm->vm_end; - } -} - -asmlinkage int sys_mmap(unsigned long *buffer) -{ - int error; - unsigned long flags; - struct file * file = NULL; - - error = verify_area(VERIFY_READ, buffer, 6*sizeof(long)); - if (error) - return error; - flags = get_fs_long(buffer+3); - if (!(flags & MAP_ANONYMOUS)) { - unsigned long fd = get_fs_long(buffer+4); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - return -EBADF; - } - return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1), - get_fs_long(buffer+2), flags, get_fs_long(buffer+5)); -} - -/* - * Normal function to fix up a mapping - * This function is the default for when an area has no specific - * function. This may be used as part of a more specific routine. - * This function works out what part of an area is affected and - * adjusts the mapping information. Since the actual page - * manipulation is done in do_mmap(), none need be done here, - * though it would probably be more appropriate. - * - * By the time this function is called, the area struct has been - * removed from the process mapping list, so it needs to be - * reinserted if necessary. - * - * The 4 main cases are: - * Unmapping the whole area - * Unmapping from the start of the segment to a point in it - * Unmapping from an intermediate point to the end - * Unmapping between to intermediate points, making a hole. - * - * Case 4 involves the creation of 2 new areas, for each side of - * the hole. - */ -void unmap_fixup(struct vm_area_struct *area, - unsigned long addr, size_t len) -{ - struct vm_area_struct *mpnt; - unsigned long end = addr + len; - - if (addr < area->vm_start || addr >= area->vm_end || - end <= area->vm_start || end > area->vm_end || - end < addr) - { - printk("unmap_fixup: area=%lx-%lx, unmap %lx-%lx!!\n", - area->vm_start, area->vm_end, addr, end); - return; - } - - /* Unmapping the whole area */ - if (addr == area->vm_start && end == area->vm_end) { - if (area->vm_ops && area->vm_ops->close) - area->vm_ops->close(area); - if (area->vm_inode) - iput(area->vm_inode); - return; - } - - /* Work out to one of the ends */ - if (addr >= area->vm_start && end == area->vm_end) - area->vm_end = addr; - if (addr == area->vm_start && end <= area->vm_end) { - area->vm_offset += (end - area->vm_start); - area->vm_start = end; - } - - /* Unmapping a hole */ - if (addr > area->vm_start && end < area->vm_end) - { - /* Add end mapping -- leave beginning for below */ - mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); - - if (!mpnt) - return; - *mpnt = *area; - mpnt->vm_offset += (end - area->vm_start); - mpnt->vm_start = end; - if (mpnt->vm_inode) - mpnt->vm_inode->i_count++; - if (mpnt->vm_ops && mpnt->vm_ops->open) - mpnt->vm_ops->open(mpnt); - area->vm_end = addr; /* Truncate area */ - insert_vm_struct(current, mpnt); - } - - /* construct whatever mapping is needed */ - mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); - if (!mpnt) - return; - *mpnt = *area; - if (mpnt->vm_ops && mpnt->vm_ops->open) - mpnt->vm_ops->open(mpnt); - if (area->vm_ops && area->vm_ops->close) { - area->vm_end = area->vm_start; - area->vm_ops->close(area); - } - insert_vm_struct(current, mpnt); -} - -asmlinkage int sys_munmap(unsigned long addr, size_t len) -{ - return do_munmap(addr, len); -} - -/* - * Munmap is split into 2 main parts -- this part which finds - * what needs doing, and the areas themselves, which do the - * work. This now handles partial unmappings. - * Jeremy Fitzhardine <jeremy@sw.oz.au> - */ -int do_munmap(unsigned long addr, size_t len) -{ - struct vm_area_struct *mpnt, **npp, *free; - - if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr) - return -EINVAL; - - if ((len = PAGE_ALIGN(len)) == 0) - return 0; - - /* - * Check if this memory area is ok - put it on the temporary - * list if so.. The checks here are pretty simple -- - * every area affected in some way (by any overlap) is put - * on the list. If nothing is put on, nothing is affected. - */ - npp = ¤t->mm->mmap; - free = NULL; - for (mpnt = *npp; mpnt != NULL; mpnt = *npp) { - unsigned long end = addr+len; - - if ((addr < mpnt->vm_start && end <= mpnt->vm_start) || - (addr >= mpnt->vm_end && end > mpnt->vm_end)) - { - npp = &mpnt->vm_next; - continue; - } - - *npp = mpnt->vm_next; - mpnt->vm_next = free; - free = mpnt; - } - - if (free == NULL) - return 0; - - /* - * Ok - we have the memory areas we should free on the 'free' list, - * so release them, and unmap the page range.. - * If the one of the segments is only being partially unmapped, - * it will put new vm_area_struct(s) into the address space. - */ - while (free) { - unsigned long st, end; - - mpnt = free; - free = free->vm_next; - - st = addr < mpnt->vm_start ? mpnt->vm_start : addr; - end = addr+len; - end = end > mpnt->vm_end ? mpnt->vm_end : end; - - if (mpnt->vm_ops && mpnt->vm_ops->unmap) - mpnt->vm_ops->unmap(mpnt, st, end-st); - else - unmap_fixup(mpnt, st, end-st); - - kfree(mpnt); - } - - unmap_page_range(addr, len); - return 0; -} - -/* This is used for a general mmap of a disk file */ -int generic_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) -{ - extern struct vm_operations_struct file_mmap; - - if (vma->vm_page_prot & PAGE_RW) /* only PAGE_COW or read-only supported right now */ - return -EINVAL; - if (vma->vm_offset & (inode->i_sb->s_blocksize - 1)) - return -EINVAL; - if (!inode->i_sb || !S_ISREG(inode->i_mode)) - return -EACCES; - if (!inode->i_op || !inode->i_op->bmap) - return -ENOEXEC; - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - vma->vm_inode = inode; - inode->i_count++; - vma->vm_ops = &file_mmap; - return 0; -} - -/* - * Insert vm structure into process list sorted by address. - */ -void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp) -{ - struct vm_area_struct **p, *mpnt; - - p = &t->mm->mmap; - while ((mpnt = *p) != NULL) { - if (mpnt->vm_start > vmp->vm_start) - break; - if (mpnt->vm_end > vmp->vm_start) - printk("insert_vm_struct: overlapping memory areas\n"); - p = &mpnt->vm_next; - } - vmp->vm_next = mpnt; - *p = vmp; -} - -/* - * Merge a list of memory segments if possible. - * Redundant vm_area_structs are freed. - * This assumes that the list is ordered by address. - */ -void merge_segments(struct vm_area_struct *mpnt) -{ - struct vm_area_struct *prev, *next; - - if (mpnt == NULL) - return; - - for(prev = mpnt, mpnt = mpnt->vm_next; - mpnt != NULL; - prev = mpnt, mpnt = next) - { - next = mpnt->vm_next; - - /* - * To share, we must have the same inode, operations.. - */ - if (mpnt->vm_inode != prev->vm_inode) - continue; - if (mpnt->vm_pte != prev->vm_pte) - continue; - if (mpnt->vm_ops != prev->vm_ops) - continue; - if (mpnt->vm_page_prot != prev->vm_page_prot || - mpnt->vm_flags != prev->vm_flags) - continue; - if (prev->vm_end != mpnt->vm_start) - continue; - /* - * and if we have an inode, the offsets must be contiguous.. - */ - if ((mpnt->vm_inode != NULL) || (mpnt->vm_flags & VM_SHM)) { - if (prev->vm_offset + prev->vm_end - prev->vm_start != mpnt->vm_offset) - continue; - } - - /* - * merge prev with mpnt and set up pointers so the new - * big segment can possibly merge with the next one. - * The old unused mpnt is freed. - */ - prev->vm_end = mpnt->vm_end; - prev->vm_next = mpnt->vm_next; - if (mpnt->vm_ops && mpnt->vm_ops->close) { - mpnt->vm_offset += mpnt->vm_end - mpnt->vm_start; - mpnt->vm_start = mpnt->vm_end; - mpnt->vm_ops->close(mpnt); - } - if (mpnt->vm_inode) - mpnt->vm_inode->i_count--; - kfree_s(mpnt, sizeof(*mpnt)); - mpnt = prev; - } -} - -/* - * Map memory not associated with any file into a process - * address space. Adjacent memory is merged. - */ -static int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma) -{ - if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -ENOMEM; - return 0; -} diff --git a/arch/mips/mm/mprotect.c b/arch/mips/mm/mprotect.c deleted file mode 100644 index 7bb4148f4..000000000 --- a/arch/mips/mm/mprotect.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * linux/mm/mprotect.c - * - * (C) Copyright 1994 Linus Torvalds - */ -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/shm.h> -#include <linux/errno.h> -#include <linux/mman.h> -#include <linux/string.h> -#include <linux/malloc.h> - -#include <asm/segment.h> -#include <asm/system.h> - -#define CHG_MASK (PAGE_MASK | PAGE_ACCESSED | PAGE_DIRTY | CACHE_UNCACHED) - -static void change_protection(unsigned long start, unsigned long end, int prot) -{ - unsigned long *page_table, *dir; - unsigned long page, offset; - int nr; - - dir = PAGE_DIR_OFFSET(current->tss.pg_dir, start); - offset = (start >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - nr = (end - start) >> PAGE_SHIFT; - while (nr > 0) { - page = *dir; - dir++; - if (!(page & PAGE_VALID)) { - nr = nr - PTRS_PER_PAGE + offset; - offset = 0; - continue; - } - page_table = offset + (unsigned long *) (page & PAGE_MASK); - offset = PTRS_PER_PAGE - offset; - if (offset > nr) - offset = nr; - nr = nr - offset; - do { - page = *page_table; - if (page & PAGE_VALID) - *page_table = (page & CHG_MASK) | prot; - ++page_table; - } while (--offset); - } - return; -} - -static inline int mprotect_fixup_all(struct vm_area_struct * vma, - int newflags, int prot) -{ - vma->vm_flags = newflags; - vma->vm_page_prot = prot; - return 0; -} - -static inline int mprotect_fixup_start(struct vm_area_struct * vma, - unsigned long end, - int newflags, int prot) -{ - struct vm_area_struct * n; - - n = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!n) - return -ENOMEM; - *n = *vma; - vma->vm_start = end; - n->vm_end = end; - vma->vm_offset += vma->vm_start - n->vm_start; - n->vm_flags = newflags; - n->vm_page_prot = prot; - if (n->vm_inode) - n->vm_inode->i_count++; - if (n->vm_ops && n->vm_ops->open) - n->vm_ops->open(n); - insert_vm_struct(current, n); - return 0; -} - -static inline int mprotect_fixup_end(struct vm_area_struct * vma, - unsigned long start, - int newflags, int prot) -{ - struct vm_area_struct * n; - - n = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!n) - return -ENOMEM; - *n = *vma; - vma->vm_end = start; - n->vm_start = start; - n->vm_offset += n->vm_start - vma->vm_start; - n->vm_flags = newflags; - n->vm_page_prot = prot; - if (n->vm_inode) - n->vm_inode->i_count++; - if (n->vm_ops && n->vm_ops->open) - n->vm_ops->open(n); - insert_vm_struct(current, n); - return 0; -} - -static inline int mprotect_fixup_middle(struct vm_area_struct * vma, - unsigned long start, unsigned long end, - int newflags, int prot) -{ - struct vm_area_struct * left, * right; - - left = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!left) - return -ENOMEM; - right = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!right) { - kfree(left); - return -ENOMEM; - } - *left = *vma; - *right = *vma; - left->vm_end = start; - vma->vm_start = start; - vma->vm_end = end; - right->vm_start = end; - vma->vm_offset += vma->vm_start - left->vm_start; - right->vm_offset += right->vm_start - left->vm_start; - vma->vm_flags = newflags; - vma->vm_page_prot = prot; - if (vma->vm_inode) - vma->vm_inode->i_count += 2; - if (vma->vm_ops && vma->vm_ops->open) { - vma->vm_ops->open(left); - vma->vm_ops->open(right); - } - insert_vm_struct(current, left); - insert_vm_struct(current, right); - return 0; -} - -static int mprotect_fixup(struct vm_area_struct * vma, - unsigned long start, unsigned long end, unsigned int newflags) -{ - int prot, error; - - if (newflags == vma->vm_flags) - return 0; - prot = PAGE_VALID; - if (newflags & (VM_READ | VM_EXEC)) - prot |= PAGE_READONLY; - if (newflags & VM_WRITE) - if (newflags & VM_SHARED) - prot |= PAGE_SHARED; - else - prot |= PAGE_COPY; - - if (start == vma->vm_start) - if (end == vma->vm_end) - error = mprotect_fixup_all(vma, newflags, prot); - else - error = mprotect_fixup_start(vma, end, newflags, prot); - else if (end == vma->vm_end) - error = mprotect_fixup_end(vma, start, newflags, prot); - else - error = mprotect_fixup_middle(vma, start, end, newflags, prot); - - if (error) - return error; - - change_protection(start, end, prot); - return 0; -} - -asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot) -{ - unsigned long end, tmp; - struct vm_area_struct * vma, * next; - int error; - - if (start & ~PAGE_MASK) - return -EINVAL; - len = (len + ~PAGE_MASK) & PAGE_MASK; - end = start + len; - if (end < start) - return -EINVAL; - if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) - return -EINVAL; - if (end == start) - return 0; - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return -EFAULT; - if (vma->vm_end > start) - break; - } - if (vma->vm_start > start) - return -EFAULT; - - for ( ; ; ) { - unsigned int newflags; - - /* Here we know that vma->vm_start <= start < vma->vm_end. */ - - newflags = prot | (vma->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC)); - if ((newflags & ~(newflags >> 4)) & 0xf) { - error = -EACCES; - break; - } - - if (vma->vm_end >= end) { - error = mprotect_fixup(vma, start, end, newflags); - break; - } - - tmp = vma->vm_end; - next = vma->vm_next; - error = mprotect_fixup(vma, start, tmp, newflags); - if (error) - break; - start = tmp; - vma = next; - if (!vma || vma->vm_start != start) { - error = -EFAULT; - break; - } - } - merge_segments(current->mm->mmap); - return error; -} diff --git a/arch/mips/mm/swap.c b/arch/mips/mm/swap.c deleted file mode 100644 index 084208c04..000000000 --- a/arch/mips/mm/swap.c +++ /dev/null @@ -1,986 +0,0 @@ -/* - * linux/mm/swap.c - * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - */ - -/* - * This file should contain most things doing the swapping from/to disk. - * Started 18.12.91 - */ - -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/head.h> -#include <linux/kernel.h> -#include <linux/kernel_stat.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/fs.h> - -#include <asm/system.h> /* for cli()/sti() */ -#include <asm/bitops.h> - -#define MAX_SWAPFILES 8 - -#define SWP_USED 1 -#define SWP_WRITEOK 3 - -#define SWP_TYPE(entry) (((entry) & 0xfe) >> 1) -#define SWP_OFFSET(entry) ((entry) >> PAGE_SHIFT) -#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << PAGE_SHIFT)) - -int min_free_pages = 20; - -static int nr_swapfiles = 0; -static struct wait_queue * lock_queue = NULL; - -static struct swap_info_struct { - unsigned long flags; - struct inode * swap_file; - unsigned int swap_device; - unsigned char * swap_map; - unsigned char * swap_lockmap; - int pages; - int lowest_bit; - int highest_bit; - unsigned long max; -} swap_info[MAX_SWAPFILES]; - -extern int shm_swap (int); - -unsigned long *swap_cache; - -#ifdef SWAP_CACHE_INFO -unsigned long swap_cache_add_total = 0; -unsigned long swap_cache_add_success = 0; -unsigned long swap_cache_del_total = 0; -unsigned long swap_cache_del_success = 0; -unsigned long swap_cache_find_total = 0; -unsigned long swap_cache_find_success = 0; - -extern inline void show_swap_cache_info(void) -{ - printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n", - swap_cache_add_total, swap_cache_add_success, - swap_cache_del_total, swap_cache_del_success, - swap_cache_find_total, swap_cache_find_success); -} -#endif - -extern inline int add_to_swap_cache(unsigned long addr, unsigned long entry) -{ - struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)]; - -#ifdef SWAP_CACHE_INFO - swap_cache_add_total++; -#endif - if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { - atomic_exchange(swap_cache[addr >> PAGE_SHIFT],entry); - if (entry) { - printk("swap_cache: replacing non-NULL entry\n"); - } -#ifdef SWAP_CACHE_INFO - swap_cache_add_success++; -#endif - return 1; - } - return 0; -} - -static unsigned long init_swap_cache(unsigned long mem_start, - unsigned long mem_end) -{ - unsigned long swap_cache_size; - - mem_start = (mem_start + 15) & ~15; - swap_cache = (unsigned long *) mem_start; - swap_cache_size = mem_end >> PAGE_SHIFT; - memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long)); - return (unsigned long) (swap_cache + swap_cache_size); -} - -void rw_swap_page(int rw, unsigned long entry, char * buf) -{ - unsigned long type, offset; - struct swap_info_struct * p; - - type = SWP_TYPE(entry); - if (type >= nr_swapfiles) { - printk("Internal error: bad swap-device\n"); - return; - } - p = &swap_info[type]; - offset = SWP_OFFSET(entry); - if (offset >= p->max) { - printk("rw_swap_page: weirdness\n"); - return; - } - if (!(p->flags & SWP_USED)) { - printk("Trying to swap to unused swap-device\n"); - return; - } - while (set_bit(offset,p->swap_lockmap)) - sleep_on(&lock_queue); - if (rw == READ) - kstat.pswpin++; - else - kstat.pswpout++; - if (p->swap_device) { - ll_rw_page(rw,p->swap_device,offset,buf); - } else if (p->swap_file) { - struct inode *swapf = p->swap_file; - unsigned int zones[8]; - int i; - if (swapf->i_op->bmap == NULL - && swapf->i_op->smap != NULL){ - /* - With MsDOS, we use msdos_smap which return - a sector number (not a cluster or block number). - It is a patch to enable the UMSDOS project. - Other people are working on better solution. - - It sounds like ll_rw_swap_file defined - it operation size (sector size) based on - PAGE_SIZE and the number of block to read. - So using bmap or smap should work even if - smap will require more blocks. - */ - int j; - unsigned int block = offset << 3; - - for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){ - if (!(zones[i] = swapf->i_op->smap(swapf,block++))) { - printk("rw_swap_page: bad swap file\n"); - return; - } - } - }else{ - int j; - unsigned int block = offset - << (12 - swapf->i_sb->s_blocksize_bits); - - for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize) - if (!(zones[i] = bmap(swapf,block++))) { - printk("rw_swap_page: bad swap file\n"); - return; - } - } - ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf); - } else - printk("re_swap_page: no swap file or device\n"); - if (offset && !clear_bit(offset,p->swap_lockmap)) - printk("rw_swap_page: lock already cleared\n"); - wake_up(&lock_queue); -} - -unsigned int get_swap_page(void) -{ - struct swap_info_struct * p; - unsigned int offset, type; - - p = swap_info; - for (type = 0 ; type < nr_swapfiles ; type++,p++) { - if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK) - continue; - for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) { - if (p->swap_map[offset]) - continue; - p->swap_map[offset] = 1; - nr_swap_pages--; - if (offset == p->highest_bit) - p->highest_bit--; - p->lowest_bit = offset; - return SWP_ENTRY(type,offset); - } - } - return 0; -} - -unsigned long swap_duplicate(unsigned long entry) -{ - struct swap_info_struct * p; - unsigned long offset, type; - - if (!entry) - return 0; - offset = SWP_OFFSET(entry); - type = SWP_TYPE(entry); - if (type == SHM_SWP_TYPE) - return entry; - if (type >= nr_swapfiles) { - printk("Trying to duplicate nonexistent swap-page\n"); - return 0; - } - p = type + swap_info; - if (offset >= p->max) { - printk("swap_duplicate: weirdness\n"); - return 0; - } - if (!p->swap_map[offset]) { - printk("swap_duplicate: trying to duplicate unused page\n"); - return 0; - } - p->swap_map[offset]++; - return entry; -} - -void swap_free(unsigned long entry) -{ - struct swap_info_struct * p; - unsigned long offset, type; - - if (!entry) - return; - type = SWP_TYPE(entry); - if (type == SHM_SWP_TYPE) - return; - if (type >= nr_swapfiles) { - printk("Trying to free nonexistent swap-page\n"); - return; - } - p = & swap_info[type]; - offset = SWP_OFFSET(entry); - if (offset >= p->max) { - printk("swap_free: weirdness\n"); - return; - } - if (!(p->flags & SWP_USED)) { - printk("Trying to free swap from unused swap-device\n"); - return; - } - while (set_bit(offset,p->swap_lockmap)) - sleep_on(&lock_queue); - if (offset < p->lowest_bit) - p->lowest_bit = offset; - if (offset > p->highest_bit) - p->highest_bit = offset; - if (!p->swap_map[offset]) - printk("swap_free: swap-space map bad (entry %08lx)\n",entry); - else - if (!--p->swap_map[offset]) - nr_swap_pages++; - if (!clear_bit(offset,p->swap_lockmap)) - printk("swap_free: lock already cleared\n"); - wake_up(&lock_queue); -} - -unsigned long swap_in(unsigned long entry) -{ - unsigned long page; - - if (!(page = get_free_page(GFP_KERNEL))) { - oom(current); - return BAD_PAGE; - } - read_swap_page(entry, (char *) page); - if (add_to_swap_cache(page, entry)) - return page | PAGE_VALID; - swap_free(entry); - return page | PAGE_DIRTY | PAGE_VALID; -} - -static inline int try_to_swap_out(unsigned long * table_ptr) -{ - unsigned long page, entry; - - page = *table_ptr; - if (!(PAGE_VALID & page)) - return 0; - if (page >= high_memory) - return 0; - if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED) - return 0; - - if ((PAGE_DIRTY & page) && delete_from_swap_cache(page)) { - *table_ptr &= ~PAGE_ACCESSED; - return 0; - } - if (PAGE_ACCESSED & page) { - *table_ptr &= ~PAGE_ACCESSED; - return 0; - } - if (PAGE_DIRTY & page) { - page &= PAGE_MASK; - if (mem_map[MAP_NR(page)] != 1) - return 0; - if (!(entry = get_swap_page())) - return 0; - *table_ptr = entry; - invalidate(); - write_swap_page(entry, (char *) page); - free_page(page); - return 1; - } - if ((entry = find_in_swap_cache(page))) { - if (mem_map[MAP_NR(page)] != 1) { - *table_ptr |= PAGE_DIRTY; - printk("Aiee.. duplicated cached swap-cache entry\n"); - return 0; - } - *table_ptr = entry; - invalidate(); - free_page(page & PAGE_MASK); - return 1; - } - page &= PAGE_MASK; - *table_ptr = 0; - invalidate(); - free_page(page); - return 1 + mem_map[MAP_NR(page)]; -} - -/* - * A new implementation of swap_out(). We do not swap complete processes, - * but only a small number of blocks, before we continue with the next - * process. The number of blocks actually swapped is determined on the - * number of page faults, that this process actually had in the last time, - * so we won't swap heavily used processes all the time ... - * - * Note: the priority argument is a hint on much CPU to waste with the - * swap block search, not a hint, of how much blocks to swap with - * each process. - * - * (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de - */ - -/* - * These are the minimum and maximum number of pages to swap from one process, - * before proceeding to the next: - */ -#define SWAP_MIN 4 -#define SWAP_MAX 32 - -/* - * The actual number of pages to swap is determined as: - * SWAP_RATIO / (number of recent major page faults) - */ -#define SWAP_RATIO 128 - -static int swap_out_process(struct task_struct * p) -{ - unsigned long address; - unsigned long offset; - unsigned long *pgdir; - unsigned long pg_table; - - /* - * Go through process' page directory. - */ - address = p->mm->swap_address; - pgdir = (address >> PGDIR_SHIFT) + (unsigned long *) p->tss.pg_dir; - offset = address & ~PGDIR_MASK; - address &= PGDIR_MASK; - for ( ; address < TASK_SIZE ; - pgdir++, address = address + PGDIR_SIZE, offset = 0) { - pg_table = *pgdir; - if (pg_table >= high_memory) - continue; - if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED) - continue; - if (!(PAGE_VALID & pg_table)) { - printk("swap_out_process (%s): bad page-table at vm %08lx: %08lx\n", - p->comm, address + offset, pg_table); - *pgdir = 0; - continue; - } - pg_table &= 0xfffff000; - - /* - * Go through this page table. - */ - for( ; offset < ~PGDIR_MASK ; offset += PAGE_SIZE) { - switch(try_to_swap_out((unsigned long *) (pg_table + (offset >> 10)))) { - case 0: - break; - - case 1: - p->mm->rss--; - /* continue with the following page the next time */ - p->mm->swap_address = address + offset + PAGE_SIZE; - return 1; - - default: - p->mm->rss--; - break; - } - } - } - /* - * Finish work with this process, if we reached the end of the page - * directory. Mark restart from the beginning the next time. - */ - p->mm->swap_address = 0; - return 0; -} - -static int swap_out(unsigned int priority) -{ - static int swap_task; - int loop; - int counter = NR_TASKS * 2 >> priority; - struct task_struct *p; - - counter = NR_TASKS * 2 >> priority; - for(; counter >= 0; counter--, swap_task++) { - /* - * Check that swap_task is suitable for swapping. If not, look for - * the next suitable process. - */ - loop = 0; - while(1) { - if (swap_task >= NR_TASKS) { - swap_task = 1; - if (loop) - /* all processes are unswappable or already swapped out */ - return 0; - loop = 1; - } - - p = task[swap_task]; - if (p && p->mm->swappable && p->mm->rss) - break; - - swap_task++; - } - - /* - * Determine the number of pages to swap from this process. - */ - if (!p->mm->swap_cnt) { - p->mm->dec_flt = (p->mm->dec_flt * 3) / 4 + p->mm->maj_flt - p->mm->old_maj_flt; - p->mm->old_maj_flt = p->mm->maj_flt; - - if (p->mm->dec_flt >= SWAP_RATIO / SWAP_MIN) { - p->mm->dec_flt = SWAP_RATIO / SWAP_MIN; - p->mm->swap_cnt = SWAP_MIN; - } else if (p->mm->dec_flt <= SWAP_RATIO / SWAP_MAX) - p->mm->swap_cnt = SWAP_MAX; - else - p->mm->swap_cnt = SWAP_RATIO / p->mm->dec_flt; - } - if (swap_out_process(p)) { - if ((--p->mm->swap_cnt) == 0) - swap_task++; - return 1; - } - } - return 0; -} - -static int try_to_free_page(int priority) -{ - int i=6; - - while (i--) { - if (priority != GFP_NOBUFFER && shrink_buffers(i)) - return 1; - if (shm_swap(i)) - return 1; - if (swap_out(i)) - return 1; - } - return 0; -} - -static inline void add_mem_queue(struct mem_list * head, struct mem_list * entry) -{ - entry->prev = head; - entry->next = head->next; - entry->next->prev = entry; - head->next = entry; -} - -static inline void remove_mem_queue(struct mem_list * head, struct mem_list * entry) -{ - entry->next->prev = entry->prev; - entry->prev->next = entry->next; -} - -/* - * Free_page() adds the page to the free lists. This is optimized for - * fast normal cases (no error jumps taken normally). - * - * The way to optimize jumps for gcc-2.2.2 is to: - * - select the "normal" case and put it inside the if () { XXX } - * - no else-statements if you can avoid them - * - * With the above two rules, you get a straight-line execution path - * for the normal case, giving better asm-code. - */ - -/* - * Buddy system. Hairy. You really aren't expected to understand this - */ -static inline void free_pages_ok(unsigned long addr, unsigned long order) -{ - unsigned long index = addr >> (PAGE_SHIFT + 1 + order); - unsigned long mask = PAGE_MASK << order; - - addr &= mask; - nr_free_pages += 1 << order; - while (order < NR_MEM_LISTS-1) { - if (!change_bit(index, free_area_map[order])) - break; - remove_mem_queue(free_area_list+order, (struct mem_list *) (addr ^ (1+~mask))); - order++; - index >>= 1; - mask <<= 1; - addr &= mask; - } - add_mem_queue(free_area_list+order, (struct mem_list *) addr); -} - -static inline void check_free_buffers(unsigned long addr) -{ - struct buffer_head * bh; - - bh = buffer_pages[MAP_NR(addr)]; - if (bh) { - struct buffer_head *tmp = bh; - do { - if (tmp->b_list == BUF_SHARED && tmp->b_dev != 0xffff) - refile_buffer(tmp); - tmp = tmp->b_this_page; - } while (tmp != bh); - } -} - -void free_pages(unsigned long addr, unsigned long order) -{ - if (addr < high_memory) { - unsigned long flag; - unsigned short * map = mem_map + MAP_NR(addr); - if (*map) { - if (!(*map & MAP_PAGE_RESERVED)) { - save_flags(flag); - cli(); - if (!--*map) { - free_pages_ok(addr, order); - delete_from_swap_cache(addr); - } - restore_flags(flag); - if (*map == 1) - check_free_buffers(addr); - } - return; - } - printk("Trying to free free memory (%08lx): memory probably corrupted\n",addr); - printk("PC = %08lx\n",*(((unsigned long *)&addr)-1)); - return; - } -} - -/* - * Some ugly macros to speed up __get_free_pages().. - */ -#define RMQUEUE(order) \ -do { struct mem_list * queue = free_area_list+order; \ - unsigned long new_order = order; \ - do { struct mem_list *next = queue->next; \ - if (queue != next) { \ - (queue->next = next->next)->prev = queue; \ - mark_used((unsigned long) next, new_order); \ - nr_free_pages -= 1 << order; \ - restore_flags(flags); \ - EXPAND(next, order, new_order); \ - return (unsigned long) next; \ - } new_order++; queue++; \ - } while (new_order < NR_MEM_LISTS); \ -} while (0) - -static inline int mark_used(unsigned long addr, unsigned long order) -{ - return change_bit(addr >> (PAGE_SHIFT+1+order), free_area_map[order]); -} - -#define EXPAND(addr,low,high) \ -do { unsigned long size = PAGE_SIZE << high; \ - while (high > low) { \ - high--; size >>= 1; cli(); \ - add_mem_queue(free_area_list+high, addr); \ - mark_used((unsigned long) addr, high); \ - restore_flags(flags); \ - addr = (struct mem_list *) (size + (unsigned long) addr); \ - } mem_map[MAP_NR((unsigned long) addr)] = 1; \ -} while (0) - -unsigned long __get_free_pages(int priority, unsigned long order) -{ - unsigned long flags; - int reserved_pages; - - if (intr_count && priority != GFP_ATOMIC) { - static int count = 0; - if (++count < 5) { - printk("gfp called nonatomically from interrupt %p\n", - __builtin_return_address(0)); - priority = GFP_ATOMIC; - } - } - reserved_pages = 5; - if (priority != GFP_NFS) - reserved_pages = min_free_pages; - save_flags(flags); -repeat: - cli(); - if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) { - RMQUEUE(order); - restore_flags(flags); - return 0; - } - restore_flags(flags); - if (priority != GFP_BUFFER && try_to_free_page(priority)) - goto repeat; - return 0; -} - -/* - * Yes, I know this is ugly. Don't tell me. - */ -unsigned long __get_dma_pages(int priority, unsigned long order) -{ - unsigned long list = 0; - unsigned long result; - unsigned long limit = 16*1024*1024; - - /* if (EISA_bus) limit = ~0UL; */ - if (priority != GFP_ATOMIC) - priority = GFP_BUFFER; - for (;;) { - result = __get_free_pages(priority, order); - if (result < limit) /* covers failure as well */ - break; - *(unsigned long *) result = list; - list = result; - } - while (list) { - unsigned long tmp = list; - list = *(unsigned long *) list; - free_pages(tmp, order); - } - return result; -} - -/* - * Show free area list (used inside shift_scroll-lock stuff) - * We also calculate the percentage fragmentation. We do this by counting the - * memory on each free list with the exception of the first item on the list. - */ -void show_free_areas(void) -{ - unsigned long order, flags; - unsigned long total = 0; - - printk("Free pages: %6dkB\n ( ",nr_free_pages<<(PAGE_SHIFT-10)); - save_flags(flags); - cli(); - for (order=0 ; order < NR_MEM_LISTS; order++) { - struct mem_list * tmp; - unsigned long nr = 0; - for (tmp = free_area_list[order].next ; tmp != free_area_list + order ; tmp = tmp->next) { - nr ++; - } - total += nr * (4 << order); - printk("%lu*%ukB ", nr, 4 << order); - } - restore_flags(flags); - printk("= %lukB)\n", total); -#ifdef SWAP_CACHE_INFO - show_swap_cache_info(); -#endif -} - -/* - * Trying to stop swapping from a file is fraught with races, so - * we repeat quite a bit here when we have to pause. swapoff() - * isn't exactly timing-critical, so who cares? - */ -static int try_to_unuse(unsigned int type) -{ - int nr, pgt, pg; - unsigned long page, *ppage; - unsigned long tmp = 0; - struct task_struct *p; - - nr = 0; - -/* - * When we have to sleep, we restart the whole algorithm from the same - * task we stopped in. That at least rids us of all races. - */ -repeat: - for (; nr < NR_TASKS ; nr++) { - p = task[nr]; - if (!p) - continue; - for (pgt = 0 ; pgt < PTRS_PER_PAGE ; pgt++) { - ppage = pgt + ((unsigned long *) p->tss.pg_dir); - page = *ppage; - if (!page) - continue; - if (!(page & PAGE_VALID) || (page >= high_memory)) - continue; - if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED) - continue; - ppage = (unsigned long *) (page & PAGE_MASK); - for (pg = 0 ; pg < PTRS_PER_PAGE ; pg++,ppage++) { - page = *ppage; - if (!page) - continue; - if (page & PAGE_VALID) { - if (!(page = in_swap_cache(page))) - continue; - if (SWP_TYPE(page) != type) - continue; - *ppage |= PAGE_DIRTY; - delete_from_swap_cache(*ppage); - continue; - } - if (SWP_TYPE(page) != type) - continue; - if (!tmp) { - if (!(tmp = __get_free_page(GFP_KERNEL))) - return -ENOMEM; - goto repeat; - } - read_swap_page(page, (char *) tmp); - if (*ppage == page) { - *ppage = tmp | (PAGE_DIRTY | PAGE_PRIVATE); - ++p->mm->rss; - swap_free(page); - tmp = 0; - } - goto repeat; - } - } - } - free_page(tmp); - return 0; -} - -asmlinkage int sys_swapoff(const char * specialfile) -{ - struct swap_info_struct * p; - struct inode * inode; - unsigned int type; - int i; - - if (!suser()) - return -EPERM; - i = namei(specialfile,&inode); - if (i) - return i; - p = swap_info; - for (type = 0 ; type < nr_swapfiles ; type++,p++) { - if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK) - continue; - if (p->swap_file) { - if (p->swap_file == inode) - break; - } else { - if (!S_ISBLK(inode->i_mode)) - continue; - if (p->swap_device == inode->i_rdev) - break; - } - } - iput(inode); - if (type >= nr_swapfiles) - return -EINVAL; - p->flags = SWP_USED; - i = try_to_unuse(type); - if (i) { - p->flags = SWP_WRITEOK; - return i; - } - nr_swap_pages -= p->pages; - iput(p->swap_file); - p->swap_file = NULL; - p->swap_device = 0; - vfree(p->swap_map); - p->swap_map = NULL; - free_page((long) p->swap_lockmap); - p->swap_lockmap = NULL; - p->flags = 0; - return 0; -} - -/* - * Written 01/25/92 by Simmule Turner, heavily changed by Linus. - * - * The swapon system call - */ -asmlinkage int sys_swapon(const char * specialfile) -{ - struct swap_info_struct * p; - struct inode * swap_inode; - unsigned int type; - int i,j; - int error; - - if (!suser()) - return -EPERM; - p = swap_info; - for (type = 0 ; type < nr_swapfiles ; type++,p++) - if (!(p->flags & SWP_USED)) - break; - if (type >= MAX_SWAPFILES) - return -EPERM; - if (type >= nr_swapfiles) - nr_swapfiles = type+1; - p->flags = SWP_USED; - p->swap_file = NULL; - p->swap_device = 0; - p->swap_map = NULL; - p->swap_lockmap = NULL; - p->lowest_bit = 0; - p->highest_bit = 0; - p->max = 1; - error = namei(specialfile,&swap_inode); - if (error) - goto bad_swap; - p->swap_file = swap_inode; - error = -EBUSY; - if (swap_inode->i_count != 1) - goto bad_swap; - error = -EINVAL; - if (S_ISBLK(swap_inode->i_mode)) { - p->swap_device = swap_inode->i_rdev; - p->swap_file = NULL; - iput(swap_inode); - error = -ENODEV; - if (!p->swap_device) - goto bad_swap; - error = -EBUSY; - for (i = 0 ; i < nr_swapfiles ; i++) { - if (i == type) - continue; - if (p->swap_device == swap_info[i].swap_device) - goto bad_swap; - } - } else if (!S_ISREG(swap_inode->i_mode)) - goto bad_swap; - p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER); - if (!p->swap_lockmap) { - printk("Unable to start swapping: out of memory :-)\n"); - error = -ENOMEM; - goto bad_swap; - } - read_swap_page(SWP_ENTRY(type,0), (char *) p->swap_lockmap); - if (memcmp("SWAP-SPACE",p->swap_lockmap+4086,10)) { - printk("Unable to find swap-space signature\n"); - error = -EINVAL; - goto bad_swap; - } - memset(p->swap_lockmap+PAGE_SIZE-10,0,10); - j = 0; - p->lowest_bit = 0; - p->highest_bit = 0; - for (i = 1 ; i < 8*PAGE_SIZE ; i++) { - if (test_bit(i,p->swap_lockmap)) { - if (!p->lowest_bit) - p->lowest_bit = i; - p->highest_bit = i; - p->max = i+1; - j++; - } - } - if (!j) { - printk("Empty swap-file\n"); - error = -EINVAL; - goto bad_swap; - } - p->swap_map = (unsigned char *) vmalloc(p->max); - if (!p->swap_map) { - error = -ENOMEM; - goto bad_swap; - } - for (i = 1 ; i < p->max ; i++) { - if (test_bit(i,p->swap_lockmap)) - p->swap_map[i] = 0; - else - p->swap_map[i] = 0x80; - } - p->swap_map[0] = 0x80; - memset(p->swap_lockmap,0,PAGE_SIZE); - p->flags = SWP_WRITEOK; - p->pages = j; - nr_swap_pages += j; - printk("Adding Swap: %dk swap-space\n",j<<2); - return 0; -bad_swap: - free_page((long) p->swap_lockmap); - vfree(p->swap_map); - iput(p->swap_file); - p->swap_device = 0; - p->swap_file = NULL; - p->swap_map = NULL; - p->swap_lockmap = NULL; - p->flags = 0; - return error; -} - -void si_swapinfo(struct sysinfo *val) -{ - unsigned int i, j; - - val->freeswap = val->totalswap = 0; - for (i = 0; i < nr_swapfiles; i++) { - if ((swap_info[i].flags & SWP_WRITEOK) != SWP_WRITEOK) - continue; - for (j = 0; j < swap_info[i].max; ++j) - switch (swap_info[i].swap_map[j]) { - case 128: - continue; - case 0: - ++val->freeswap; - default: - ++val->totalswap; - } - } - val->freeswap <<= PAGE_SHIFT; - val->totalswap <<= PAGE_SHIFT; - return; -} - -/* - * set up the free-area data structures: - * - mark all pages MAP_PAGE_RESERVED - * - mark all memory queues empty - * - clear the memory bitmaps - */ -unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem) -{ - unsigned short * p; - unsigned long mask = PAGE_MASK; - int i; - - /* - * select nr of pages we try to keep free for important stuff - * with a minimum of 16 pages. This is totally arbitrary - */ - i = end_mem >> (PAGE_SHIFT+6); - if (i < 16) - i = 16; - min_free_pages = i; - start_mem = init_swap_cache(start_mem, end_mem); - mem_map = (unsigned short *) start_mem; - p = mem_map + MAP_NR(end_mem); - start_mem = (unsigned long) p; - while (p > mem_map) - *--p = MAP_PAGE_RESERVED; - - for (i = 0 ; i < NR_MEM_LISTS ; i++, mask <<= 1) { - unsigned long bitmap_size; - free_area_list[i].prev = free_area_list[i].next = &free_area_list[i]; - end_mem = (end_mem + ~mask) & mask; - bitmap_size = end_mem >> (PAGE_SHIFT + i); - bitmap_size = (bitmap_size + 7) >> 3; - free_area_map[i] = (unsigned char *) start_mem; - memset((void *) start_mem, 0, bitmap_size); - start_mem += bitmap_size; - } - return start_mem; -} diff --git a/arch/mips/mm/vmalloc.c b/arch/mips/mm/vmalloc.c deleted file mode 100644 index 9b3ab7f59..000000000 --- a/arch/mips/mm/vmalloc.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * linux/mm/vmalloc.c - * - * Copyright (C) 1993 Linus Torvalds - */ - -#include <asm/system.h> -#include <linux/config.h> - -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/head.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/malloc.h> -#include <asm/segment.h> - -struct vm_struct { - unsigned long flags; - void * addr; - unsigned long size; - struct vm_struct * next; -}; - -static struct vm_struct * vmlist = NULL; - -/* Just any arbitrary offset to the start of the vmalloc VM area: the - * current 8MB value just means that there will be a 8MB "hole" after the - * physical memory until the kernel virtual memory starts. That means that - * any out-of-bounds memory accesses will hopefully be caught. - * The vmalloc() routines leaves a hole of 4kB between each vmalloced - * area for the same reason. ;) - */ -#define VMALLOC_OFFSET (8*1024*1024) - -static inline void set_pgdir(unsigned long dindex, unsigned long value) -{ - struct task_struct * p; - - p = &init_task; - do { - ((unsigned long *) p->tss.pg_dir)[dindex] = value; - p = p->next_task; - } while (p != &init_task); -} - -static int free_area_pages(unsigned long dindex, unsigned long index, unsigned long nr) -{ - unsigned long page, *pte; - - if (!(PAGE_VALID & (page = swapper_pg_dir[dindex]))) - return 0; - page &= PAGE_MASK; - pte = index + (unsigned long *) page; - do { - unsigned long pg = *pte; - *pte = 0; - if (pg & PAGE_VALID) - free_page(pg); - pte++; - } while (--nr); - pte = (unsigned long *) page; - for (nr = 0 ; nr < 1024 ; nr++, pte++) - if (*pte) - return 0; - set_pgdir(dindex,0); - mem_map[MAP_NR(page)] = 1; - free_page(page); - invalidate(); - return 0; -} - -static int alloc_area_pages(unsigned long dindex, unsigned long index, unsigned long nr) -{ - unsigned long page, *pte; - - page = swapper_pg_dir[dindex]; - if (!page) { - page = get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - if (swapper_pg_dir[dindex]) { - free_page(page); - page = swapper_pg_dir[dindex]; - } else { - mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED; - set_pgdir(dindex, page | PAGE_SHARED); - } - } - page &= PAGE_MASK; - pte = index + (unsigned long *) page; - *pte = PAGE_SHARED; /* remove a race with vfree() */ - do { - unsigned long pg = get_free_page(GFP_KERNEL); - - if (!pg) - return -ENOMEM; - *pte = pg | PAGE_SHARED; - pte++; - } while (--nr); - invalidate(); - return 0; -} - -static int do_area(void * addr, unsigned long size, - int (*area_fn)(unsigned long,unsigned long,unsigned long)) -{ - unsigned long nr, dindex, index; - - nr = size >> PAGE_SHIFT; - dindex = (TASK_SIZE + (unsigned long) addr) >> 22; - index = (((unsigned long) addr) >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - while (nr > 0) { - unsigned long i = PTRS_PER_PAGE - index; - - if (i > nr) - i = nr; - nr -= i; - if (area_fn(dindex, index, i)) - return -1; - index = 0; - dindex++; - } - return 0; -} - -void vfree(void * addr) -{ - struct vm_struct **p, *tmp; - - if (!addr) - return; - if ((PAGE_SIZE-1) & (unsigned long) addr) { - printk("Trying to vfree() bad address (%p)\n", addr); - return; - } - for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) { - if (tmp->addr == addr) { - *p = tmp->next; - do_area(tmp->addr, tmp->size, free_area_pages); - kfree(tmp); - return; - } - } - printk("Trying to vfree() nonexistent vm area (%p)\n", addr); -} - -void * vmalloc(unsigned long size) -{ - void * addr; - struct vm_struct **p, *tmp, *area; - - size = PAGE_ALIGN(size); - if (!size || size > high_memory) - return NULL; - area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); - if (!area) - return NULL; - addr = (void *) ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); - area->size = size + PAGE_SIZE; - area->next = NULL; - for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { - if (size + (unsigned long) addr < (unsigned long) tmp->addr) - break; - addr = (void *) (tmp->size + (unsigned long) tmp->addr); - } - area->addr = addr; - area->next = *p; - *p = area; - if (do_area(addr, size, alloc_area_pages)) { - vfree(addr); - return NULL; - } - return addr; -} - -int vread(char *buf, char *addr, int count) -{ - struct vm_struct **p, *tmp; - char *vaddr, *buf_start = buf; - int n; - - for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { - vaddr = (char *) tmp->addr; - while (addr < vaddr) { - if (count == 0) - goto finished; - put_fs_byte('\0', buf++), addr++, count--; - } - n = tmp->size - PAGE_SIZE; - if (addr > vaddr) - n -= addr - vaddr; - while (--n >= 0) { - if (count == 0) - goto finished; - put_fs_byte(*addr++, buf++), count--; - } - } -finished: - return buf - buf_start; -} diff --git a/arch/mips/sched.c b/arch/mips/sched.c deleted file mode 100644 index 5c60764a8..000000000 --- a/arch/mips/sched.c +++ /dev/null @@ -1,804 +0,0 @@ -/* - * linux/kernel/sched.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * 'sched.c' is the main kernel file. It contains scheduling primitives - * (sleep_on, wakeup, schedule etc) as well as a number of simple system - * call functions (type getpid(), which just extracts a field from - * current-task - */ - -#include <linux/config.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/kernel.h> -#include <linux/kernel_stat.h> -#include <linux/fdreg.h> -#include <linux/errno.h> -#include <linux/time.h> -#include <linux/ptrace.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/tqueue.h> -#include <linux/resource.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/segment.h> - -#define TIMER_IRQ 0 - -#include <linux/timex.h> - -/* - * kernel variables - */ -long tick = 1000000 / HZ; /* timer interrupt period */ -volatile struct timeval xtime; /* The current time */ -int tickadj = 500/HZ; /* microsecs */ - -DECLARE_TASK_QUEUE(tq_timer); -DECLARE_TASK_QUEUE(tq_immediate); - -/* - * phase-lock loop variables - */ -int time_status = TIME_BAD; /* clock synchronization status */ -long time_offset = 0; /* time adjustment (us) */ -long time_constant = 0; /* pll time constant */ -long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */ -long time_precision = 1; /* clock precision (us) */ -long time_maxerror = 0x70000000;/* maximum error */ -long time_esterror = 0x70000000;/* estimated error */ -long time_phase = 0; /* phase offset (scaled us) */ -long time_freq = 0; /* frequency offset (scaled ppm) */ -long time_adj = 0; /* tick adjust (scaled 1 / HZ) */ -long time_reftime = 0; /* time at last adjustment (s) */ - -long time_adjust = 0; -long time_adjust_step = 0; - -int need_resched = 0; -unsigned long event = 0; - -/* - * Tell us the machine setup.. - */ -int hard_math = 0; /* set by boot/head.S */ -int wp_works_ok = 0; /* set if paging hardware honours WP */ - -/* - * Bus types .. - */ -int EISA_bus = 0; - -extern int _setitimer(int, struct itimerval *, struct itimerval *); -unsigned long * prof_buffer = NULL; -unsigned long prof_len = 0; - -#define _S(nr) (1<<((nr)-1)) - -extern void mem_use(void); - -extern int timer_interrupt(void); -asmlinkage int system_call(void); - -static unsigned long init_kernel_stack[1024] = { STACK_MAGIC, }; -static struct vm_area_struct init_mmap = INIT_MMAP; -struct task_struct init_task = INIT_TASK; - -unsigned long volatile jiffies=0; - -struct task_struct *current = &init_task; -struct task_struct *last_task_used_math = NULL; - -struct task_struct * task[NR_TASKS] = {&init_task, }; - -long user_stack [ PAGE_SIZE>>2 ] = { STACK_MAGIC, }; - -struct kernel_stat kstat = { 0 }; - -#ifndef CONFIG_MATH_EMULATION - -/* - * FIXME: There is no fpa emulator yet - */ -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); - schedule(); -} - -#endif /* CONFIG_MATH_EMULATION */ - -unsigned long itimer_ticks = 0; -unsigned long itimer_next = ~0; - -/* - * 'schedule()' is the scheduler function. It's a very simple and nice - * scheduler: it's not perfect, but certainly works for most things. - * The one thing you might take a look at is the signal-handler code here. - * - * NOTE!! Task 0 is the 'idle' task, which gets called when no other - * tasks can run. It can not be killed, and it cannot sleep. The 'state' - * information in task[0] is never used. - * - * The "confuse_gcc" goto is used only to get better assembly code.. - * Dijkstra probably hates me. - */ -asmlinkage void schedule(void) -{ - int c; - struct task_struct * p; - struct task_struct * next; - unsigned long ticks; - - /* - * check alarm, wake up any interruptible tasks that have got a signal - */ - if (intr_count) { - printk("Aiee: scheduling in interrupt\n"); - intr_count = 0; - } - cli(); - ticks = itimer_ticks; - itimer_ticks = 0; - itimer_next = ~0; - sti(); - need_resched = 0; - p = &init_task; - for (;;) { - if ((p = p->next_task) == &init_task) - goto confuse_gcc1; - if (ticks && p->it_real_value) { - if (p->it_real_value <= ticks) { - send_sig(SIGALRM, p, 1); - if (!p->it_real_incr) { - p->it_real_value = 0; - goto end_itimer; - } - do { - p->it_real_value += p->it_real_incr; - } while (p->it_real_value <= ticks); - } - p->it_real_value -= ticks; - if (p->it_real_value < itimer_next) - itimer_next = p->it_real_value; - } -end_itimer: - if (p->state != TASK_INTERRUPTIBLE) - continue; - if (p->signal & ~p->blocked) { - p->state = TASK_RUNNING; - continue; - } - if (p->timeout && p->timeout <= jiffies) { - p->timeout = 0; - p->state = TASK_RUNNING; - } - } -confuse_gcc1: - -/* this is the scheduler proper: */ -#if 0 - /* give processes that go to sleep a bit higher priority.. */ - /* This depends on the values for TASK_XXX */ - /* This gives smoother scheduling for some things, but */ - /* can be very unfair under some circumstances, so.. */ - if (TASK_UNINTERRUPTIBLE >= (unsigned) current->state && - current->counter < current->priority*2) { - ++current->counter; - } -#endif - c = -1000; - next = p = &init_task; - for (;;) { - if ((p = p->next_task) == &init_task) - goto confuse_gcc2; - if (p->state == TASK_RUNNING && p->counter > c) - c = p->counter, next = p; - } -confuse_gcc2: - if (!c) { - for_each_task(p) - p->counter = (p->counter >> 1) + p->priority; - } - if (current == next) - return; - kstat.context_swtch++; - switch_to(next); -} - -asmlinkage int sys_pause(void) -{ - current->state = TASK_INTERRUPTIBLE; - schedule(); - return -ERESTARTNOHAND; -} - -/* - * wake_up doesn't wake up stopped processes - they have to be awakened - * with signals or similar. - * - * Note that this doesn't need cli-sti pairs: interrupts may not change - * the wait-queue structures directly, but only call wake_up() to wake - * a process. The process itself must remove the queue once it has woken. - */ -void wake_up(struct wait_queue **q) -{ - struct wait_queue *tmp; - struct task_struct * p; - - if (!q || !(tmp = *q)) - return; - do { - if ((p = tmp->task) != NULL) { - if ((p->state == TASK_UNINTERRUPTIBLE) || - (p->state == TASK_INTERRUPTIBLE)) { - p->state = TASK_RUNNING; - if (p->counter > current->counter + 3) - need_resched = 1; - } - } - if (!tmp->next) { - printk("wait_queue is bad (pc = %p)\n", - __builtin_return_address(0)); - printk(" q = %p\n",q); - printk(" *q = %p\n",*q); - printk(" tmp = %p\n",tmp); - break; - } - tmp = tmp->next; - } while (tmp != *q); -} - -void wake_up_interruptible(struct wait_queue **q) -{ - struct wait_queue *tmp; - struct task_struct * p; - - if (!q || !(tmp = *q)) - return; - do { - if ((p = tmp->task) != NULL) { - if (p->state == TASK_INTERRUPTIBLE) { - p->state = TASK_RUNNING; - if (p->counter > current->counter + 3) - need_resched = 1; - } - } - if (!tmp->next) { - printk("wait_queue is bad (eip = %p)\n", - __builtin_return_address(0)); - printk(" q = %p\n",q); - printk(" *q = %p\n",*q); - printk(" tmp = %p\n",tmp); - break; - } - tmp = tmp->next; - } while (tmp != *q); -} - -void __down(struct semaphore * sem) -{ - struct wait_queue wait = { current, NULL }; - add_wait_queue(&sem->wait, &wait); - current->state = TASK_UNINTERRUPTIBLE; - while (sem->count <= 0) { - schedule(); - current->state = TASK_UNINTERRUPTIBLE; - } - current->state = TASK_RUNNING; - remove_wait_queue(&sem->wait, &wait); -} - -static inline void __sleep_on(struct wait_queue **p, int state) -{ - unsigned long flags; - struct wait_queue wait = { current, NULL }; - - if (!p) - return; - if (current == task[0]) - panic("task[0] trying to sleep"); - current->state = state; - add_wait_queue(p, &wait); - save_flags(flags); - sti(); - schedule(); - remove_wait_queue(p, &wait); - restore_flags(flags); -} - -void interruptible_sleep_on(struct wait_queue **p) -{ - __sleep_on(p,TASK_INTERRUPTIBLE); -} - -void sleep_on(struct wait_queue **p) -{ - __sleep_on(p,TASK_UNINTERRUPTIBLE); -} - -/* - * The head for the timer-list has a "expires" field of MAX_UINT, - * and the sorting routine counts on this.. - */ -static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL }; -#define SLOW_BUT_DEBUGGING_TIMERS 1 - -void add_timer(struct timer_list * timer) -{ - unsigned long flags; - struct timer_list *p; - -#if SLOW_BUT_DEBUGGING_TIMERS - if (timer->next || timer->prev) { - printk("add_timer() called with non-zero list from %p\n", - __builtin_return_address(0)); - return; - } -#endif - p = &timer_head; - timer->expires += jiffies; - save_flags(flags); - cli(); - do { - p = p->next; - } while (timer->expires > p->expires); - timer->next = p; - timer->prev = p->prev; - p->prev = timer; - timer->prev->next = timer; - restore_flags(flags); -} - -int del_timer(struct timer_list * timer) -{ - unsigned long flags; -#if SLOW_BUT_DEBUGGING_TIMERS - struct timer_list * p; - - p = &timer_head; - save_flags(flags); - cli(); - while ((p = p->next) != &timer_head) { - if (p == timer) { - timer->next->prev = timer->prev; - timer->prev->next = timer->next; - timer->next = timer->prev = NULL; - restore_flags(flags); - timer->expires -= jiffies; - return 1; - } - } - if (timer->next || timer->prev) - printk("del_timer() called from %p with timer not initialized\n", - __builtin_return_address(0)); - restore_flags(flags); - return 0; -#else - save_flags(flags); - cli(); - if (timer->next) { - timer->next->prev = timer->prev; - timer->prev->next = timer->next; - timer->next = timer->prev = NULL; - restore_flags(flags); - timer->expires -= jiffies; - return 1; - } - restore_flags(flags); - return 0; -#endif -} - -unsigned long timer_active = 0; -struct timer_struct timer_table[32]; - -/* - * Hmm.. Changed this, as the GNU make sources (load.c) seems to - * imply that avenrun[] is the standard name for this kind of thing. - * Nothing else seems to be standardized: the fractional size etc - * all seem to differ on different machines. - */ -unsigned long avenrun[3] = { 0,0,0 }; - -/* - * Nr of active tasks - counted in fixed-point numbers - */ -static unsigned long count_active_tasks(void) -{ - struct task_struct **p; - unsigned long nr = 0; - - for(p = &LAST_TASK; p > &FIRST_TASK; --p) - if (*p && ((*p)->state == TASK_RUNNING || - (*p)->state == TASK_UNINTERRUPTIBLE || - (*p)->state == TASK_SWAPPING)) - nr += FIXED_1; - return nr; -} - -static inline void calc_load(void) -{ - unsigned long active_tasks; /* fixed-point */ - static int count = LOAD_FREQ; - - if (count-- > 0) - return; - count = LOAD_FREQ; - active_tasks = count_active_tasks(); - CALC_LOAD(avenrun[0], EXP_1, active_tasks); - CALC_LOAD(avenrun[1], EXP_5, active_tasks); - CALC_LOAD(avenrun[2], EXP_15, active_tasks); -} - -/* - * this routine handles the overflow of the microsecond field - * - * The tricky bits of code to handle the accurate clock support - * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame. - * They were originally developed for SUN and DEC kernels. - * All the kudos should go to Dave for this stuff. - * - * These were ported to Linux by Philip Gladstone. - */ -static void second_overflow(void) -{ - long ltemp; - /* last time the cmos clock got updated */ - static long last_rtc_update=0; - extern int set_rtc_mmss(unsigned long); - - /* Bump the maxerror field */ - time_maxerror = (0x70000000-time_maxerror < time_tolerance) ? - 0x70000000 : (time_maxerror + time_tolerance); - - /* Run the PLL */ - if (time_offset < 0) { - ltemp = (-(time_offset+1) >> (SHIFT_KG + time_constant)) + 1; - time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); - time_offset += (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE); - time_adj = - time_adj; - } else if (time_offset > 0) { - ltemp = ((time_offset-1) >> (SHIFT_KG + time_constant)) + 1; - time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); - time_offset -= (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE); - } else { - time_adj = 0; - } - - time_adj += (time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE)) - + FINETUNE; - - /* Handle the leap second stuff */ - switch (time_status) { - case TIME_INS: - /* ugly divide should be replaced */ - if (xtime.tv_sec % 86400 == 0) { - xtime.tv_sec--; /* !! */ - time_status = TIME_OOP; - printk("Clock: inserting leap second 23:59:60 GMT\n"); - } - break; - - case TIME_DEL: - /* ugly divide should be replaced */ - if (xtime.tv_sec % 86400 == 86399) { - xtime.tv_sec++; - time_status = TIME_OK; - printk("Clock: deleting leap second 23:59:59 GMT\n"); - } - break; - - case TIME_OOP: - time_status = TIME_OK; - break; - } - if (xtime.tv_sec > last_rtc_update + 660) - 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 one min */ -} - -/* - * disregard lost ticks for now.. We don't care enough. - */ -static void timer_bh(void * unused) -{ - unsigned long mask; - struct timer_struct *tp; - struct timer_list * timer; - - cli(); - while ((timer = timer_head.next) != &timer_head && timer->expires < jiffies) { - void (*fn)(unsigned long) = timer->function; - unsigned long data = timer->data; - timer->next->prev = timer->prev; - timer->prev->next = timer->next; - timer->next = timer->prev = NULL; - sti(); - fn(data); - cli(); - } - sti(); - - for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) { - if (mask > timer_active) - break; - if (!(mask & timer_active)) - continue; - if (tp->expires > jiffies) - continue; - timer_active &= ~mask; - tp->fn(); - sti(); - } -} - -void tqueue_bh(void * unused) -{ - run_task_queue(&tq_timer); -} - -void immediate_bh(void * unused) -{ - run_task_queue(&tq_immediate); -} - -/* - * The int argument is really a (struct pt_regs *), in case the - * interrupt wants to know from where it was called. The timer - * irq uses this to decide if it should update the user or system - * times. - */ -static void do_timer(struct pt_regs * regs) -{ - unsigned long mask; - struct timer_struct *tp; - - long ltemp, psecs; - - /* Advance the phase, once it gets to one microsecond, then - * advance the tick more. - */ - time_phase += time_adj; - if (time_phase < -FINEUSEC) { - ltemp = -time_phase >> SHIFT_SCALE; - time_phase += ltemp << SHIFT_SCALE; - xtime.tv_usec += tick + time_adjust_step - ltemp; - } - else if (time_phase > FINEUSEC) { - ltemp = time_phase >> SHIFT_SCALE; - time_phase -= ltemp << SHIFT_SCALE; - xtime.tv_usec += tick + time_adjust_step + ltemp; - } else - xtime.tv_usec += tick + time_adjust_step; - - if (time_adjust) - { - /* We are doing an adjtime thing. - * - * Modify the value of the tick for next time. - * Note that a positive delta means we want the clock - * to run fast. This means that the tick should be bigger - * - * Limit the amount of the step for *next* tick to be - * in the range -tickadj .. +tickadj - */ - if (time_adjust > tickadj) - time_adjust_step = tickadj; - else if (time_adjust < -tickadj) - time_adjust_step = -tickadj; - else - time_adjust_step = time_adjust; - - /* Reduce by this step the amount of time left */ - time_adjust -= time_adjust_step; - } - else - time_adjust_step = 0; - - if (xtime.tv_usec >= 1000000) { - xtime.tv_usec -= 1000000; - xtime.tv_sec++; - second_overflow(); - } - - jiffies++; - calc_load(); - if (USES_USER_TIME(regs)) { - current->utime++; - if (current != task[0]) { - if (current->priority < 15) - kstat.cpu_nice++; - else - kstat.cpu_user++; - } - /* Update ITIMER_VIRT for current task if not in a system call */ - if (current->it_virt_value && !(--current->it_virt_value)) { - current->it_virt_value = current->it_virt_incr; - send_sig(SIGVTALRM,current,1); - } - } else { - current->stime++; - if(current != task[0]) - kstat.cpu_system++; -#if defined (CONFIG_PROFILE) & !defined (__mips__) - if (prof_buffer && current != task[0]) { - unsigned long eip = regs->eip; - eip >>= 2; - if (eip < prof_len) - prof_buffer[eip]++; - } -#endif - } - /* - * check the cpu time limit on the process. - */ - if ((current->rlim[RLIMIT_CPU].rlim_max != RLIM_INFINITY) && - (((current->stime + current->utime) / HZ) >= current->rlim[RLIMIT_CPU].rlim_max)) - send_sig(SIGKILL, current, 1); - if ((current->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) && - (((current->stime + current->utime) % HZ) == 0)) { - psecs = (current->stime + current->utime) / HZ; - /* send when equal */ - if (psecs == current->rlim[RLIMIT_CPU].rlim_cur) - send_sig(SIGXCPU, current, 1); - /* and every five seconds thereafter. */ - else if ((psecs > current->rlim[RLIMIT_CPU].rlim_cur) && - ((psecs - current->rlim[RLIMIT_CPU].rlim_cur) % 5) == 0) - send_sig(SIGXCPU, current, 1); - } - - if (current != task[0] && 0 > --current->counter) { - current->counter = 0; - need_resched = 1; - } - /* Update ITIMER_PROF for the current task */ - if (current->it_prof_value && !(--current->it_prof_value)) { - current->it_prof_value = current->it_prof_incr; - send_sig(SIGPROF,current,1); - } - for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) { - if (mask > timer_active) - break; - if (!(mask & timer_active)) - continue; - if (tp->expires > jiffies) - continue; - mark_bh(TIMER_BH); - } - cli(); - itimer_ticks++; - if (itimer_ticks > itimer_next) - need_resched = 1; - if (timer_head.next->expires < jiffies) - mark_bh(TIMER_BH); - if (tq_timer != &tq_last) - mark_bh(TQUEUE_BH); - sti(); -} - -asmlinkage int sys_alarm(long seconds) -{ - struct itimerval it_new, it_old; - - it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; - it_new.it_value.tv_sec = seconds; - it_new.it_value.tv_usec = 0; - _setitimer(ITIMER_REAL, &it_new, &it_old); - return(it_old.it_value.tv_sec + (it_old.it_value.tv_usec / 1000000)); -} - -asmlinkage int sys_getpid(void) -{ - return current->pid; -} - -asmlinkage int sys_getppid(void) -{ - return current->p_opptr->pid; -} - -asmlinkage int sys_getuid(void) -{ - return current->uid; -} - -asmlinkage int sys_geteuid(void) -{ - return current->euid; -} - -asmlinkage int sys_getgid(void) -{ - return current->gid; -} - -asmlinkage int sys_getegid(void) -{ - return current->egid; -} - -asmlinkage int sys_nice(long increment) -{ - int newprio; - - if (increment < 0 && !suser()) - return -EPERM; - newprio = current->priority - increment; - if (newprio < 1) - newprio = 1; - if (newprio > 35) - newprio = 35; - current->priority = newprio; - return 0; -} - -static void show_task(int nr,struct task_struct * p) -{ - unsigned long free; - static char * stat_nam[] = { "R", "S", "D", "Z", "T", "W" }; - - printk("%-8s %3d ", p->comm, (p == current) ? -nr : nr); - if (((unsigned) p->state) < sizeof(stat_nam)/sizeof(char *)) - printk(stat_nam[p->state]); - else - printk(" "); - if (p == current) - printk(" current "); - else - printk(" %08lX ", (unsigned long *)p->tss.cp0_epc); - for (free = 1; free < 1024 ; free++) { - if (((unsigned long *)p->kernel_stack_page)[free]) - break; - } - printk("%5lu %5d %6d ", free << 2, p->pid, p->p_pptr->pid); - if (p->p_cptr) - printk("%5d ", p->p_cptr->pid); - else - printk(" "); - if (p->p_ysptr) - printk("%7d", p->p_ysptr->pid); - else - printk(" "); - if (p->p_osptr) - printk(" %5d\n", p->p_osptr->pid); - else - printk("\n"); -} - -void show_state(void) -{ - int i; - - printk(" free sibling\n"); - printk(" task PC stack pid father child younger older\n"); - for (i=0 ; i<NR_TASKS ; i++) - if (task[i]) - show_task(i,task[i]); -} - -void sched_init(void) -{ - bh_base[TIMER_BH].routine = timer_bh; - bh_base[TQUEUE_BH].routine = tqueue_bh; - bh_base[IMMEDIATE_BH].routine = immediate_bh; - if (sizeof(struct sigaction) != 16) - panic("Struct sigaction MUST be 16 bytes"); - - outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ - outb_p(LATCH & 0xff , 0x40); /* LSB */ - outb(LATCH >> 8 , 0x40); /* MSB */ - if (request_irq(TIMER_IRQ,(void (*)(int)) do_timer, 0, "timer") != 0) - panic("Could not allocate timer IRQ!"); -} diff --git a/arch/mips/splx.c b/arch/mips/splx.c deleted file mode 100644 index 5ff5e8284..000000000 --- a/arch/mips/splx.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * splx.c - SYSV DDI/DKI ipl manipulation functions - * - * Internally, many unices use a range of different interrupt - * privilege levels, ie from "allow all interrupts" (7) to - * "allow no interrupts." (0) under SYSV. - * - * This a simple splx() function behaves as the SYSV DDI/DKI function does, - * although since Linux only implements the equivalent of level 0 (cli) and - * level 7 (sti), this implementation only implements those levels. - * - * Also, unlike the current Linux routines, splx() also returns the - * old privilege level so that it can be restored. - */ - -#include <asm/system.h> -#include <asm/mipsregs.h> - -int splx (int new_level) -{ - register int old_level, tmp; - - save_flags(tmp); - old_level = ((tmp & (ST0_IE|ST0_ERL|ST0_EXL)) == ST0_IE) ? 7 : 0; - if (new_level) - sti(); - else - cli(); - return old_level; -} diff --git a/arch/mips/traps.c b/arch/mips/traps.c deleted file mode 100644 index 9cb1252d5..000000000 --- a/arch/mips/traps.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * linux/kernel/traps.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * 'traps.c' handles hardware traps and faults after we have saved some - * state in 'asm.s'. Currently mostly a debugging-aid, will be extended - * to mainly kill the offending process (probably by giving it a signal, - * but possibly by killing it outright if necessary). - */ -#include <linux/head.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/ptrace.h> - -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/io.h> -#include <asm/mipsregs.h> - -static inline void console_verbose(void) -{ - extern int console_loglevel; - console_loglevel = 15; -} - -asmlinkage extern void handle_int(void); -asmlinkage extern void handle_mod(void); -asmlinkage extern void handle_tlbl(void); -asmlinkage extern void handle_tlbs(void); -asmlinkage extern void handle_adel(void); -asmlinkage extern void handle_ades(void); -asmlinkage extern void handle_ibe(void); -asmlinkage extern void handle_dbe(void); -asmlinkage extern void handle_sys(void); -asmlinkage extern void handle_bp(void); -asmlinkage extern void handle_ri(void); -asmlinkage extern void handle_cpu(void); -asmlinkage extern void handle_ov(void); -asmlinkage extern void handle_tr(void); -asmlinkage extern void handle_reserved(void); -asmlinkage extern void handle_fpe(void); - -void die_if_kernel(char * str, struct pt_regs * regs, long err) -{ - int i; - unsigned long *sp, *pc; - - if (regs->cp0_status & (ST0_ERL|ST0_EXL) == 0) - return; - - sp = (unsigned long *)regs->reg29; - pc = (unsigned long *)regs->cp0_epc; - - console_verbose(); - printk("%s: %08lx\n", str, err ); - - /* - * Saved main processor registers - */ - printk("at : %08lx\n", regs->reg1); - printk("v0 : %08lx %08lx\n", regs->reg2, regs->reg3); - printk("a0 : %08lx %08lx %08lx %08lx\n", - regs->reg4, regs->reg5, regs->reg6, regs->reg7); - printk("t0 : %08lx %08lx %08lx %08lx %08lx\n", - regs->reg8, regs->reg9, regs->reg10, regs->reg11, regs->reg12); - printk("t5 : %08lx %08lx %08lx %08lx %08lx\n", - regs->reg13, regs->reg14, regs->reg15, regs->reg24, regs->reg25); - printk("s0 : %08lx %08lx %08lx %08lx\n", - regs->reg16, regs->reg17, regs->reg18, regs->reg19); - printk("s4 : %08lx %08lx %08lx %08lx\n", - regs->reg20, regs->reg21, regs->reg22, regs->reg23); - printk("gp : %08lx\n", regs->reg28); - printk("sp : %08lx\n", regs->reg29); - printk("fp/s8: %08lx\n", regs->reg30); - printk("ra : %08lx\n", regs->reg31); - - /* - * Saved cp0 registers - */ - printk("EPC : %08lx\nErrorEPC: %08lx\nStatus: %08lx\n", - regs->cp0_epc, regs->cp0_errorepc, regs->cp0_status); - /* - * Some goodies... - */ - printk("Int : %ld\n", regs->interrupt); - - /* - * Dump the stack - */ - if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) - printk("Corrupted stack page\n"); - printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)\nStack: ", - current->comm, current->pid, 0xffff & i, - current->kernel_stack_page); - for(i=0;i<5;i++) - printk("%08lx ", *sp++); - - printk("\nCode: "); - for(i=0;i<5;i++) - printk("%08lx ", *pc++); - printk("\n"); - do_exit(SIGSEGV); -} - -void trap_init(void) -{ - int i; - -#if 0 - set_except_vector(0, handle_int); - set_except_vector(1, handle_mod); - set_except_vector(2, handle_tlbl); - set_except_vector(3, handle_tlbs); - set_except_vector(4, handle_adel); - set_except_vector(5, handle_ades); - set_except_vector(6, handle_ibe); - set_except_vector(7, handle_dbe); - set_except_vector(8, handle_sys); - set_except_vector(9, handle_bp); - set_except_vector(10, handle_ri); - set_except_vector(11, handle_cpu); - set_except_vector(12, handle_ov); - set_except_vector(13, handle_tr); - set_except_vector(14, handle_reserved); - set_except_vector(15, handle_fpe); - - for (i=16;i<256;i++) - set_except_vector(i, handle_reserved); -#endif -} diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile new file mode 100644 index 000000000..88bcd6578 --- /dev/null +++ b/arch/sparc/Makefile @@ -0,0 +1,33 @@ +# +# sparc/Makefile +# +# Makefile for the architecture dependent flags and dependencies on the +# Sparc. +# +# Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) +# + + +# If the solaris /bin/sh wasn't so broken, I wouldn't need the following +# line... +SHELL =/bin/bash + +# +# How to link, we send the linker the address at which the text section +# is to start. The prom loads us at 0x0-kernel_size. There is also an +# alias of this address space at 0xf8000000-(0xf8000000+kernel_size) but +# I ignore it and eliminate those mappings during vm initialization and +# just leave the low mapping. +# +LINKFLAGS = -N -Ttext 0x00004000 +CFLAGS := $(CFLAGS) -pipe + +HEAD := arch/sparc/kernel/head.o + +SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm +ARCHIVES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(ARCHIVES) +LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/lib/lib.a + +archclean: + +archdep: diff --git a/arch/sparc/config.in b/arch/sparc/config.in new file mode 100644 index 000000000..be9336eed --- /dev/null +++ b/arch/sparc/config.in @@ -0,0 +1,246 @@ +# +# arch/sparc/config.in +# +# Bare minimum configuration file for the Sparc. +# +# Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) +# +# For a description of the syntax of this configuration file, +# see the Configure script. +# + +comment 'Sparc Kernel setup' + +bool 'Sparc V8 kernel' CONFIG_SPARC_V8 y +bool 'Sparc SMP support' CONFIG_LINUX_SMP n +bool 'Sparc SUN4M support' CONFIG_SUN4M n +bool 'Sparc Reference MMU' CONFIG_SRMMU n +bool 'Networking support' CONFIG_NET n +bool 'Limit memory to low 16MB' CONFIG_MAX_16M n +bool 'System V IPC' CONFIG_SYSVIPC y +bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y + +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 (ALPHA)' CONFIG_IP_MULTICAST n +bool 'IP firewalling' CONFIG_IP_FIREWALL n +bool 'IP accounting' CONFIG_IP_ACCT n +comment '(it is safe to leave these untouched)' +bool 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n +bool 'Reverse ARP' CONFIG_INET_RARP n +bool 'Assume subnets are local' CONFIG_INET_SNARL y +bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n +fi +bool 'The IPX protocol' CONFIG_IPX n +#bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n +fi + +comment 'SCSI support' + +bool 'SCSI support?' CONFIG_SCSI n + +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 n +bool 'SCSI generic support' CONFIG_CHR_DEV_SG n + +comment 'SCSI low-level drivers' + +bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n +bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y +bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n +bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X n +bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC 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 +if [ "$CONFIG_PCI" = "y" ]; then + bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +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 +fi + + +if [ "$CONFIG_NET" = "y" ]; then + +comment 'Network device support' + +bool 'Network device support?' CONFIG_NETDEVICES y +if [ "$CONFIG_NETDEVICES" = "n" ]; then + +comment 'Skipping network driver configuration options...' + +else +bool 'Dummy net driver support' CONFIG_DUMMY n +bool 'SLIP (serial line) support' CONFIG_SLIP n +if [ "$CONFIG_SLIP" = "y" ]; then + bool ' CSLIP compressed headers' SL_COMPRESSED y + bool ' 16 channels instead of 4' SL_SLIP_LOTS n +# bool ' SLIP debugging on' SL_DUMP y +fi +bool 'PPP (point-to-point) support' CONFIG_PPP n +bool 'PLIP (parallel port) support' CONFIG_PLIP n +bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING 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 '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 'Arcnet support' CONFIG_ARCNET n + 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 + 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 + 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 '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 +fi +fi + +comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' + +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 + fi +fi +bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n + +comment 'Filesystems' + +bool 'Standard (minix) fs support' CONFIG_MINIX_FS y +bool 'Extended fs support' CONFIG_EXT_FS y +bool 'Second extended fs support' CONFIG_EXT2_FS y +bool 'xiafs filesystem support' CONFIG_XIA_FS y +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 +fi +bool '/proc filesystem support' CONFIG_PROC_FS n +if [ "$CONFIG_INET" = "y" ]; then +bool 'NFS filesystem support' CONFIG_NFS_FS n +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" ]; 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 y + + +comment 'character devices' + +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 +fi +bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n +bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n + + +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 + +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 + +comment 'Sound' + +bool 'Sound card support' CONFIG_SOUND n + +comment 'Kernel hacking' + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n +bool 'Kernel profiling support' CONFIG_PROFILE n +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 diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile new file mode 100644 index 000000000..b5151d77e --- /dev/null +++ b/arch/sparc/kernel/Makefile @@ -0,0 +1,47 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# 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__ -ansi $< -o $*.s +.S.o: + $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + +OBJS = entry.o traps.o irq.o process.o promops.o signal.o ioport.o setup.o \ + idprom.o probe.o + +all: kernel.o head.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/asm-sparc/head.h + $(CPP) -D__ASSEMBLY__ -ansi -o $*.s $< + +kernel.o: $(OBJS) + $(LD) -r -o kernel.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/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S new file mode 100644 index 000000000..21548015e --- /dev/null +++ b/arch/sparc/kernel/entry.S @@ -0,0 +1,927 @@ +/* arch/sparc/kernel/entry.S: Sparc trap low-level entry points. + * + * Sparc traps are so ugly, this code is going to go through a lot + * of changes as I find out more interesting things. See head.S for + * the trap table and how it works, this will show you how we get + * to these routines. + * + * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <asm/head.h> +#include <asm/asi.h> +#include <asm/psr.h> +#include <asm/cprefix.h> +#include <asm/vaddrs.h> + +/* Here are macros for routines we do often, this allows me to inline this + * without making the code look real ugly. Well, the macro looks ugly too but + * makes the trap entry code easier to understand. + */ + +/* I really don't like synthetic instructions. So I avoid them like the + * plague. + */ + +/* Note that when I have to write a window out, and it is a user's window, I + * have to check that the pages of memory that I am going to throw the window(s) + * onto are valid and are writable by the user (this is %sp to %sp + 64) before + * I start dumping stuff there. We always assume that kernels stack is ok. + * + * If we have to save a kernel window, only one branch is taken. This should + * make trap handlers quicker in this scenario. + * + * Once 'current' is loaded into %g6, it stays there until we leave + * this macro. + * + * XXX must do some checking on the assumption that kernel stack is always ok + */ + +/* I will document how this works real soon. TODO */ + +#define TRAP_WIN_CLEAN \ + or %g0, %g5, %l5; /* we need the globals to do our work */ \ + or %g0, %g6, %l6; /* and %l0 to %l4 are loaded with important */ \ + or %g0, %g7, %l7; /* information like the psr and pc's to return to */ \ + sethi %hi( C_LABEL(current) ), %g6; \ + ld [%g6 + %lo( C_LABEL(current) )], %g6; \ + ld [%g6 + THREAD_UWINDOWS], %g7; /* how many user wins are active? */ \ + subcc %g7, 0x0, %g0; \ + bne 2f; /* If there are any, branch. */ \ + save %g0, %g0, %g0; /* Save into that window either way. */ \ + std %l0, [%sp]; /* If above shows only kernel windows */ \ +1: std %l2, [%sp + 0x8]; /* then we get here. */ \ + std %l4, [%sp + 0x10]; \ + std %l6, [%sp + 0x18]; \ + std %i0, [%sp + 0x20]; \ + std %i2, [%sp + 0x28]; \ + std %i4, [%sp + 0x30]; \ + std %i6, [%sp + 0x38]; \ + or %g0, 0x1, %g5; \ + rd %psr, %g7; \ + sll %g5, %g7, %g5; \ + wr %g5, 0x0, %wim; /* update %wim to 'now' invalid */ \ + and %g7, 0x1f, %g7; \ + st %g7, [%g6 + THREAD_WIM]; /* save 'this' threads mask */ \ + restore %g0, %g0, %g0; \ + or %g0, %l5, %g5; /* restore the globals we used */ \ + or %g0, %l6, %g6; \ + b 8f; /* we are done */ \ + or %g0, %l7, %g7; \ +2: sub %g7, 0x1, %g7; \ + st %g7, [%g6 + THREAD_UWINDOWS]; /* There are user windows if we */ \ + andcc %sp, 0x7, %g0; /* get here. Check for stack alignment. */ \ + bne 5f; /* Stack is unaligned, yuck. */ \ + sra %sp, 0x1e, %g7; /* This stuff checks to see if top 3-bits */ \ + subcc %g7, 0x0, %g0; /* of stack pointer address are ok. */ \ + be,a 3f; \ + andn %sp, 0xfff, %g7; \ + subcc %g7, -1, %g0; \ + bne 5f; /* bad stack pointer, ugh */ \ + andn %sp, 0xfff, %g7; \ +3: lda [%g7] ASI_PTE, %g7; /* Ok, user stack is a valid address */ \ + srl %g7, 0x1d, %g7; \ + subcc %g7, 0x6, %g0; /* Can the user write to it? */ \ + bne 5f; \ + and %sp, 0xfff, %g7; \ + subcc %g7, 0xfc1, %g0; /* Is our save area on one page? */ \ + bl,a 1b; \ + std %l0, [%sp]; \ + add %sp, 0x38, %g5; /* Nope, have to check both pages */ \ + sra %g5, 0x1e, %g7; \ + subcc %g7, 0x0, %g0; \ + be,a 4f; \ + andn %g5, 0xfff, %g7; \ + subcc %g7, -1, %g0; \ + bne 5f; \ + andn %g5, 0xfff, %g7; \ +4: lda [%g7] ASI_PTE, %g7; /* Stack space in 2nd page is valid */ \ + srl %g7, 0x1d, %g7; \ + subcc %g7, 0x6, %g0; /* Can user write here too? */ \ + be,a 1b; \ + std %l0, [%sp]; \ +5: ld [%g6 + THREAD_UWINDOWS], %g7; /* This is due to either bad page perms */ \ + add %g6, THREAD_REG_WINDOW, %g5; /* for the users stack area, or the stack */ \ +6: std %l0, [%g5]; /* pointer is misaligned. See above. */ \ + std %l2, [%g5 + 0x8]; \ + std %l4, [%g5 + 0x10]; \ + std %l6, [%g5 + 0x18]; \ + std %i0, [%g5 + 0x20]; \ + std %i2, [%g5 + 0x28]; \ + std %i4, [%g5 + 0x30]; \ + std %i6, [%g5 + 0x38]; \ + subcc %g7, 0x1, %g7; \ + bge,a 6b; /* while(uwindows>=0) { write_win(); */ \ + save %g5, 0x40, %g5; /* uwindows--; } */ \ + st %sp, [%g6 + THREAD_USP]; \ + or %g0, 0x1, %g5; \ + rd %psr, %g7; \ + sll %g5, %g7, %g5; \ + wr %g5, 0x0, %wim; \ + and %g7, 0x1f, %g7; \ + st %g7, [%g6 + THREAD_WIM]; /* Update thread_struct fields */ \ + ld [%g6 + THREAD_UWINDOWS], %g7; \ + add %g7, 0x1, %g5; \ + st %g5, [%g6 + THREAD_W_SAVED]; \ + st %g0, [%g6 + THREAD_UWINDOWS]; \ +7: subcc %g7, 0x1, %g7; /* Restore back to where we started. */ \ + bge 7b; \ + restore %g0, %g0, %g0; \ + or %g0, %l5, %g5; /* Restore the globals. */ \ + or %g0, %l6, %g6; \ + or %g0, %l7, %g7; \ +8: nop; /* We are done when we get here. */ \ + +/* As if the last macro wasn't enough, we have to go through a very similar routine + * upon entry to most traps and interrupts. This is save away the current window + * if it is the trap window, clean it, and adjust the stack for the handler c-code + * to work. + */ + +#define ENTER_TRAP \ + rd %wim, %l4; \ + or %g0, 0x1, %l5; \ + sll %l5, %l0, %l5; \ + andcc %l0, 0x40, %g0; \ + bz 1f; \ + andcc %l4, %l5, %g0; \ + bz,a 3f; \ + sub %fp, 0xb0, %sp; \ + TRAP_WIN_CLEAN \ + b 3f; \ + sub %fp, 0xb0, %sp; \ +1: sethi %hi( C_LABEL(current) ), %l6; \ + ld [%l6 + %lo( C_LABEL(current) )], %l6; \ + ld [%l6 + THREAD_WIM], %l5; \ + and %l0, 0x1f, %l4; \ + cmp %l5, %l3; \ + ble,a 4f; \ + sethi %hi( C_LABEL(nwindowsm1) ), %l4; \ + sub %l5, %l3, %l3; \ + b 5f; \ + sub %l3, 0x1, %l5; \ +4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4; \ + sub %l4, %l3, %l4; \ + add %l5, %l4, %l5; \ +5: st %l5, [%l6 + THREAD_UWINDOWS]; \ + bz,a 2f; \ + sethi %hi(TASK_SIZE-176), %l5; \ + TRAP_WIN_CLEAN; \ + sethi %hi( C_LABEL(current) ), %l6; \ + ld [%l6 + %lo( C_LABEL(current) )], %l6; \ + sethi %hi(TASK_SIZE-176), %l5; \ +2: or %l5, %lo(TASK_SIZE-176), %l5; \ + add %l6, %l5, %sp; \ +3: \ + +#define ENTER_IRQ \ + rd %wim, %l4; \ + or %g0, 0x1, %l5; \ + sll %l5, %l0, %l5; \ + andcc %l0, 0x40, %g0; \ + bz 1f; \ + andcc %l4, %l5, %g0; \ + sethi %hi( C_LABEL(eintstack) ), %l7; \ + or %l7, %lo( C_LABEL(eintstack) ), %l7; \ + bz 0f; \ + nop; \ + TRAP_WIN_CLEAN \ + sethi %hi( C_LABEL(eintstack) ), %l7; \ + or %l7, %lo( C_LABEL(eintstack) ), %l7; \ +0: subcc %fp, %l7, %g0; \ + bg,a 3f; \ + sub %l7, 0xb0, %sp; \ + b 3f; \ + sub %fp, 0xb0, %sp; \ +1: sethi %hi( C_LABEL(current) ), %l6; \ + ld [%l6 + %lo( C_LABEL(current) )], %l6; \ + ld [%l6 + THREAD_WIM], %l5; \ + and %l0, 0x1f, %l7; \ + cmp %l5, %l7; \ + ble,a 4f; \ + sethi %hi( C_LABEL(nwindowsm1) ), %l4; \ + sub %l5, %l7, %l7; \ + b 5f; \ + sub %l7, 0x1, %l5; \ +4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4; \ + sub %l4, %l7, %l4; \ + add %l5, %l4, %l5; \ +5: st %l5, [%l6 + THREAD_UWINDOWS]; \ + bz,a 2f; \ + sethi %hi( C_LABEL(eintstack) ), %l7; \ + TRAP_WIN_CLEAN \ + sethi %hi( C_LABEL(eintstack) ), %l7; \ +2: \ + sub %l7, 0xb0, %sp; \ +3: + + + .text + .align 4 + +/* Default trap handler */ + .globl my_trap_handler +my_trap_handler: +#if 1 + jmp %l1 + rett %l2 + nop +#else + rd %wim, %l4 + or %g0, 0x1, %l5 + sll %l5, %l0, %l5 + cmp %l4, %l5 ! are we in the invalid window? + + TRAP_WIN_CLEAN + + nop + or %g0, %l3, %o0 + call C_LABEL(do_hw_interrupt) + or %g0, %g0, %o1 + wr %l0, 0x20, %psr ! re-enable traps and reset the condition codes + nop + nop + nop ! click our heels three times, "no place like home" + jmp %l1 + rett %l2 +#endif /* bogon */ + + .align 4 + .globl sparc_timer +sparc_timer: + sethi %hi(TIMER_VADDR), %l4 + or %l4, %lo(TIMER_VADDR), %l4 ! read the limit register + ld [%l4 + 0xc], %l4 ! to clear the interrupt + rd %wim, %l4 + or %g0, 0x1, %l5 + sll %l5, %l0, %l5 + andcc %l0, 0x40, %g0 + bz st1 + sethi %hi( C_LABEL(eintstack) ), %l7 + andcc %l4, %l5, %g0 + bz st0 + or %l7, %lo( C_LABEL(eintstack) ), %l7 + TRAP_WIN_CLEAN + sethi %hi( C_LABEL(eintstack) ), %l7 + or %l7, %lo( C_LABEL(eintstack) ), %l7 +st0: subcc %fp, %l7, %g0 + bg,a st3 + sub %l7, 0xb0, %sp + b st3 + sub %fp, 0xb0, %sp +st1: sethi %hi( C_LABEL(current) ), %l6 + ld [%l6 + %lo( C_LABEL(current) )], %l6 + ld [%l6 + THREAD_WIM], %l5 + and %l0, 0x1f, %l7 + cmp %l5, %l7 + ble,a st4 + sethi %hi( C_LABEL(nwindowsm1) ), %l4 + sub %l5, %l7, %l7 + b st5 + sub %l7, 0x1, %l5 +st4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4 + sub %l4, %l7, %l4 + add %l5, %l4, %l5 +st5: st %l5, [%l6 + THREAD_UWINDOWS] + sethi %hi( C_LABEL(eintstack) ), %l7 + bz,a st2 + or %l7, %lo( C_LABEL(eintstack) ), %l7 + TRAP_WIN_CLEAN + sethi %hi( C_LABEL(eintstack) ), %l7 + or %l7, %lo( C_LABEL(eintstack) ), %l7 +st2: sub %l7, 0xb0, %sp + +st3: std %g2, [%sp + 96 + 24] + or %g0, %g1, %l7 + rd %y, %l6 + std %g4, [%sp + 96 + 32] + andn %l0, PSR_PIL, %l4 + sll %l3, 0x8, %l5 + std %g6, [%sp + 96 + 40] + or %l5, %l4, %l4 + + wr %l4, 0x0, %psr + wr %l4, PSR_ET, %psr + + std %l0, [%sp + 96 + 0] + std %l2, [%sp + 96 + 8] + st %fp, [%sp + 96 + 16] + + or %g0, 14, %o0 + or %g0, %g0, %o1 + call C_LABEL(do_sparc_timer) + nop + + or %g0, %l7, %g1 + wr %l6, 0x0, %y + ldd [%sp + 96 + 24], %g2 + ldd [%sp + 96 + 32], %g4 + ldd [%sp + 96 + 40], %g6 + wr %l0, 0x0, %psr + nop + nop + nop + + and %l0, 31, %l5 + sethi %hi(lnx_winmask), %l6 + or %l6, %lo(lnx_winmask), %l6 + ldub [%l6 + %l5], %l5 + andcc %l0, PSR_PS, %g0 + bnz 1f + rd %wim, %l4 + +1: andcc %l5, %l4, %g0 + bnz 2f + wr %l0, 0x0, %psr + nop + nop + nop + + jmp %l1 + rett %l2 + +2: wr %g0, 0x0, %wim + nop + nop + nop + + restore + restore %g0, 0x1, %l1 + rd %psr, %l0 + and %l0, 31, %l0 + sll %l1, %l0, %l1 + wr %l1, 0x0, %wim + sethi %hi( C_LABEL(current) ), %l1 + ld [%l1 + %lo( C_LABEL(current) ) ], %l1 + st %l0, [%l1 + THREAD_WIM] + save %g0, %g0, %g0 + + ldd [%sp], %l0 + ldd [%sp + 0x8], %l2 + ldd [%sp + 0x10], %l4 + ldd [%sp + 0x18], %l6 + ldd [%sp + 0x20], %i0 + ldd [%sp + 0x28], %i2 + ldd [%sp + 0x30], %i4 + ldd [%sp + 0x38], %i6 + + save %g0, %g0, %g0 + + jmp %l1 + rett %l2 + + +/* For now all IRQ's not registered get sent here so I can see + * what is poking the chip. + */ + + .align 4 + .globl stray_irq_entry +stray_irq_entry: + rd %wim, %l4 + or %g0, 0x1, %l5 + sll %l5, %l0, %l5 + andcc %l0, 0x40, %g0 + bz tt1 + sethi %hi( C_LABEL(eintstack) ), %l7 + andcc %l4, %l5, %g0 + bz tt0 + or %l7, %lo( C_LABEL(eintstack) ), %l7 + TRAP_WIN_CLEAN + sethi %hi( C_LABEL(eintstack) ), %l7 + or %l7, %lo( C_LABEL(eintstack) ), %l7 +tt0: subcc %fp, %l7, %g0 + bg,a tt3 + sub %l7, 0xb0, %sp + b tt3 + sub %fp, 0xb0, %sp +tt1: sethi %hi( C_LABEL(current) ), %l6 + ld [%l6 + %lo( C_LABEL(current) )], %l6 + ld [%l6 + THREAD_WIM], %l5 + and %l0, 0x1f, %l7 + cmp %l5, %l7 + ble,a tt4 + sethi %hi( C_LABEL(nwindowsm1) ), %l4 + sub %l5, %l7, %l7 + b tt5 + sub %l7, 0x1, %l5 +tt4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4 + sub %l4, %l7, %l4 + add %l5, %l4, %l5 +tt5: st %l5, [%l6 + THREAD_UWINDOWS] + sethi %hi( C_LABEL(eintstack) ), %l7 + bz,a tt2 + or %l7, %lo( C_LABEL(eintstack) ), %l7 + TRAP_WIN_CLEAN + sethi %hi( C_LABEL(eintstack) ), %l7 + or %l7, %lo( C_LABEL(eintstack) ), %l7 +tt2: sub %l7, 0xb0, %sp + +tt3: std %g2, [%sp + 96 + 24] + or %g0, %g1, %l7 + rd %y, %l6 + std %g4, [%sp + 96 + 32] + andn %l0, PSR_PIL, %l4 + sll %l3, 0x8, %l5 + std %g6, [%sp + 96 + 40] + or %l5, %l4, %l4 + + wr %l4, 0x0, %psr + wr %l4, PSR_ET, %psr + + std %l0, [%sp + 96 + 0] + std %l2, [%sp + 96 + 8] + st %fp, [%sp + 96 + 16] + + or %g0, %l3, %o0 + or %g0, %g0, %o1 + call C_LABEL(unexpected_irq) + nop + + or %g0, %l7, %g1 + wr %l6, 0x0, %y + ldd [%sp + 96 + 24], %g2 + ldd [%sp + 96 + 32], %g4 + ldd [%sp + 96 + 40], %g6 + wr %l0, 0x0, %psr + nop + nop + nop + + and %l0, 31, %l5 + sethi %hi(lnx_winmask), %l6 + or %l6, %lo(lnx_winmask), %l6 + ldub [%l6 + %l5], %l5 + andcc %l0, PSR_PS, %g0 + bnz 1f + rd %wim, %l4 + +1: andcc %l5, %l4, %g0 + bnz 2f + wr %l0, 0x0, %psr + nop + nop + nop + + jmp %l1 + rett %l2 + +2: wr %g0, 0x0, %wim + nop + nop + nop + + restore + restore %g0, 0x1, %l1 + rd %psr, %l0 + and %l0, 31, %l0 + sll %l1, %l0, %l1 + wr %l1, 0x0, %wim + sethi %hi( C_LABEL(current) ), %l1 + ld [%l1 + %lo( C_LABEL(current) ) ], %l1 + st %l0, [%l1 + THREAD_WIM] + save %g0, %g0, %g0 + + ldd [%sp], %l0 + ldd [%sp + 0x8], %l2 + ldd [%sp + 0x10], %l4 + ldd [%sp + 0x18], %l6 + ldd [%sp + 0x20], %i0 + ldd [%sp + 0x28], %i2 + ldd [%sp + 0x30], %i4 + ldd [%sp + 0x38], %i6 + + save %g0, %g0, %g0 + + jmp %l1 + rett %l2 + + + +/* This routine is optimized for kernel window fills. User fills take about two + * or three extra jumps on the average. We'll see how this works out. + */ + +/* Don't use local labels, or if you do be REAL CAREFUL. TRAP_WIN_CLEAN is + * full of them! If you think this routine is hairy, window spills are worse, + * see below. + */ + + .align 4 + .globl spill_window_entry +spill_window_entry: + andcc %l0, 0x40, %g0 ! see if this is a user window fill + bz,a spill_from_user + nop + + TRAP_WIN_CLEAN /* danger, danger... */ + wr %l0, 0x0, %psr + nop + jmp %l1 + rett %l2 + +spill_from_user: + sethi %hi( C_LABEL(current) ), %l6 + ld [%l6 + %lo( C_LABEL(current) )], %l6 + ld [%l6 + THREAD_WIM], %l5 + and %l0, 0x1f, %l3 + +/* I don't know what's worse, the extra comparison here, or an extra load + * from a lookup table, we'll see. + */ + cmp %l5, %l3 + ble,a 1f + sethi %hi( C_LABEL(nwindowsm1) ), %l4 + sub %l5, %l3, %l3 + b 2f + sub %l3, 0x1, %l5 +1: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4 + sub %l4, %l3, %l4 + add %l5, %l4, %l5 +2: st %l5, [%l6 + THREAD_UWINDOWS] + + TRAP_WIN_CLEAN /* danger, danger... */ + sethi %hi( C_LABEL(current) ), %l6 + ld [%l6 + %lo( C_LABEL(current) )], %l6 + ld [%l6 + THREAD_KSP], %sp + and %l0, 0x1f, %l3 + sethi %hi(lnx_winmask), %l6 + or %l6, %lo(lnx_winmask), %l6 + ldub [%l6 + %l3], %l5 + rd %wim, %l4 + jmp %l1 + rett %l2 + +/* A window spill has occurred. This presents a weird situation, a restore + * was attempted and a trap occurred. Therefore the restore attempt had no + * effect on window movement and the trap saved, which means it went in the + * other direction. :-( We are in a trap window which is two restores away + * from the window we want to un-invalidate so to speak and three away from + * the one which will become invalid after this routine. There are probably + * bugs already this routine. Bugs suck. + */ + +/* This is a very complicated and hairy routine, don't expect to understand + * it the first time. :> + */ + + .align 4 + .globl fill_window_entry +fill_window_entry: + wr %g0, 0, %wim ! Can not enter invalid register without this. + andcc %l0, 0x40, %g0 ! From user? + restore ! restore to where trap occurred + bz fill_from_user + restore ! enter invalid register, whee... + restore %g0, 0x1, %l1 ! enter one-past invalid register + rd %psr, %l0 ! this is the window we need to save + and %l0, 0x1f, %l0 + sll %l1, %l0, %l1 + wr %l1, 0x0, %wim + sethi %hi( C_LABEL(current) ), %l1 + ld [%l1 + %lo( C_LABEL(current) )], %l1 + st %l0, [%l1 + THREAD_WIM] + save %g0, %g0, %g0 ! back to invalid register + ldd [%sp], %l0 ! load the window from stack + ldd [%sp + 8], %l2 + ldd [%sp + 16], %l4 + ldd [%sp + 24], %l6 + ldd [%sp + 32], %i0 + ldd [%sp + 40], %i2 + ldd [%sp + 48], %i4 + ldd [%sp + 56], %i6 + save %g0, %g0, %g0 ! to window where trap happened + save %g0, %g0, %g0 ! back to trap window, so rett works + wr %l0, 0x0, %psr ! load condition codes + nop + jmp %l1 + rett %l2 ! are you as confused as I am? + +fill_from_user: + andcc %sp, 0x7, %g0 ! check for alignment of user stack + bne fill_bad_stack + sra %sp, 0x1e, %l7 + cmp %l7, 0x0 + be,a 1f + andn %sp, 0xfff, %l7 + cmp %l7, -1 + bne fill_bad_stack + andn %sp, 0xfff, %l7 +1: lda [%l7] ASI_PTE, %l7 + srl %l7, 0x1d, %l7 + andn %l7, 0x2, %l7 + cmp %l7, 0x4 + bne fill_bad_stack + and %sp, 0xfff, %l7 + cmp %l7, 0xfc1 + bl,a fill_stack_ok + restore %g0, 1, %l1 + add %sp, 0x38, %l5 + sra %sp, 0x1e, %l7 + cmp %l7, 0x0 + be,a 1f + andn %sp, 0xfff, %l7 + cmp %l7, -1 + bne fill_bad_stack + andn %sp, 0xfff, %l7 +1: lda [%l7] ASI_PTE, %l7 + srl %l7, 0x1d, %l7 + andn %l7, 0x2, %l7 + cmp %l7, 0x4 + be,a fill_stack_ok + restore %g0, 0x1, %l1 + +fill_bad_stack: + save %g0, %g0, %g0 ! save to where restore happened + save %g0, 0x1, %l4 ! save is an add remember? to trap window + sethi %hi( C_LABEL(current) ), %l6 + ld [%l6 + %lo( C_LABEL(current) )], %l6 + st %l4, [%l6 + THREAD_UWINDOWS] ! update current->tss values + ld [%l6 + THREAD_WIM], %l5 + sll %l4, %l5, %l4 + wr %l4, 0x0, %wim + ld [%l6 + THREAD_KSP], %sp ! set to kernel stack pointer + wr %l0, 0x20, %psr ! turn off traps + std %l0, [%sp + C_STACK] ! set up thread_frame on stack + rd %y, %l3 + std %l2, [%sp + C_STACK + 0x8] + or %g0, 0x6, %o0 ! so _sparc_trap knows what to do + st %g1, [%sp + C_STACK + 0x14] ! no need to save %g0, always zero + or %g0, %l0, %o1 + std %g2, [%sp + C_STACK + 0x18] + or %g0, %l1, %o2 + std %g4, [%sp + C_STACK + 0x20] + add %sp, C_STACK, %o3 + std %g6, [%sp + C_STACK + 0x28] + std %i0, [%sp + C_STACK + 0x30] + std %i2, [%sp + C_STACK + 0x38] + std %i4, [%sp + C_STACK + 0x40] + call sparc_trap + std %i6, [%sp + C_STACK + 0x48] + + ldd [%sp + C_STACK], %l0 + ldd [%sp + C_STACK + 0x8], %l2 + wr %l3, 0, %y + ld [%sp + C_STACK + 0x14], %g1 + ldd [%sp + C_STACK + 0x18], %g2 + ldd [%sp + C_STACK + 0x20], %g4 + ldd [%sp + C_STACK + 0x28], %g6 + ldd [%sp + C_STACK + 0x30], %i0 + ldd [%sp + C_STACK + 0x38], %i2 + ldd [%sp + C_STACK + 0x40], %i4 + wr %l0, 0, %psr ! disable traps again + ldd [%sp + C_STACK + 0x48], %i6 + sethi %hi( C_LABEL(current) ), %l6 + ld [%l6 + %lo( C_LABEL(current) )], %l6 + ld [%l6 + THREAD_W_SAVED], %l7 + cmp %l7, 0x0 + bl,a 1f + wr %g0, 0x0, %wim + b,a leave_trap + +1: or %g0, %g6, %l3 + or %g0, %l6, %g6 + st %g0, [%g6 + THREAD_W_SAVED] + restore %g0, %g0, %g0 + restore %g0, %g0, %g0 + restore %g0, 0x1, %l1 + rd %psr, %l0 + sll %l1, %l0, %l1 + wr %l1, 0x0, %wim + and %l0, 0x1f, %l0 + st %l0, [%g6 + THREAD_WIM] + nop + save %g0, %g0, %g0 + ldd [%sp], %l0 ! load number one + ldd [%sp + 0x8], %l2 + ldd [%sp + 0x10], %l4 + ldd [%sp + 0x18], %l6 + ldd [%sp + 0x20], %i0 + ldd [%sp + 0x28], %i2 + ldd [%sp + 0x30], %i4 + ldd [%sp + 0x38], %i6 + save %g0, %g0, %g0 + ldd [%sp], %l0 ! load number two + ldd [%sp + 0x8], %l2 + ldd [%sp + 0x10], %l4 + ldd [%sp + 0x18], %l6 + ldd [%sp + 0x20], %i0 + ldd [%sp + 0x28], %i2 + ldd [%sp + 0x30], %i4 + ldd [%sp + 0x38], %i6 + save %g0, %g0, %g0 ! re-enter trap window + wr %l0, 0x0, %psr ! restore condition codes + or %g0, %l3, %g6 ! restore scratch register + jmp %l1 + rett %l2 + +fill_stack_ok: + rd %psr, %l0 + sll %l1, %l0, %l1 + wr %l1, 0x0, %wim + sethi %hi( C_LABEL(current) ), %l2 + ld [%l2 + %lo( C_LABEL(current) )], %l2 + and %l0, 0x1f, %l0 + st %l0, [%l2 + THREAD_WIM] + save %g0, %g0, %g0 + ldd [%sp], %l0 ! only one load necessary + ldd [%sp + 0x8], %l2 + ldd [%sp + 0x10], %l4 + ldd [%sp + 0x18], %l6 + ldd [%sp + 0x20], %i0 + ldd [%sp + 0x28], %i2 + ldd [%sp + 0x30], %i4 + ldd [%sp + 0x38], %i6 + save %g0, %g0, %g0 + save %g0, %g0, %g0 ! save into trap window + wr %l0, 0x0, %psr ! local number 0 here has cond codes + nop + jmp %l1 + rett %l2 + + .align 4 + .globl trap_entry +trap_entry: + TRAP_WIN_CLEAN + jmp %l1 + rett %l2 + + .align 4 + .globl linux_trap_nmi +linux_trap_nmi: + TRAP_WIN_CLEAN + jmp %l1 + rett %l2 + + .align 4 + .globl sparc_trap +sparc_trap: + TRAP_WIN_CLEAN + jmp %l1 + rett %l2 + + .align 4 + .globl leave_trap +leave_trap: + jmp %l1 + rett %l2 + +/* The following two things point to window management tables. The first + one is used to quickly look up how many user windows there are from + trap-land. The second is used in a trap handler to determine if a rett + instruction will land us smack inside the invalid window that possibly + the trap was called to fix-up. +*/ + +/* For now these are static tables geared for a 7 window sparc. */ + + .data + .align 4 +lnx_winmask: .byte 2, 4, 8, 16, 32, 64, 128, 1 ! lnx_winmask[0..7] + + + .align 4 + .globl C_LABEL(sys_call_table) +C_LABEL(sys_call_table): + .long C_LABEL(sys_setup) /* 0 */ + .long C_LABEL(sys_exit) + .long C_LABEL(sys_fork) + .long C_LABEL(sys_read) + .long C_LABEL(sys_write) + .long C_LABEL(sys_open) /* 5 */ + .long C_LABEL(sys_close) + .long C_LABEL(sys_waitpid) + .long C_LABEL(sys_creat) + .long C_LABEL(sys_link) + .long C_LABEL(sys_unlink) /* 10 */ + .long C_LABEL(sys_execve) + .long C_LABEL(sys_chdir) + .long C_LABEL(sys_time) + .long C_LABEL(sys_mknod) + .long C_LABEL(sys_chmod) /* 15 */ + .long C_LABEL(sys_chown) + .long C_LABEL(sys_break) + .long C_LABEL(sys_stat) + .long C_LABEL(sys_lseek) + .long C_LABEL(sys_getpid) /* 20 */ + .long C_LABEL(sys_mount) + .long C_LABEL(sys_umount) + .long C_LABEL(sys_setuid) + .long C_LABEL(sys_getuid) + .long C_LABEL(sys_stime) /* 25 */ + .long C_LABEL(sys_ni_syscall) /* this will be sys_ptrace() */ + .long C_LABEL(sys_alarm) + .long C_LABEL(sys_fstat) + .long C_LABEL(sys_pause) + .long C_LABEL(sys_utime) /* 30 */ + .long C_LABEL(sys_stty) + .long C_LABEL(sys_gtty) + .long C_LABEL(sys_access) + .long C_LABEL(sys_nice) + .long C_LABEL(sys_ftime) /* 35 */ + .long C_LABEL(sys_sync) + .long C_LABEL(sys_kill) + .long C_LABEL(sys_rename) + .long C_LABEL(sys_mkdir) + .long C_LABEL(sys_rmdir) /* 40 */ + .long C_LABEL(sys_dup) + .long C_LABEL(sys_pipe) + .long C_LABEL(sys_times) + .long C_LABEL(sys_prof) + .long C_LABEL(sys_brk) /* 45 */ + .long C_LABEL(sys_setgid) + .long C_LABEL(sys_getgid) + .long C_LABEL(sys_signal) + .long C_LABEL(sys_geteuid) + .long C_LABEL(sys_getegid) /* 50 */ + .long C_LABEL(sys_acct) + .long C_LABEL(sys_phys) + .long C_LABEL(sys_lock) + .long C_LABEL(sys_ioctl) + .long C_LABEL(sys_fcntl) /* 55 */ + .long C_LABEL(sys_mpx) + .long C_LABEL(sys_setpgid) + .long C_LABEL(sys_ulimit) + .long C_LABEL(sys_olduname) + .long C_LABEL(sys_umask) /* 60 */ + .long C_LABEL(sys_chroot) + .long C_LABEL(sys_ustat) + .long C_LABEL(sys_dup2) + .long C_LABEL(sys_getppid) + .long C_LABEL(sys_getpgrp) /* 65 */ + .long C_LABEL(sys_setsid) + .long C_LABEL(sys_sigaction) + .long C_LABEL(sys_sgetmask) + .long C_LABEL(sys_ssetmask) + .long C_LABEL(sys_setreuid) /* 70 */ + .long C_LABEL(sys_setregid) + .long C_LABEL(sys_sigsuspend) + .long C_LABEL(sys_sigpending) + .long C_LABEL(sys_sethostname) + .long C_LABEL(sys_setrlimit) /* 75 */ + .long C_LABEL(sys_getrlimit) + .long C_LABEL(sys_getrusage) + .long C_LABEL(sys_gettimeofday) + .long C_LABEL(sys_settimeofday) + .long C_LABEL(sys_getgroups) /* 80 */ + .long C_LABEL(sys_setgroups) + .long C_LABEL(sys_select) + .long C_LABEL(sys_symlink) + .long C_LABEL(sys_lstat) + .long C_LABEL(sys_readlink) /* 85 */ + .long C_LABEL(sys_uselib) + .long C_LABEL(sys_swapon) + .long C_LABEL(sys_reboot) + .long C_LABEL(sys_readdir) + .long C_LABEL(sys_mmap) /* 90 */ + .long C_LABEL(sys_munmap) + .long C_LABEL(sys_truncate) + .long C_LABEL(sys_ftruncate) + .long C_LABEL(sys_fchmod) + .long C_LABEL(sys_fchown) /* 95 */ + .long C_LABEL(sys_getpriority) + .long C_LABEL(sys_setpriority) + .long C_LABEL(sys_profil) + .long C_LABEL(sys_statfs) + .long C_LABEL(sys_fstatfs) /* 100 */ + .long C_LABEL(sys_ni_syscall) + .long C_LABEL(sys_socketcall) + .long C_LABEL(sys_syslog) + .long C_LABEL(sys_setitimer) + .long C_LABEL(sys_getitimer) /* 105 */ + .long C_LABEL(sys_newstat) + .long C_LABEL(sys_newlstat) + .long C_LABEL(sys_newfstat) + .long C_LABEL(sys_uname) + .long C_LABEL(sys_ni_syscall) /* 110 */ + .long C_LABEL(sys_vhangup) + .long C_LABEL(sys_idle) + .long C_LABEL(sys_ni_syscall) /* was vm86, meaningless on Sparc */ + .long C_LABEL(sys_wait4) + .long C_LABEL(sys_swapoff) /* 115 */ + .long C_LABEL(sys_sysinfo) + .long C_LABEL(sys_ipc) + .long C_LABEL(sys_fsync) + .long C_LABEL(sys_sigreturn) + .long C_LABEL(sys_ni_syscall) /* 120 */ + .long C_LABEL(sys_setdomainname) + .long C_LABEL(sys_newuname) + .long C_LABEL(sys_ni_syscall) + .long C_LABEL(sys_adjtimex) + .long C_LABEL(sys_mprotect) /* 125 */ + .long C_LABEL(sys_sigprocmask) + .long C_LABEL(sys_create_module) + .long C_LABEL(sys_init_module) + .long C_LABEL(sys_delete_module) + .long C_LABEL(sys_get_kernel_syms) /* 130 */ + .long C_LABEL(sys_ni_syscall) + .long C_LABEL(sys_getpgid) + .long C_LABEL(sys_fchdir) + .long C_LABEL(sys_bdflush) + .long C_LABEL(sys_sysfs) /* 135 */ + .long C_LABEL(sys_personality) + .long 0 /* for afs_syscall */ + .long C_LABEL(sys_setfsuid) + .long C_LABEL(sys_setfsgid) + .long C_LABEL(sys_llseek) /* 140 */ + .align 4 diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S new file mode 100644 index 000000000..c3a5453e7 --- /dev/null +++ b/arch/sparc/kernel/head.S @@ -0,0 +1,1045 @@ +/* boot.S: The initial boot code for the Sparc port of Linux. + + Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) + + This file has to serve three purposes. + + 1) determine the prom-version and cpu/architecture + 2) print enough useful info before we start to execute + c-code that I can possibly begin to debug things + 3) Hold the vector of trap entry points + + The Sparc offers many challenges to kernel design. Here I will + document those I have come across thus far. Upon bootup the boot + prom loads your a.out image into memory. This memory the prom has + already mapped for you in two places, however as far as I can tell + the virtual address cache is not turned on although the MMU is + translating things. You get loaded at 0x4000 exactly and you are + aliased to 0xf8004000 with the appropriate mmu entries. So, when + you link a boot-loadable object you want to do something like: + + ld -e start -Ttext 4000 -o mykernel myobj1.o myobj2.o .... + + to produce a proper image. + + At boot time you are given (as far as I can tell at this time) + one key to figure out what machine you are one and what devices + are available. The prom when it loads you leaves a pointer to + the 'rom vector' in register %o0 right before it jumps to your + starting address. This is a pointer to a struct that is full of + pointer to functions (ie. printf, halt, reboot), pointers to + linked lists (ie. memory mappings), and pointer to empirical + constants (ie. stdin and stdout magic cookies + rom version). + Starting with this piece of information you can figure out + just about anything you want about the machine you are on. + + Although I don't use it now, if you are on a Multiprocessor and + therefore a v3 or above prom, register %o2 at boot contains a + function pointer you must call before you proceed to invoke the + other cpu's on the machine. I have no idea what kind of magic this + is, give me time. +*/ + +#include <asm/cprefix.h> +#include <asm/head.h> +#include <asm/version.h> +#include <asm/asi.h> +#include <asm/contregs.h> +#include <asm/psr.h> +#include <asm/page.h> + + .data + +/* First thing to go in the data segment is the interrupt stack. */ + + .globl C_LABEL(intstack) + .globl C_LABEL(eintstack) +C_LABEL(intstack): + .skip 4 * PAGE_SIZE ! 16k = 128 128-byte stack frames +C_LABEL(eintstack): + + + +/* + The following are used with the prom_vector node-ops to figure out + the cpu-type +*/ + + .globl C_LABEL(cputyp) + +C_LABEL(cputyp): + .word 1 + +C_LABEL(cputypval): + .asciz "sun4c" + .ascii " " + + .align 4 +/* + * Sun people can't spell worth damn. "compatability" indeed. + * At least we *know* we can't spell, and use a spell-checker. + */ + +/* Uh, actually Linus it is I who cannot spell. Too much murky + * Sparc assembly will do this to ya. + */ +C_LABEL(cputypvar): + .asciz "compatability" + +C_LABEL(cputypvallen) = C_LABEL(cputypvar) - C_LABEL(cputypval) + +/* This hold the prom-interface-version number for either v0 or v2. */ + + .align 4 + .globl C_LABEL(prom_iface_vers) + +C_LABEL(prom_iface_vers): .skip 4 + +/* WARNING: evil messages follow */ + + .align 4 + +sun4_notsup: + .asciz "Sparc-Linux: sun4 support not implemented yet\n\n" + .align 4 + +sun4m_notsup: + .asciz "Sparc-Linux: sun4m support does not exist\n\n" + .align 4 + +sun4d_notsup: + .asciz "Sparc-Linux: sun4d support does not exist\n\n" + .align 4 + +you_lose: + .asciz "You lose..... Thanks for playing...\n" + .align 4 + + + .globl boot_msg + +/* memory descriptor property strings, v2 = yuk yuk yuk */ +/* XXX how to figure out vm mapped by prom? May have to scan magic addresses */ + +mem_prop_physavail: .asciz "available" + + .align 4 +mem_prop_phystot: .asciz "reg" + +/* v2_memory descriptor struct kludged here for assembly, if it ain't broke */ + + .align 4 +v2_mem_struct: .skip 0xff + + .align 4 +v2_printf_physavail: .asciz "Physical Memory Available: 0x%x bytes" + + .align 4 +v2_printf_phystot: .asciz "Physical Memory: 0x%x bytes" + +/* A place to store property strings returned from the prom 'node' funcs */ + + .align 4 +prop_string_buf: .skip 32 + + .align 4 +prop_name: .asciz "name" + + .align 4 +current_node: .skip 4 + + +/* nice little boot message */ + + .align 4 +boot_msg: + .ascii "Booting Sparc-Linux V0.00PRE-ALPHA " + .ascii WHO_COMPILED_ME + .ascii "\r\n" + .align 4 + + .globl boot_msg2 + +boot_msg2: + .asciz "Booting Sparclinux V0.00 PRE-ALPHA on a (SUN4C)\r\n\n" + + .align 4 + +pstring1: + .asciz "Prom Magic Cookie: 0x%x \n" + .align 4 + +pstring2: + .asciz "Interface Version: v%d\n" + .align 4 + +pstring3: + .asciz "Prom Revision: V%d\n\n" + .align 4 + +pstring4: + .ascii "Total Physical Memory: %d bytes\nVM mapped by Prom: %d bytes\n" + .asciz "Available Physical Memory: %d bytes\n" + .align 4 + + + .text + + .globl C_LABEL(msgbuf) +msgbufsize = PAGE_SIZE ! 1 page for msg buffer +C_LABEL(msgbuf) = PAGE_SIZE + + +IE_reg_addr = C_LABEL(msgbuf) + msgbufsize ! this page not used; points to IEreg + + +/* Ok, things start to get interesting. We get linked such that 'start' + is the entry symbol. However, it is real low in kernel address space + and as such a nifty place to place the trap table. We achieve this goal + by just jumping to 'gokernel' for the first trap's entry as the sparc + never receives the zero trap as it is real special (hw reset). + + Each trap entry point is the size of 4 sparc instructions (or 4 bytes + * 4 insns = 16 bytes). There are 128 hardware traps (some undefined + or unimplemented) and 128 software traps (sys-calls, etc.). + + One of the instructions must be a branch. More often than not this + will be to a trap handler entry point because it is completely + impossible to handle any trap in 4 insns. I welcome anyone to + challenge this theory. :-) + + On entry into this table the hardware has loaded the program counter + at which the trap occurred into register %l1 and the next program + counter into %l2, this way we can return from the trap with a simple + + jmp %l1; rett %l2 ! poof... + + after properly servicing the trap. It wouldn't be a bad idea to load + some more information into the local regs since we have technically + 2 or 3 instructions to play with besides the jmp to the 'real' trap + handler (one can even go in the delay slot). For now I am going to put + the %psr (processor status register) and the trap-type value in %l0 + and %l3 respectively. Also, for IRQ's I'll put the level in %l4. + +*/ + + .globl start + .globl _start /* warning, solaris hack */ + .globl C_LABEL(trapbase) +_start: /* danger danger */ +start: +C_LABEL(trapbase): + b gokernel; nop; nop; nop; ! we never get trap #0 it is special + + TRAP_ENTRY(0x1, my_trap_handler) /* Instruction Access Exception */ + TRAP_ENTRY(0x2, my_trap_handler) /* Illegal Instruction */ + TRAP_ENTRY(0x3, my_trap_handler) /* Privileged Instruction */ + TRAP_ENTRY(0x4, my_trap_handler) /* Floating Point Disabled */ + TRAP_ENTRY(0x5, spill_window_entry) /* Window Overflow */ + TRAP_ENTRY(0x6, fill_window_entry) /* Window Underflow */ + TRAP_ENTRY(0x7, my_trap_handler) /* Memory Address Not Aligned */ + TRAP_ENTRY(0x8, my_trap_handler) /* Floating Point Exception */ + TRAP_ENTRY(0x9, my_trap_handler) /* Data Miss Exception */ + TRAP_ENTRY(0xa, my_trap_handler) /* Tagged Instruction Overflow */ + TRAP_ENTRY(0xb, my_trap_handler) /* Watchpoint Detected */ + TRAP_ENTRY(0xc, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0xd, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0xe, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0xf, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x10, my_trap_handler) /* Undefined... */ + +/* Level'd interrupt entry points, see macro defs above */ + + TRAP_ENTRY_INTERRUPT_SOFT(1, 0x101) /* IRQ Software/SBUS Level 1 */ + TRAP_ENTRY_INTERRUPT(2) /* IRQ SBUS Level 2 */ + TRAP_ENTRY_INTERRUPT(3) /* IRQ SCSI/DMA/SBUS Level 3 */ + TRAP_ENTRY_INTERRUPT_SOFT(4, 0x104) /* IRQ Software Level 4 */ + TRAP_ENTRY_INTERRUPT(5) /* IRQ SBUS/Ethernet Level 5 */ + TRAP_ENTRY_INTERRUPT_SOFT(6, 0x106) /* IRQ Software Level 6 */ + TRAP_ENTRY_INTERRUPT(7) /* IRQ Video/SBUS Level 5 */ + TRAP_ENTRY_INTERRUPT(8) /* IRQ SBUS Level 6 */ + TRAP_ENTRY_INTERRUPT(9) /* IRQ SBUS Level 7 */ + TRAP_ENTRY_INTERRUPT(10) /* IRQ Timer #1 */ + TRAP_ENTRY_INTERRUPT(11) /* IRQ Floppy Intr. */ + TRAP_ENTRY_INTERRUPT(12) /* IRQ Zilog serial chip */ + TRAP_ENTRY_INTERRUPT(13) /* IRQ Audio Intr. */ + TRAP_ENTRY_TIMER /* IRQ Timer #2 (one we use) */ + TRAP_ENTRY_INTERRUPT_NMI(15, linux_trap_nmi) /* Level 15 (nmi) */ + + TRAP_ENTRY(0x20, my_trap_handler) /* General Register Access Error */ + TRAP_ENTRY(0x21, my_trap_handler) /* Instruction Access Error */ + TRAP_ENTRY(0x22, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x23, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x24, my_trap_handler) /* Co-Processor Disabled */ + TRAP_ENTRY(0x25, my_trap_handler) /* Unimplemented FLUSH inst. */ + TRAP_ENTRY(0x26, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x27, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x28, my_trap_handler) /* Co-Processor Exception */ + TRAP_ENTRY(0x29, my_trap_handler) /* Data Access Error */ + TRAP_ENTRY(0x2a, my_trap_handler) /* Division by zero, you lose... */ + TRAP_ENTRY(0x2b, my_trap_handler) /* Data Store Error */ + TRAP_ENTRY(0x2c, my_trap_handler) /* Data Access MMU-Miss */ + TRAP_ENTRY(0x2d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x2e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x2f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x30, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x31, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x32, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x33, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x34, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x35, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x36, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x37, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x38, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x39, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3a, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3b, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3c, my_trap_handler) /* Instruction Access MMU-Miss */ + TRAP_ENTRY(0x3d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x40, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x41, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x42, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x43, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x44, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x45, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x46, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x47, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x48, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x49, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4a, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4b, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4c, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x50, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x51, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x52, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x53, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x54, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x55, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x56, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x57, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x58, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x59, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5a, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5b, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5c, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x60, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x61, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x62, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x63, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x64, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x65, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x66, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x67, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x68, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x69, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6a, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6b, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6c, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6d, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6e, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6f, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x70, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x71, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x72, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x73, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x74, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x75, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x76, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x77, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x78, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x79, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7a, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7b, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7c, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7d, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7e, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7f, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x80, my_trap_handler) /* SunOS System Call */ + TRAP_ENTRY(0x81, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x82, my_trap_handler) /* Divide by zero trap XXX */ + TRAP_ENTRY(0x83, my_trap_handler) /* Flush Windows Trap XXX */ + TRAP_ENTRY(0x84, my_trap_handler) /* Clean Windows Trap XXX */ + TRAP_ENTRY(0x85, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x86, my_trap_handler) /* Fix Unaligned Access Trap XXX */ + TRAP_ENTRY(0x87, my_trap_handler) /* Integer Overflow Trap XXX */ + TRAP_ENTRY(0x88, my_trap_handler) /* Slowaris System Call */ + TRAP_ENTRY(0x89, my_trap_handler) /* NetBSD System Call */ + TRAP_ENTRY(0x8a, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8b, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8c, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8d, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8e, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8f, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x90, my_trap_handler) /* SparcLinux System Call */ + TRAP_ENTRY(0x91, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x92, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x93, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x94, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x95, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x96, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x97, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x98, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x99, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9a, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9b, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9c, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9d, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9e, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9f, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xaa, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xab, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xac, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xad, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xae, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xaf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xba, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbe, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xca, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xce, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xda, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xde, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xea, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xeb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xec, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xed, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xee, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xef, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfa, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfe, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xff, my_trap_handler) /* Software Trap */ + + .skip 4096 + +C_LABEL(msgbufmapped): + .word 1 + + + +/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in + %g7 and at _prom_vector_p. And also quickly check whether we are on + a v0 or v2 prom. +*/ + +gokernel: or %g0, %o0, %g7 + sethi %hi( C_LABEL(prom_vector_p) ), %g1 + st %o0, [%g1 + %lo( C_LABEL(prom_vector_p) )] ! we will need it later + rd %psr, %l2 + rd %wim, %l3 + rd %tbr, %l4 + or %g0, %o2, %l5 ! could be prom magic value... + +#if 0 /* You think I'm nutz? */ + subcc %l5, 0x0, %g0 ! check for magic SMP pointer + bne nosmp + nop + call %o2 ! call smp prom setup + nop +#endif /* I will be soon... */ + +/* Acquire boot time privileged register values, this will help debugging. + * I figure out and store nwindows later on. + */ + +nosmp: sethi %hi( C_LABEL(boot_psr) ), %l1 + st %l2, [%l1 + %lo( C_LABEL(boot_psr) )] + sethi %hi( C_LABEL(boot_wim) ), %l1 + st %l3, [%l1 + %lo( C_LABEL(boot_wim) )] + sethi %hi( C_LABEL(boot_tbr) ), %l1 + st %l4, [%l1 + %lo( C_LABEL(boot_tbr) )] + sethi %hi( C_LABEL(boot_smp_ptr) ), %l1 + st %l5, [%l1 + %lo( C_LABEL(boot_smp_ptr) )] + + or %g0, %o0, %g7 + sethi %hi( C_LABEL(prom_vector_p) ), %g5 + st %o0, [%g5 + %lo( C_LABEL(prom_vector_p) )] ! we will need it later + + ld [%g7 + 0x4], %o3 + subcc %o3, 0x2, %g0 ! a v2 prom? + be found_v2 + nop + + /* paul@sfe.com.au */ + subcc %o3, 0x3, %g0 ! a v3 prom? + or %g0, 0x3, %o5 + sethi %hi(C_LABEL(prom_iface_vers) ), %g1 + st %o5, [%g1 + %lo( C_LABEL(prom_iface_vers) )] + be not_v2 + nop + + +/* Old sun4's pass our load address into %o0 instead of the prom + pointer. On sun4's you have to hard code the romvec pointer into + your code. Sun probably still does that because they don't even + trust their own "OpenBoot" specifications. +*/ + + sethi %hi(LOAD_ADDR), %g6 + subcc %o0, %g6, %g0 ! an old sun4? + be no_sun4_here + nop + + sethi %hi( C_LABEL(prom_iface_vers) ), %g1 + st %g0, [%g1 + %lo( C_LABEL(prom_iface_vers) )] + b not_v2 + nop + +found_v2: + or %g0, 0x2, %o5 + sethi %hi( C_LABEL(prom_iface_vers) ), %g1 + st %o5, [%g1 + %lo( C_LABEL(prom_iface_vers) )] + +not_v2: + +/* Get the machine type via the mysterious romvec node operations. + * Here we can find out whether we are on a sun4 sun4c, sun4m, or + * a sun4m. The "nodes" are set up as a bunch of n-ary trees which + * you can traverse to get information about devices and such. The + * information acquisition happens via the node-ops which are defined + * in the linux_openprom.h header file. Of particular interest is the + * 'nextnode(int node)' function as it does the smart thing when + * presented with a value of '0', it gives you the first node in the + * tree. These node integers probably offset into some internal prom + * pointer table the openboot has. It's completely undocumented, so + * I'm not about to go sifting through the prom address space, but may + * do so if I get suspicious enough. :-) + */ + + or %g0, %g7, %l1 + add %l1, 0x1c, %l1 + ld [%l1], %l0 + ld [%l0], %l0 + call %l0 + or %g0, %g0, %o0 ! next_node(0) = first_node + + sethi %hi( C_LABEL(cputypvar) ), %o1 ! first node has cpu-arch + or %o1, %lo( C_LABEL(cputypvar) ), %o1 + sethi %hi( C_LABEL(cputypval) ), %o2 ! information, the string + or %o2, %lo( C_LABEL(cputypval) ), %o2 + ld [%l1], %l0 ! 'compatibility' tells + ld [%l0 + 0xc], %l0 ! that we want 'sun4x' where + call %l0 ! x is one of '', 'c', 'm', + nop ! 'd' or 'e'. %o2 holds pointer + ! to a buf where above string + ! will get stored by the prom. + + sethi %hi( C_LABEL(cputypval) ), %o2 ! better safe than sorry + or %o2, %lo( C_LABEL(cputypval) ), %o2 + ldub [%o2 + 0x4], %o0 + subcc %o0, 'c', %g0 ! we already know we are not + be is_sun4c ! on a plain sun4 because of + nop ! the check for 0x4000 in %o0 + subcc %o0, 'm', %g0 ! at start: + be is_sun4m + nop + b no_sun4d_here ! god bless the person who + nop ! tried to run this on sun4d + +is_sun4m: +is_sun4c: ! OK, this is a sun4c, yippie + or %g0, %g7, %g6 ! load up the promvec offsets + sethi %hi(prom_magic), %g5 ! magic mushroom :> + st %g6, [%g5 + %lo(prom_magic)] + add %g7, 0x4, %g6 + sethi %hi(prom_rom_vers), %g5 + st %g6, [%g5 + %lo(prom_rom_vers)] + add %g7, 0x8, %g6 + sethi %hi(prom_pluginvers), %g5 + st %g6, [%g5 + %lo(prom_pluginvers)] + add %g7, 0xc, %g6 + sethi %hi(prom_revision), %g5 + st %g6, [%g5 + %lo(prom_revision)] + add %g7, 0x10, %g6 + sethi %hi(prom_v0mem_desc), %g5 + st %g6, [%g5 + %lo(prom_v0mem_desc)] + add %g7, 0x1c, %g6 + sethi %hi(prom_nodefuncs), %g5 + st %g6, [%g5 + %lo(prom_nodefuncs)] + add %g7, 0x68, %g6 + sethi %hi(prom_printf), %g5 + st %g6, [%g5 + %lo(prom_printf)] + add %g7, 0x6c, %g6 + sethi %hi(prom_abort), %g5 + st %g6, [%g5 + %lo(prom_abort)] + add %g7, 0x74, %g6 + sethi %hi(prom_halt), %g5 + st %g6, [%g5 + %lo(prom_halt)] + add %g7, 0x78, %g6 + sethi %hi(prom_sync), %g5 + st %g6, [%g5 + %lo(prom_sync)] + add %g7, 0x7c, %g6 + sethi %hi(prom_eval), %g5 + st %g6, [%g5 + %lo(prom_eval)] + add %g7, 0x80, %g6 + sethi %hi(prom_v0bootline), %g6 + st %g6, [%g5 + %lo(prom_v0bootline)] + + +/* That was easy, now lets try to print some message on the screen. + * We don't have to worry about bad address translations when the prom + * addresses our pointers because our pointers are at 0x0-kern_size + * as the prom expects. + */ + +/* paul@sfe.com.au */ +/* V3 doesn't have printf.. And I don't really feel like doing the formatting + * myself.. So we miss out on some messages (for now). + */ + ld [%g7 + 0x4], %o0 + subcc %o3, 0x3, %g0 + be v3_bootmsg + nop + + sethi %hi(boot_msg), %o0 + or %o0, %lo(boot_msg), %o0 + sethi %hi(prom_printf), %o1 + ld [%o1 + %lo(prom_printf)], %o1 + ld [%o1], %o1 + call %o1 ! print boot message #1 + nop + + sethi %hi(pstring1), %o0 + or %o0, %lo(pstring1), %o0 + sethi %hi(prom_printf), %o2 + ld [%o2 + %lo(prom_printf)], %o2 + ld [%o2], %o2 + sethi %hi(prom_magic), %o1 + ld [%o1 + %lo(prom_magic)], %o1 + ld [%o1], %o1 + call %o2 + nop + + sethi %hi(pstring2), %o0 + or %o0, %lo(pstring2), %o0 + sethi %hi(prom_printf), %o2 + ld [%o2 + %lo(prom_printf)], %o2 + ld [%o2], %o2 + sethi %hi( C_LABEL(prom_iface_vers) ), %o1 + ld [%o1 + %lo( C_LABEL(prom_iface_vers) )], %o1 + ld [%o1], %o1 + call %o2 + nop + + b rest_of_boot + nop + +v3_bootmsg: + ld [%g7 + 0x94], %o0 + ld [%o0], %o0 + sethi %hi(boot_msg), %o1 + or %o1, %lo(boot_msg), %o1 + mov BOOT_MSG_LEN, %o2 + ld [%g7 + 0xb8], %o4 + call %o4 + nop + + ld [%g7 + 0x94], %o0 + ld [%o0], %o0 + sethi %hi(boot_msg2), %o1 + or %o1, %lo(boot_msg2), %o1 + mov BOOT_MSG2_LEN, %o2 + ld [%g7 + 0xb8], %o4 + call %o4 + nop + b rest_of_boot + nop + + +no_sun4_here: + ld [%g7 + 0x68], %o1 + set sun4_notsup, %o0 + call %o1 + nop + +rest_of_boot: + or %g0, PAGE_SHIFT, %g5 + + sethi %hi(AC_CONTEXT), %g1 ! kernel context, safe now + ! the only valid context + ! until we call paging_init() + stba %g0, [%g1] ASI_CONTROL + + +/* I make the kernel image sit in memory relative to 0x0 with the text + * starting at 0x4000. Now it looks like the way memory is set in Linux + * on an ix86. + */ + +/* Uh, oh, interrupt time. This crap is real confusing. What I want to do is + * clear all interrupts, map the interrupt enable register which in effect + * enables non-maskable interrupts (or NMI's). Actually we take no interrupts + * until we frob with the %tbr (trap base register) which the prom has set + * to all its routines which allows some sanity during bootup. + */ + + sethi %hi(IE_reg_addr), %l0 + or %l0, %lo(IE_reg_addr), %l0 + + set 0xf4000000, %l3 + sethi %hi(INT_ENABLE_REG_PHYSADR), %l2 + or %l2, %lo(INT_ENABLE_REG_PHYSADR), %l2 + srl %l2, %g5, %l2 + or %l2, %l3, %l1 + +#ifndef CONFIG_SRMMU + sta %l1, [%l0] ASI_PTE +#endif + + or %g0, 0x1, %l1 + stb %l1, [%l0] + + +/* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's + * show-time! + */ + + sethi %hi(1f), %g1 + or %g1, %lo(1f), %g1 + jmp %g1 + nop + + .align 4 +1: sethi %hi( C_LABEL(cputyp) ), %o0 + st %g4, [%o0 + %lo( C_LABEL(cputyp) )] + + sethi %hi( C_LABEL(pgshift) ), %o0 + st %g5, [%o0 + %lo( C_LABEL(pgshift) )] + + mov 1, %o0 + sll %o0, %g5, %g5 + sethi %hi( C_LABEL(nbpg) ), %o0 + st %g5, [%o0 + %lo( C_LABEL(nbpg) )] + + sub %g5, 1, %g5 + sethi %hi( C_LABEL(pgofset) ), %o0 + st %g5, [%o0 + %lo( C_LABEL(pgofset) )] + + + rd %psr, %g3 + andn %g3, PSR_ET, %g3 + wr %g3, 0x0, %psr ! make sure traps are off + ! before we play around + WRITE_PAUSE ! no guarantees until 3 insns + + + wr %g0, 0x0, %wim ! magical invalid window reg + WRITE_PAUSE ! see above + +/* I keep the timer interrupt on so that BogoMIPS works and the prom + * keeps updating its "jiffies" counter. 100HZ clock on sparcstations. + */ + +/* If gas wasn't so dumb, I could use or'd macros in this next + * write. ;-( like this (PSR_PS | PSR_S | PSR_PIL)... + */ + + sethi %hi(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2 + or %g2, %lo(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2 + wr %g2, 0x0, %psr + WRITE_PAUSE + + wr %g0, 0x2, %wim ! window 1 invalid + WRITE_PAUSE + + or %g0, 0x1, %g1 + sethi %hi( C_LABEL(current) + THREAD_WIM), %g2 + st %g1, [%g2 + %lo( C_LABEL(current) + THREAD_WIM)] + +/* I want a kernel stack NOW! */ + + set ( C_LABEL(init_user_stack) + 4092 - 96 - 80), %fp + set ( C_LABEL(init_user_stack) + 4092), %sp + +/* now out stack is set up similarly to the way it is on the i386 */ + + rd %psr, %l0 + wr %l0, PSR_ET, %psr + WRITE_PAUSE + +/* + * Maybe the prom zeroes out our BSS section, maybe it doesn't. I certainly + * don't know, do you? + */ + + set C_LABEL(edata) , %o0 + set C_LABEL(end) , %o1 + sub %o1, %o0, %g2 + sethi %hi( C_LABEL(kernel_bss_len) ), %g3 + st %g2, [%g3 + %lo( C_LABEL(kernel_bss_len) )] + sethi %hi( C_LABEL(trapbase) ), %g3 + or %g3, %lo( C_LABEL(trapbase) ), %g3 + sethi %hi( C_LABEL(etext) ), %g4 + or %g4, %lo( C_LABEL(etext) ), %g4 + sub %g4, %g3, %g2 + sethi %hi( C_LABEL(kernel_text_len) ), %g3 + st %g2, [%g3 + %lo( C_LABEL(kernel_text_len) )] + sethi %hi( C_LABEL(etext) ), %g4 + or %g4, %lo( C_LABEL(etext) ), %g4 + sethi %hi( C_LABEL(edata) ), %g3 + or %g3, %lo( C_LABEL(edata) ), %g3 + sub %g3, %g4, %g2 + sethi %hi( C_LABEL(kernel_data_len) ), %g3 + st %g2, [%g3 + %lo( C_LABEL(kernel_data_len) )] + or %g0, %g0, %g1 + +1: + st %g0, [%o0] + add %o0, 0x4, %o0 + subcc %o0, %o1, %g0 + bl 1b + nop + +/* Compute NWINDOWS and stash it away. Now uses %wim trick explained + * in the V8 manual. Ok, this method seems to work, sparc is cool... + */ + + sethi %hi(0xffffffff), %g1 + rd %wim, %g2 ! save current value + or %g1, %lo(0xffffffff), %g1 + wr %g1, 0x0, %wim + rd %wim, %g1 ! get remaining mask + wr %g2, 0x0, %wim ! restore old value + WRITE_PAUSE + + or %g0, 0x0, %g3 + +1: srl %g1, 0x1, %g1 ! shift until highest + subcc %g1, 0x0, %g0 ! bit set + bne 1b + add %g3, 0x1, %g3 + sethi %hi( C_LABEL(nwindows) ), %g4 + st %g3, [%g4 + %lo( C_LABEL(nwindows) )] ! store final value + sub %g3, 0x1, %g3 + sethi %hi( C_LABEL(nwindowsm1) ), %g4 + st %g3, [%g4 + %lo( C_LABEL(nwindowsm1) )] + + +/* Here we go */ + +#ifndef CONFIG_SUN4M + /* paul@sfe.com.au */ + /* Look into traps later :( */ + set C_LABEL(trapbase), %g3 + wr %g3, 0x0, %tbr + WRITE_PAUSE +#endif + + +/* First we call init_prom() to set up romvec, then off to start_kernel() */ +/* XXX put this in arch_init() */ + + sethi %hi( C_LABEL(prom_vector_p) ), %g5 + call C_LABEL(init_prom) + ld [%g5 + %lo( C_LABEL(prom_vector_p) )], %o0 /* delay slot */ + + call C_LABEL(start_kernel) + nop + + call halt_me + nop + +/* There, happy now adrian? */ + +no_sun4d_here: + ld [%g7 + 0x68], %o1 + set sun4d_notsup, %o0 + call %o1 + nop + b halt_me + nop + +halt_me: + ld [%g7 + 0x74], %o0 + call %o0 ! get us out of here... + nop ! apparently solaris is better + + .data + .align 4 + +/* + * Fill up the prom vector, note in particular the kind first element, + * no joke. I don't need all of them in here as the entire prom vector + * gets initialized in c-code so all routines can use it. + */ + + .globl C_LABEL(prom_vector_p) + +C_LABEL(prom_vector_p): .skip 4 +prom_magic: .skip 4 ! magic mushroom, beware... +prom_rom_vers: .skip 4 ! interface version (v0 or v2) +prom_pluginvers: .skip 4 ! XXX help help help ??? +prom_revision: .skip 4 ! PROM revision (ie. 1.4) +prom_halt: .skip 4 ! void halt(void) solaris friend +prom_eval: .skip 4 ! void eval(int len, char* string) +prom_v0bootline: .skip 4 ! boot command line +prom_v0mem_desc: .skip 4 ! V0 memory descriptor list ptr. +prom_nodefuncs: .skip 4 ! Magical Node functions +prom_printf: .skip 4 ! minimal printf() + +/* The prom_abort pointer MUST be mapped in all contexts, because if you + * don't then if a user process is running when you press the abort key + * sequence, all sorts of bad things can happen + */ + +prom_abort: .skip 4 ! L1-A magic cookie + ! must be mapped in ALL contexts + +/* prom_sync is a place where the kernel should place a pointer to a kernel + * function that when called will sync all pending information to the drives + * and then promptly return. If the kernel gets aborted with 'L1-A' one can + * give the 'sync' command to the boot prompt and this magic cookie gets + * executed. Nice feature eh? + */ + +prom_sync: .skip 4 ! hook in prom for sync func + + .align 4 + +/* We calculate the following at boot time, window fills/spills and trap entry + * code uses these to keep track of the register windows. + */ + + .globl C_LABEL(nwindows) + .globl C_LABEL(nwindowsm1) +C_LABEL(nwindows): .skip 4 +C_LABEL(nwindowsm1): .skip 4 + + .align 4 +/* Boot time privileged register values, plus magic %o2 value */ + + .globl C_LABEL(boot_wim) + .globl C_LABEL(boot_psr) + .globl C_LABEL(boot_tbr) + .globl C_LABEL(boot_smp_ptr) +C_LABEL(boot_wim): .skip 4 +C_LABEL(boot_psr): .skip 4 +C_LABEL(boot_tbr): .skip 4 +C_LABEL(boot_smp_ptr): .skip 4 + + + .align 4 +/* Miscellaneous pieces of information saved at kernel startup. */ + .globl C_LABEL(kernel_text_len) + .globl C_LABEL(kernel_data_len) + .globl C_LABEL(kernel_bss_len) +C_LABEL(kernel_text_len): .word 0 +C_LABEL(kernel_data_len): .word 0 +C_LABEL(kernel_bss_len): .word 0 + +/* These are for page alignment/offset information as they change from + machine to machine. +*/ + + .globl C_LABEL(pgshift) + .globl C_LABEL(nbpg) + .globl C_LABEL(pgofset) + + .align 4 +C_LABEL(pgshift): + .word 1 +C_LABEL(nbpg): + .word 1 +C_LABEL(pgofset): + .word 1 + +/* Just to get the kernel through the compiler for now */ + .globl C_LABEL(swapper_pg_dir), C_LABEL(pg0) + .globl C_LABEL(empty_bad_page) + .globl C_LABEL(empty_bad_page_table) + .globl C_LABEL(empty_zero_page) + .globl C_LABEL(floppy_track_buffer) +C_LABEL(floppy_track_buffer): + .fill 512*2*36,1,0 + + .align 4 +C_LABEL(swapper_pg_dir): .skip 0x1000 +C_LABEL(pg0): .skip 0x1000 +C_LABEL(empty_bad_page): .skip 0x1000 +C_LABEL(empty_bad_page_table): .skip 0x1000 +C_LABEL(empty_zero_page): .skip 0x1000 + + .align 4 +diagstr: .asciz "DIAG\n" + .align 4 diff --git a/arch/sparc/kernel/idprom.c b/arch/sparc/kernel/idprom.c new file mode 100644 index 000000000..c12f7467b --- /dev/null +++ b/arch/sparc/kernel/idprom.c @@ -0,0 +1,183 @@ +/* idprom.c: Routines to load the idprom into kernel addresses and + * interpret the data contained within. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/kernel.h> + +#include <asm/types.h> +#include <asm/openprom.h> +#include <asm/idprom.h> + +struct idp_struct idprom; +extern int num_segmaps, num_contexts; + +void get_idprom(void) +{ + char* idp_addr; + char* knl_idp_addr; + int i; + + idp_addr = (char *)IDPROM_ADDR; + knl_idp_addr = (char *) &idprom; + + for(i = 0; i<IDPROM_SIZE; i++) + *knl_idp_addr++ = *idp_addr++; + + return; +} + +/* find_vac_size() returns the number of bytes in the VAC (virtual + * address cache) on this machine. + */ + +int +find_vac_size(void) +{ + int vac_prop_len; + int vacsize = 0; + int node_root; + + node_root = (*(romvec->pv_nodeops->no_nextnode))(0); + + vac_prop_len = (*(romvec->pv_nodeops->no_proplen))(node_root, "vac-size"); + + if(vac_prop_len != -1) + { + (*(romvec->pv_nodeops->no_getprop))(node_root, "vac-size", (char *) &vacsize); + return vacsize; + } + else + { + + /* The prom node functions can't help, do it via idprom struct */ + switch(idprom.id_machtype) + { + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + return 65536; + default: + return -1; + } + }; +} + +/* find_vac_linesize() returns the size in bytes of the VAC linesize */ + +int +find_vac_linesize(void) +{ + int vac_prop_len; + int vaclinesize = 0; + int node_root; + + node_root = (*(romvec->pv_nodeops->no_nextnode))(0); + + vac_prop_len = (*(romvec->pv_nodeops->no_proplen))(node_root, "vac-linesize"); + + if(vac_prop_len != -1) + { + (*(romvec->pv_nodeops->no_getprop))(node_root, "vac-linesize", + (char *) &vaclinesize); + return vaclinesize; + } + else + { + + /* The prom node functions can't help, do it via idprom struct */ + switch(idprom.id_machtype) + { + case 0x51: + case 0x52: + case 0x53: + case 0x54: + return 16; + case 0x55: + case 0x56: + case 0x57: + return 32; + default: + return -1; + } + }; +} + +int +find_vac_hwflushes(void) +{ + register int len, node_root; + int tmp1, tmp2; + + node_root = (*(romvec->pv_nodeops->no_nextnode))(0); + + len = (*(romvec->pv_nodeops->no_proplen))(node_root, "vac_hwflush"); + +#ifdef DEBUG_IDPROM + printf("DEBUG: find_vac_hwflushes: proplen vac_hwflush=0x%x\n", len); +#endif + + /* Sun 4/75 has typo in prom_node, it's a dash instead of an underscore + * in the property name. :-( + */ + len |= (*(romvec->pv_nodeops->no_proplen))(node_root, "vac-hwflush"); + +#ifdef DEBUG_IDPROM + printf("DEBUG: find_vac_hwflushes: proplen vac-hwflush=0x%x\n", len); +#endif + + len = (*(romvec->pv_nodeops->no_getprop))(node_root,"vac_hwflush", + (char *) &tmp1); + if(len != 4) tmp1=0; + + len = (*(romvec->pv_nodeops->no_getprop))(node_root, "vac-hwflush", + (char *) &tmp2); + if(len != 4) tmp2=0; + + + return (tmp1|tmp2); +} + +void +find_mmu_num_segmaps(void) +{ + register int root_node, len; + + root_node = (*(romvec->pv_nodeops->no_nextnode))(0); + + len = (*(romvec->pv_nodeops->no_getprop))(root_node, "mmu-npmg", + (char *) &num_segmaps); + +#ifdef DEBUG_MMU + printf("find_mmu_num_segmaps: property length = %d\n", len); +#endif + + if(len != 4) num_segmaps = 128; + + return; +} + +void +find_mmu_num_contexts(void) +{ + register int root_node, len; + + root_node = (*(romvec->pv_nodeops->no_nextnode))(0); + + len = (*(romvec->pv_nodeops->no_getprop))(root_node, "mmu-nctx", + (char *) &num_contexts); + +#ifdef DEBUG_MMU + printf("find_mmu_num_contexts: property length = %d\n", len); +#endif + + if(len != 4) num_contexts = 8; + + return; +} + diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c new file mode 100644 index 000000000..effa6c25e --- /dev/null +++ b/arch/sparc/kernel/ioport.c @@ -0,0 +1,12 @@ +/* ioport.c: I/O access on the Sparc. Work in progress.. Most of the things + * in this file are for the sole purpose of getting the kernel + * through the compiler. :-) + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/ioport.h> diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c new file mode 100644 index 000000000..5dcaf1971 --- /dev/null +++ b/arch/sparc/kernel/irq.c @@ -0,0 +1,335 @@ +/* arch/sparc/kernel/irq.c: Interrupt request handling routines. On the + * Sparc the IRQ's are basically 'cast in stone' + * and you are supposed to probe the prom's device + * node trees to find out who's got which IRQ. + * + * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) + * + */ + +/* + * IRQ's are in fact implemented a bit like signal handlers for the kernel. + * The same sigaction struct is used, and with similar semantics (ie there + * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there + * are similarities. + * + * sa_handler(int irq_NR) is the default function called (0 if no). + * sa_mask is horribly ugly (I won't even mention it) + * sa_flags contains various info: SA_INTERRUPT etc + * sa_restorer is the unused + */ + +#include <linux/config.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/linkage.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <asm/ptrace.h> +#include <asm/system.h> +#include <asm/psr.h> +#include <asm/vaddrs.h> +#include <asm/clock.h> +#include <asm/openprom.h> + +#define DEBUG_IRQ + +void disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + unsigned char *int_reg; + + save_flags(flags); + cli(); + + /* We have mapped the irq enable register in head.S and all we + * have to do here is frob the bits. + */ + + int_reg = (unsigned char *) IRQ_ENA_ADR; + + switch(irq_nr) + { + case 1: + *int_reg = ((*int_reg) & (~(0x02))); + break; + case 4: + *int_reg = ((*int_reg) & (~(0x04))); + break; + case 6: + *int_reg = ((*int_reg) & (~(0x08))); + break; + case 8: + *int_reg = ((*int_reg) & (~(0x10))); + break; + case 10: + *int_reg = ((*int_reg) & (~(0x20))); + break; + case 14: + *int_reg = ((*int_reg) & (~(0x80))); + break; + default: + printk("AIEEE, Illegal interrupt disable requested irq=%d\n", + (int) irq_nr); + break; + }; + + restore_flags(flags); + return; +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + unsigned char *int_reg; + + save_flags(flags); + cli(); + + /* We have mapped the irq enable register in head.S and all we + * have to do here is frob the bits. + */ + + int_reg = (unsigned char *) IRQ_ENA_ADR; + +#ifdef DEBUG_IRQ + printk(" --- Enabling IRQ level %d ---\n", irq_nr); +#endif + + switch(irq_nr) + { + case 1: + *int_reg = ((*int_reg) | 0x02); + break; + case 4: + *int_reg = ((*int_reg) | 0x04); + break; + case 6: + *int_reg = ((*int_reg) | 0x08); + break; + case 8: + *int_reg = ((*int_reg) | 0x10); + break; + case 10: + *int_reg = ((*int_reg) | 0x20); + break; + case 14: + *int_reg = ((*int_reg) | 0x80); + break; + default: + printk("AIEEE, Illegal interrupt enable requested irq=%d\n", + (int) irq_nr); + break; + }; + + restore_flags(flags); + + return; +} + +/* + * 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 } +}; + + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct irqaction * action = irq_action; + + for (i = 0 ; i < 16 ; i++, action++) { + if (!action->handler) + continue; + len += sprintf(buf+len, "%2d: %8d %c %s\n", + i, kstat.interrupts[i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + } + return len; +} + +void free_irq(unsigned int irq) +{ + struct irqaction * action = irq + irq_action; + unsigned long flags; + + if (irq > 14) { /* 14 irq levels on the sparc */ + 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(); + disable_irq(irq); + action->handler = NULL; + action->flags = 0; + action->mask = 0; + action->name = NULL; + restore_flags(flags); +} + +#if 0 +static void handle_nmi(struct pt_regs * regs) +{ + printk("NMI, probably due to bus-parity error.\n"); + printk("PC=%08lx, SP=%08lx\n", regs->pc, regs->sp); +} +#endif + +void unexpected_irq(int irq, struct pt_regs * regs) +{ + int i; + + printk("IO device interrupt, irq = %d\n", irq); + printk("PC = %08lx NPC = %08lx SP=%08lx\n", regs->pc, + regs->npc, regs->sp); + printk("Expecting: "); + for (i = 0; i < 16; i++) + if (irq_action[i].handler) + printk("[%s:%d] ", irq_action[i].name, i); + printk("AIEEE\n"); +} + +static inline void handler_irq(int irq, struct pt_regs * regs) +{ + struct irqaction * action = irq + irq_action; + + if (!action->handler) { + unexpected_irq(irq, regs); + return; + } + action->handler(irq, regs); +} + +/* + * do_IRQ handles IRQ's that have been installed without the + * SA_INTERRUPT flag: it uses the full signal-handling return + * and runs with other interrupts enabled. All relatively slow + * IRQ's should use this format: notably the keyboard/timer + * routines. + */ +asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +{ + struct irqaction *action = irq + irq_action; + + kstat.interrupts[irq]++; + action->handler(irq, regs); + return; +} + +/* + * Since we need to special things to clear up the clock chip around + * the do_timer() call we have a special version of do_IRQ for the + * level 14 interrupt which does these things. + */ + +asmlinkage void do_sparc_timer(int irq, struct pt_regs * regs) +{ + struct irqaction *action = irq + irq_action; + register volatile int clear; + + kstat.interrupts[irq]++; + + /* I do the following already in the entry code, better safe than + * sorry for now. Reading the limit register clears the interrupt. + */ + clear = TIMER_STRUCT->timer_limit14; + + action->handler(irq, regs); + return; +} + +/* + * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return + * stuff - the handler is also running with interrupts disabled unless + * it explicitly enables them later. + */ +asmlinkage void do_fast_IRQ(int irq) +{ + kstat.interrupts[irq]++; + printk("Got FAST_IRQ number %04lx\n", (long unsigned int) irq); + return; +} + +extern int first_descent; +extern void probe_clock(int); + +int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), + unsigned long irqflags, const char * devname) +{ + struct irqaction *action; + unsigned long flags; + + if(irq > 14) /* Only levels 1-14 are valid on the Sparc. */ + return -EINVAL; + + if(irq == 0) /* sched_init() requesting the timer IRQ */ + { + irq = 14; + probe_clock(first_descent); + } + + action = irq + irq_action; + + if(action->handler) + return -EBUSY; + + if(!handler) + return -EINVAL; + + save_flags(flags); + + cli(); + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + + enable_irq(irq); + + restore_flags(flags); + + return 0; +} + +unsigned int probe_irq_on (void) +{ + unsigned int irqs = 0; + + return irqs; +} + +int probe_irq_off (unsigned int irqs) +{ + unsigned int i = 0; + + return i; +} + +void init_IRQ(void) +{ + return; +} diff --git a/arch/sparc/kernel/probe.c b/arch/sparc/kernel/probe.c new file mode 100644 index 000000000..462556164 --- /dev/null +++ b/arch/sparc/kernel/probe.c @@ -0,0 +1,432 @@ +/* probe.c: Preliminary device tree probing routines... + + Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) +*/ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <asm/vac-ops.h> +#include <asm/io.h> +#include <asm/vaddrs.h> +#include <asm/param.h> +#include <asm/clock.h> +#include <asm/system.h> + +/* #define DEBUG_PROBING */ + +char promstr_buf[64]; /* overkill */ +unsigned int promint_buf[1]; + +extern int prom_node_root; +extern int num_segmaps, num_contexts; + +extern int node_get_sibling(int node); +extern int node_get_child(int node); +extern char* get_str_from_prom(int node, char* name, char* value); +extern unsigned int* get_int_from_prom(int node, char* name, unsigned int *value); + +int first_descent; + +/* Cpu-type information and manufacturer strings */ + + +struct cpu_iu_info { + int psr_impl; + int psr_vers; + char* cpu_name; /* should be enough I hope... */ +}; + +struct cpu_fp_info { + int psr_impl; + int fp_vers; + char* fp_name; +}; + +struct cpu_fp_info linux_sparc_fpu[] = { + { 0, 0, "Fujitsu MB86910 or Weitek WTL1164/5"}, + { 0, 1, "Fujitsu MB86911 or Weitek WTL1164/5"}, + { 0, 2, "LSI Logic L64802 or Texas Instruments ACT8847"}, + { 0, 3, "Weitek WTL3170/2"}, + { 0, 4, "Lsi Logic/Meiko L64804"}, + { 0, 5, "reserved"}, + { 0, 6, "reserved"}, + { 0, 7, "No FPU"}, + { 1, 0, "Lsi Logic L64812 or Texas Instruments ACT8847"}, + { 1, 1, "Lsi Logic L64814"}, + { 1, 2, "Texas Instruments TMS390-C602A"}, + { 1, 3, "Weitek WTL3171"}, + { 1, 4, "reserved"}, + { 1, 5, "reserved"}, + { 1, 6, "reserved"}, + { 1, 7, "No FPU"}, + { 2, 0, "BIT B5010 or B5110/20 or B5210"}, + { 2, 1, "reserved"}, + { 2, 2, "reserved"}, + { 2, 3, "reserved"}, + { 2, 4, "reserved"}, + { 2, 5, "reserved"}, + { 2, 6, "reserved"}, + { 2, 7, "No FPU"}, + { 5, 0, "Matsushita MN10501"}, + { 5, 1, "reserved"}, + { 5, 2, "reserved"}, + { 5, 3, "reserved"}, + { 5, 4, "reserved"}, + { 5, 5, "reserved"}, + { 5, 6, "reserved"}, + { 5, 7, "No FPU"}, +}; + +struct cpu_iu_info linux_sparc_chips[] = { + { 0, 0, "Fujitsu Microelectronics, Inc. - MB86900/1A"}, + { 1, 0, "Cypress CY7C601"}, + { 1, 1, "LSI Logic Corporation - L64811"}, + { 1, 3, "Cypress CY7C611"}, + { 2, 0, "Bipolar Integrated Technology - B5010"}, + { 3, 0, "LSI Logic Corporation - unknown-type"}, + { 4, 0, "Texas Instruments, Inc. - unknown"}, + { 4, 1, "Texas Instruments, Inc. - Sparc Classic"}, + { 4, 2, "Texas Instruments, Inc. - unknown"}, + { 4, 3, "Texas Instruments, Inc. - unknown"}, + { 4, 4, "Texas Instruments, Inc. - unknown"}, + { 4, 5, "Texas Instruments, Inc. - unknown"}, + { 5, 0, "Matsushita - MN10501"}, + { 6, 0, "Philips Corporation - unknown"}, + { 7, 0, "Harvest VLSI Design Center, Inc. - unknown"}, + { 8, 0, "Systems and Processes Engineering Corporation (SPEC)"}, + { 9, 0, "UNKNOWN CPU-VENDOR/TYPE"}, + { 0xa, 0, "UNKNOWN CPU-VENDOR/TYPE"}, + { 0xb, 0, "UNKNOWN CPU-VENDOR/TYPE"}, + { 0xc, 0, "UNKNOWN CPU-VENDOR/TYPE"}, + { 0xd, 0, "UNKNOWN CPU-VENDOR/TYPE"}, + { 0xe, 0, "UNKNOWN CPU-VENDOR/TYPE"}, + { 0xf, 0, "UNKNOWN CPU-VENDOR/TYPE"}, +}; + +char *sparc_cpu_type = "cpu-oops"; +char *sparc_fpu_type = "fpu-oops"; + +/* various Virtual Address Cache parameters we find at boot time... */ + +extern int vac_size, vac_linesize, vac_do_hw_vac_flushes; +extern int vac_entries_per_context, vac_entries_per_segment; +extern int vac_entries_per_page; + +extern int find_vac_size(void); +extern int find_vac_linesize(void); +extern int find_vac_hwflushes(void); +extern void find_mmu_num_segmaps(void); +extern void find_mmu_num_contexts(void); + +void +probe_cpu(void) +{ + register int psr_impl=0; + register int psr_vers = 0; + register int fpu_vers = 0; + register int i = 0; + unsigned int tmp_fsr; + + &tmp_fsr; /* GCC grrr... */ + + __asm__("rd %%psr, %0\n\t" + "mov %0, %1\n\t" + "srl %0, 28, %0\n\t" + "srl %1, 24, %1\n\t" + "and %0, 0xf, %0\n\t" + "and %1, 0xf, %1\n\t" : + "=r" (psr_impl), + "=r" (psr_vers) : + "0" (psr_impl), + "1" (psr_vers)); + + + __asm__("st %%fsr, %1\n\t" + "ld %1, %0\n\t" + "srl %0, 17, %0\n\t" + "and %0, 0x7, %0\n\t" : + "=r" (fpu_vers), + "=m" (tmp_fsr) : + "0" (fpu_vers), + "1" (tmp_fsr)); + + printk("fpu_vers: %d ", fpu_vers); + printk("psr_impl: %d ", psr_impl); + printk("psr_vers: %d \n\n", psr_vers); + + for(i = 0; i<23; i++) + { + if(linux_sparc_chips[i].psr_impl == psr_impl) + if(linux_sparc_chips[i].psr_vers == psr_vers) + { + sparc_cpu_type = linux_sparc_chips[i].cpu_name; + break; + } + } + + if(i==23) + { + printk("No CPU type! You lose\n"); + printk("DEBUG: psr.impl = 0x%x psr.vers = 0x%x\n", psr_impl, + psr_vers); + return; + } + + for(i = 0; i<32; i++) + { + if(linux_sparc_fpu[i].psr_impl == psr_impl) + if(linux_sparc_fpu[i].fp_vers == fpu_vers) + { + sparc_fpu_type = linux_sparc_fpu[i].fp_name; + break; + } + } + + if(i == 32) + { + printk("No FPU type! You don't completely lose though...\n"); + printk("DEBUG: psr.impl = 0x%x fsr.vers = 0x%x\n", psr_impl, fpu_vers); + sparc_fpu_type = linux_sparc_fpu[31].fp_name; + } + + printk("CPU: %s \n", sparc_cpu_type); + printk("FPU: %s \n", sparc_fpu_type); + + return; +} + +void +probe_vac(void) +{ + register unsigned int x,y; + +#ifndef CONFIG_SRMMU + vac_size = find_vac_size(); + vac_linesize = find_vac_linesize(); + vac_do_hw_vac_flushes = find_vac_hwflushes(); + + /* Calculate various constants that make the cache-flushing code + * mode speedy. + */ + + vac_entries_per_segment = vac_entries_per_context = vac_size >> 12; + + for(x=0,y=vac_linesize; ((1<<x)<y); x++); + if((1<<x) != vac_linesize) printk("Warning BOGUS VAC linesize 0x%x", + vac_size); + + vac_entries_per_page = x; + + printk("Sparc VAC cache: Size=%d bytes Line-Size=%d bytes ... ", vac_size, + vac_linesize); + + /* Here we want to 'invalidate' all the software VAC "tags" + * just in case there is garbage in there. Then we enable it. + */ + + for(x=0x80000000, y=(x+vac_size); x<y; x+=vac_linesize) + __asm__("sta %0, [%1] %2" : : "r" (0), "r" (x), "n" (0x2)); + + x=enable_vac(); + printk("ENABLED\n"); +#endif + + return; +} + +void +probe_mmu(void) +{ + find_mmu_num_segmaps(); + find_mmu_num_contexts(); + + printk("MMU segmaps: %d MMU contexts: %d\n", num_segmaps, + num_contexts); + + return; +} + +void +probe_clock(int fchild) +{ + register int node, type; + register char *node_str; + + /* This will basically traverse the node-tree of the prom to see + * which timer chip is on this machine. + */ + + printk("Probing timer chip... "); + + type = 0; + for(node = fchild ; ; ) + { + node_str = get_str_from_prom(node, "model", promstr_buf); + if(strcmp(node_str, "mk48t02") == 0) + { + type = 2; + break; + } + + if(strcmp(node_str, "mk48t08") == 0) + { + type = 8; + break; + } + + node = node_get_sibling(node); + if(node == fchild) + { + printk("Aieee, could not find timer chip type\n"); + return; + } + } + + printk("Mostek %s\n", node_str); + printk("At OBIO address: 0x%x Virtual address: 0x%x\n", + (unsigned int) TIMER_PHYSADDR, (unsigned int) TIMER_STRUCT); + + mapioaddr((unsigned long) TIMER_PHYSADDR, + (unsigned long) TIMER_STRUCT); + + TIMER_STRUCT->timer_limit14=(((1000000/HZ) << 10) | 0x80000000); + + return; +} + + +void +probe_esp(register int esp_node) +{ + register int nd; + register char* lbuf; + + nd = node_get_child(esp_node); + + printk("\nProbing ESP:\n"); + lbuf = get_str_from_prom(nd, "name", promstr_buf); + + if(*get_int_from_prom(nd, "name", promint_buf) != 0) + printk("Node: 0x%x Name: %s\n", nd, lbuf); + + while((nd = node_get_sibling(nd)) != 0) { + lbuf = get_str_from_prom(nd, "name", promstr_buf); + printk("Node: 0x%x Name: %s\n", nd, lbuf); + } + + printk("\n"); + + return; +} + +void +probe_sbus(register int cpu_child_node) +{ + register int nd, savend; + register char* lbuf; + + nd = cpu_child_node; + + lbuf = (char *) 0; + + while((nd = node_get_sibling(nd)) != 0) { + lbuf = get_str_from_prom(nd, "name", promstr_buf); + if(strcmp(lbuf, "sbus") == 0) + break; + }; + + nd = node_get_child(nd); + + printk("Node: 0x%x Name: %s\n", nd, + get_str_from_prom(nd, "name", promstr_buf)); + + if(strcmp(lbuf, "esp") == 0) { + probe_esp(nd); + }; + + while((nd = node_get_sibling(nd)) != 0) { + printk("Node: 0x%x Name: %s\n", nd, + lbuf = get_str_from_prom(nd, "name", promstr_buf)); + + if(strcmp(lbuf, "esp") == 0) { + savend = nd; + probe_esp(nd); + nd = savend; + }; + }; + + printk("\n"); + return; +} + +extern unsigned long probe_memory(void); +extern struct sparc_phys_banks sp_banks[14]; +unsigned int phys_bytes_of_ram, end_of_phys_memory; + +void +probe_devices(void) +{ + register int nd, i; + register char* str; + + nd = prom_node_root; + + printk("PROBING DEVICES:\n"); + + str = get_str_from_prom(nd, "device_type", promstr_buf); + if(strcmp(str, "cpu") == 0) { + printk("Found CPU root prom device tree node.\n"); + } else { + printk("Root node in device tree was not 'cpu' cannot continue.\n"); + halt(); + }; + +#ifdef DEBUG_PROBING + printk("String address for d_type: 0x%x\n", (unsigned int) str); + printk("str[0] = %c str[1] = %c str[2] = %c \n", str[0], str[1], str[2]); +#endif + + str = get_str_from_prom(nd, "name", promstr_buf); + +#ifdef DEBUG_PROBING + printk("String address for name: 0x%x\n", (unsigned int) str); + printk("str[0] = %c str[1] = %c str[2] = %c \n", str[0], str[1], str[2]); +#endif + + printk("Name: %s \n", str); + + first_descent = nd = node_get_child(nd); + + +/* Ok, here will go a call to each specific device probe. We can + * call these now that we have the 'root' node and the child of + * this node to send to the routines. ORDER IS IMPORTANT! + */ + + probe_cpu(); + probe_vac(); + probe_mmu(); + phys_bytes_of_ram = probe_memory(); + + printk("Physical Memory: %d bytes\n", (int) phys_bytes_of_ram); + for(i=0; sp_banks[i].num_bytes != 0; i++) { + printk("Bank %d: base 0x%x bytes %d\n", i, + (unsigned int) sp_banks[i].base_addr, + (int) sp_banks[i].num_bytes); + end_of_phys_memory = sp_banks[i].base_addr + sp_banks[i].num_bytes; + } + + printk("PROM Root Child Node: 0x%x Name: %s \n", nd, + get_str_from_prom(nd, "name", promstr_buf)); + + while((nd = node_get_sibling(nd)) != 0) { + printk("Node: 0x%x Name: %s", nd, + get_str_from_prom(nd, "name", promstr_buf)); + printk("\n"); + }; + + printk("\nProbing SBUS:\n"); + probe_sbus(first_descent); + + return; +} diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c new file mode 100644 index 000000000..679863ba3 --- /dev/null +++ b/arch/sparc/kernel/process.c @@ -0,0 +1,112 @@ +/* + * linux/arch/i386/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#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 <asm/segment.h> +#include <asm/system.h> + +void ret_from_sys_call(void) { __asm__("nop"); } + +/* + * The idle loop on a i386.. + */ +asmlinkage int sys_idle(void) +{ + if (current->pid != 0) + return -EPERM; + + /* Map out the low memory: it's no longer needed */ + /* Sparc version RSN */ + + /* endless idle loop with no priority at all */ + current->counter = -100; + for (;;) { + schedule(); + } +} + +void hard_reset_now(void) +{ + halt(); +} + +void show_regs(struct pt_regs * regs) +{ + printk("\nSP: %08lx PC: %08lx NPC: %08lx\n", regs->sp, regs->pc, + regs->npc); +} + +/* + * Do necessary setup to start up a newly executed thread. + */ +void start_thread(struct pt_regs * regs, unsigned long sp, unsigned long fp) +{ + regs->sp = sp; + regs->fp = fp; +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + halt(); +} + +void flush_thread(void) +{ + halt(); +} + +void copy_thread(int nr, unsigned long clone_flags, unsigned long sp, struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs * childregs; + + childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1; + p->tss.usp = (unsigned long) childregs; + *childregs = *regs; + childregs->sp = sp; + p->tss.psr = regs->psr; /* for condition codes */ + return; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + return; /* solaris does this enough */ +} + +asmlinkage int sys_fork(struct pt_regs regs) +{ + return do_fork(COPYVM | SIGCHLD, regs.sp, ®s); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(struct pt_regs regs) +{ + halt(); + return 0; +} + diff --git a/arch/sparc/kernel/promops.c b/arch/sparc/kernel/promops.c new file mode 100644 index 000000000..b5c897b0d --- /dev/null +++ b/arch/sparc/kernel/promops.c @@ -0,0 +1,107 @@ +/* promops.c: Prom node tree operations and Prom Vector initialization + * initialization routines. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/kernel.h> + +#include <asm/openprom.h> + +/* #define DEBUG_PROMOPS */ +#define MAX_PR_LEN 64 /* exotic hardware probably overshoots this */ + +int prom_node_root; /* initialized in init_prom */ + +extern struct linux_romvec *romvec; + +/* These two functions return and siblings and direct child descendents + * in the prom device tree respectively. + */ + +int +node_get_sibling(int node) +{ + return (*(romvec->pv_nodeops->no_nextnode))(node); +} + +int +node_get_child(int node) +{ + return (*(romvec->pv_nodeops->no_child))(node); +} + +/* The following routine is used during device probing to determine + * an integer value property about a (perhaps virtual) device. This + * could be anything, like the size of the mmu cache lines, etc. + * the default return value is -1 is the prom has nothing interesting. + */ + +unsigned int prom_int_null; + +unsigned int * +get_int_from_prom(int node, char *nd_prop, unsigned int *value) +{ + unsigned int pr_len; + + *value = &prom_int_null; /* duh, I was returning -1 as an unsigned int, prom_panic() */ + + pr_len = romvec->pv_nodeops->no_proplen(node, nd_prop); + if(pr_len > MAX_PR_LEN) + { +#ifdef DEBUG_PROMOPS + printk("Bad pr_len in promops -- node: %d nd_prop: %s pr_len: %d", + node, nd_prop, (int) pr_len); +#endif + return value; /* XXX */ + } + + romvec->pv_nodeops->no_getprop(node, nd_prop, (char *) value); + + return value; +} + + +/* This routine returns what is termed a property string as opposed + * to a property integer as above. This can be used to extract the + * 'type' of device from the prom. An example could be the clock timer + * chip type. By default you get returned a null string if garbage + * is returned from the prom. + */ + +char * +get_str_from_prom(int node, char *nd_prop, char *value) +{ + unsigned int pr_len; + + *value='\n'; + + pr_len = romvec->pv_nodeops->no_proplen(node, nd_prop); + if(pr_len > MAX_PR_LEN) + { +#ifdef DEBUG_PROMOPS + printk("Bad pr_len in promops -- node: %d nd_prop: %s pr_len: %d", + node, nd_prop, pr_len); +#endif + return value; /* XXX */ + } + + romvec->pv_nodeops->no_getprop(node, nd_prop, value); + value[pr_len] = 0; + + return value; +} + +/* This gets called from head.S upon bootup to initialize the + * prom vector pointer for the rest of the kernel. + */ + +void +init_prom(struct linux_romvec *r_ptr) +{ + romvec = r_ptr; + prom_node_root = romvec->pv_nodeops->no_nextnode(0); + prom_int_null = 0; + + return; +} diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c new file mode 100644 index 000000000..5ec5b56ba --- /dev/null +++ b/arch/sparc/kernel/setup.c @@ -0,0 +1,120 @@ +/* + * linux/arch/alpha/kernel/setup.c + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#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 <asm/segment.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/openprom.h> /* for console registration + cheese */ + +extern void get_idprom(void); +extern void probe_devices(void); + +/* + * Gcc is hard to keep happy ;-) + */ +struct screen_info screen_info = { + 0, 0, /* orig-x, orig-y */ + { 0, 0, }, /* unused */ + 0, /* orig-video-page */ + 0, /* orig-video-mode */ + 80, /* orig-video-cols */ + 0,0,0, /* ega_ax, ega_bx, ega_cx */ + 25 /* orig-video-lines */ +}; + +/* At least I hide the sneaky floppy_track_buffer in my dirty assembly + * code. ;-) + */ + +unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) +{ + return memory_start; +} + +/* Lame prom console routines, gets registered below. Thanks for the + * tip Linus. First comes the V0 prom routine, then the V3 version + * written by Paul Hatchman (paul@sfe.com.au). + */ + +void sparc_console_print(const char * p) +{ + unsigned char c; + + while ((c = *(p++)) != 0) + { + if (c == '\n') romvec->pv_putchar('\r'); + (*(romvec->pv_putchar))(c); + } + + return; + +} + +/* paul@sfe.com.au */ +/* V3 prom console printing routines */ +void sparc_console_print_v3 (const char *p) +{ + unsigned char c; + + while ((c = *(p++)) != 0) + { + if (c == '\n') romvec->pv_v2devops.v2_dev_write + ((*romvec->pv_v2bootargs.fd_stdout), "\r", 1); + romvec->pv_v2devops.v2_dev_write + ((*romvec->pv_v2bootargs.fd_stdout), &c, 1); + } + + return; +} + + +/* This routine will in the future do all the nasty prom stuff + * to probe for the mmu type and its parameters, etc. This will + * also be where SMP things happen plus the Sparc specific memory + * physical memory probe as on the alpha. + */ + +extern void register_console(void (*proc)(const char *)); +extern unsigned int prom_iface_vers, end_of_phys_memory; + +void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + if(romvec->pv_romvers == 0) { + register_console(sparc_console_print); + } else { + register_console(sparc_console_print_v3); + }; + + printk("Sparc PROM-Console registered...\n"); + get_idprom(); /* probe_devices expects this to be done */ + probe_devices(); /* cpu/fpu, mmu probes */ + + *memory_start_p = (((unsigned long) &end)); + *memory_end_p = (((unsigned long) end_of_phys_memory)); +} + +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + return -EIO; +} diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c new file mode 100644 index 000000000..cd949e4ed --- /dev/null +++ b/arch/sparc/kernel/signal.c @@ -0,0 +1,71 @@ +/* + * linux/arch/sparc/kernel/signal.c + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> + +#include <asm/segment.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); + +/* + * atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set) +{ + unsigned long mask; + struct pt_regs * regs = (struct pt_regs *) &restart; + + mask = current->blocked; + current->blocked = set & _BLOCKABLE; + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(mask,regs)) + return -EINTR; + } +} + +asmlinkage int sys_sigreturn(unsigned long __unused) +{ + halt(); + return 0; +} + +/* + * 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) +{ + halt(); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ +asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) +{ + halt(); + return 1; +} diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c new file mode 100644 index 000000000..9302191c7 --- /dev/null +++ b/arch/sparc/kernel/traps.c @@ -0,0 +1,47 @@ +/* + * arch/sparc/kernel/traps.c + * + * Copyright 1994 David S. Miller (davem@caip.rutgers.edu) + */ + +/* + * I hate traps on the sparc, grrr... + */ + +#include <linux/sched.h> /* for jiffies */ +#include <linux/kernel.h> + +void do_hw_interrupt(unsigned long type, unsigned long vector) +{ + if (vector == 14) { + jiffies++; + return; + } + + /* Just print garbage for everything else for now. */ + + printk("Unimplemented Sparc TRAP, vector = %lx type = %lx\n", vector, type); + + return; +} + +extern unsigned long *trapbase; + +void trap_init(void) +{ + + /* load up the trap table */ + +#if 0 /* not yet */ + __asm__("wr %0, 0x0, %%tbr\n\t" + "nop; nop; nop\n\t" : : + "r" (trapbase)); +#endif + + return; +} + +void die_if_kernel(char * str, struct pt_regs * regs, long err) +{ + return; +} diff --git a/arch/sparc/lib/COPYING.LIB b/arch/sparc/lib/COPYING.LIB new file mode 100644 index 000000000..eb685a5ec --- /dev/null +++ b/arch/sparc/lib/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile new file mode 100644 index 000000000..1f2ce0e1c --- /dev/null +++ b/arch/sparc/lib/Makefile @@ -0,0 +1,48 @@ +# +# Makefile for Sparc library files.. +# + +CFLAGS := $(CFLAGS) -ansi + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< + +OBJS = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o + +lib.a: $(OBJS) + $(AR) rcs lib.a $(OBJS) + sync + +mul.o: mul.S + $(CC) -c -o mul.o mul.S + +rem.o: rem.S + $(CC) -DST_DIV0=0x2 -c -o rem.o rem.S + +sdiv.o: sdiv.S + $(CC) -DST_DIV0=0x2 -c -o sdiv.o sdiv.S + +udiv.o: udiv.S + $(CC) -DST_DIV0=0x2 -c -o udiv.o udiv.S + +umul.o: umul.S + $(CC) -c -o umul.o umul.S + +urem.o: urem.S + $(CC) -DST_DIV0=0x2 -c -o urem.o urem.S + +ashrdi3.o: ashrdi3.S + $(CC) -c -o ashrdi3.o ashrdi3.S + +dep: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/arch/sparc/lib/ashrdi3.S b/arch/sparc/lib/ashrdi3.S new file mode 100644 index 000000000..c672d2c9f --- /dev/null +++ b/arch/sparc/lib/ashrdi3.S @@ -0,0 +1,28 @@ +/* ashrdi3.S: The filesystem code creates all kinds of references to + * this little routine on the sparc with gcc. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <asm/cprefix.h> + + .globl C_LABEL(__ashrdi3) +C_LABEL(__ashrdi3): + tst %o2 + be 3f + or %g0, 32, %g2 + sub %g2, %o2, %g2 + tst %g2 + bg 1f + sra %o0, %o2, %o4 + sra %o0, 31, %o4 + sub %g0, %g2, %g2 + ba 2f + sra %o0, %g2, %o5 +1: sll %o0, %g2, %g3 + srl %o1, %o2, %g2 + or %g2, %g3, %o5 +2: or %g0, %o4, %o0 + or %g0, %o5, %o1 +3: jmpl %o7 + 8, %g0 + nop diff --git a/arch/sparc/lib/mul.S b/arch/sparc/lib/mul.S new file mode 100644 index 000000000..e6d78f85f --- /dev/null +++ b/arch/sparc/lib/mul.S @@ -0,0 +1,127 @@ +/* mul.S: This routine was taken from glibc-1.09 and is covered + * by the GNU Library General Public License Version 2. + */ + +/* + * Signed multiply, from Appendix E of the Sparc Version 8 + * Architecture Manual. + */ + +/* + * Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the upper 32 bits of + * the 64-bit product). + * + * This code optimizes short (less than 13-bit) multiplies. + */ + + .globl .mul +.mul: + mov %o0, %y ! multiplier -> Y + andncc %o0, 0xfff, %g0 ! test bits 12..31 + be Lmul_shortway ! if zero, can do it the short way + andcc %g0, %g0, %o4 ! zero the partial product and clear N and V + + /* + * Long multiply. 32 steps, followed by a final shift step. + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %o1, %o4 ! 13 + mulscc %o4, %o1, %o4 ! 14 + mulscc %o4, %o1, %o4 ! 15 + mulscc %o4, %o1, %o4 ! 16 + mulscc %o4, %o1, %o4 ! 17 + mulscc %o4, %o1, %o4 ! 18 + mulscc %o4, %o1, %o4 ! 19 + mulscc %o4, %o1, %o4 ! 20 + mulscc %o4, %o1, %o4 ! 21 + mulscc %o4, %o1, %o4 ! 22 + mulscc %o4, %o1, %o4 ! 23 + mulscc %o4, %o1, %o4 ! 24 + mulscc %o4, %o1, %o4 ! 25 + mulscc %o4, %o1, %o4 ! 26 + mulscc %o4, %o1, %o4 ! 27 + mulscc %o4, %o1, %o4 ! 28 + mulscc %o4, %o1, %o4 ! 29 + mulscc %o4, %o1, %o4 ! 30 + mulscc %o4, %o1, %o4 ! 31 + mulscc %o4, %o1, %o4 ! 32 + mulscc %o4, %g0, %o4 ! final shift + + ! If %o0 was negative, the result is + ! (%o0 * %o1) + (%o1 << 32)) + ! We fix that here. + +#if 0 + tst %o0 + bge 1f + rd %y, %o0 + + ! %o0 was indeed negative; fix upper 32 bits of result by subtracting + ! %o1 (i.e., return %o4 - %o1 in %o1). + retl + sub %o4, %o1, %o1 + +1: + retl + mov %o4, %o1 +#else + /* Faster code adapted from tege@sics.se's code for umul.S. */ + sra %o0, 31, %o2 ! make mask from sign bit + and %o1, %o2, %o2 ! %o2 = 0 or %o1, depending on sign of %o0 + rd %y, %o0 ! get lower half of product + retl + sub %o4, %o2, %o1 ! subtract compensation + ! and put upper half in place +#endif + +Lmul_shortway: + /* + * Short multiply. 12 steps, followed by a final shift step. + * The resulting bits are off by 12 and (32-12) = 20 bit positions, + * but there is no problem with %o0 being negative (unlike above). + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %g0, %o4 ! final shift + + /* + * %o4 has 20 of the bits that should be in the low part of the + * result; %y has the bottom 12 (as %y's top 12). That is: + * + * %o4 %y + * +----------------+----------------+ + * | -12- | -20- | -12- | -20- | + * +------(---------+------)---------+ + * --hi-- ----low-part---- + * + * The upper 12 bits of %o4 should be sign-extended to form the + * high part of the product (i.e., highpart = %o4 >> 20). + */ + + rd %y, %o5 + sll %o4, 12, %o0 ! shift middle bits left 12 + srl %o5, 20, %o5 ! shift low bits right 20, zero fill at left + or %o5, %o0, %o0 ! construct low part of result + retl + sra %o4, 20, %o1 ! ... and extract high part of result diff --git a/arch/sparc/lib/rem.S b/arch/sparc/lib/rem.S new file mode 100644 index 000000000..3c0cc579b --- /dev/null +++ b/arch/sparc/lib/rem.S @@ -0,0 +1,359 @@ +/* rem.S: This routine was taken from glibc-1.09 and is covered + * by the GNU Library General Public License Version 2. + */ + + +/* This file is generated from divrem.m4; DO NOT EDIT! */ +/* + * Division and remainder, from Appendix E of the Sparc Version 8 + * Architecture Manual, with fixes from Gordon Irlam. + */ + +/* + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * m4 parameters: + * .rem name of function to generate + * rem rem=div => %o0 / %o1; rem=rem => %o0 % %o1 + * true true=true => signed; true=false => unsigned + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TOPBITS number of bits in the top decade of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ + + + .globl .rem +.rem: + ! compute sign of result; if neither is negative, no problem + orcc %o1, %o0, %g0 ! either negative? + bge 2f ! no, go do the divide + xor %o1, %o0, %g6 ! compute sign in any case + tst %o1 + bge 1f + tst %o0 + ! %o1 is definitely negative; %o0 might also be negative + bge 2f ! if %o0 not negative... + sub %g0, %o1, %o1 ! in any case, make %o1 nonneg +1: ! %o0 is negative, %o1 is nonnegative + sub %g0, %o0, %o0 ! make %o0 nonnegative +2: + + ! Ready to divide. Compute size of quotient; scale comparand. + orcc %o1, %g0, %o5 + bne 1f + mov %o0, %o3 + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + ta ST_DIV0 + retl + clr %o0 + +1: + cmp %o3, %o5 ! if %o1 exceeds %o0, done + blu Lgot_result ! (and algorithm fails otherwise) + clr %o2 + sethi %hi(1 << (32 - 4 - 1)), %g1 + cmp %o3, %g1 + blu Lnot_really_big + clr %o4 + + ! Here the dividend is >= 2**(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R. + 1: + cmp %o5, %g1 + bgeu 3f + mov 1, %g7 + sll %o5, 4, %o5 + b 1b + add %o4, 1, %o4 + + ! Now compute %g7. + 2: addcc %o5, %o5, %o5 + bcc Lnot_too_big + add %g7, 1, %g7 + + ! We get here if the %o1 overflowed while shifting. + ! This means that %o3 has the high-order bit set. + ! Restore %o5 and subtract from %o3. + sll %g1, 4, %g1 ! high order bit + srl %o5, 1, %o5 ! rest of %o5 + add %o5, %g1, %o5 + b Ldo_single_div + sub %g7, 1, %g7 + + Lnot_too_big: + 3: cmp %o5, %o3 + blu 2b + nop + be Ldo_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! %o5 > %o3: went too far: back up 1 step + ! srl %o5, 1, %o5 + ! dec %g7 + ! do single-bit divide steps + ! + ! We have to be careful here. We know that %o3 >= %o5, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + Ldo_single_div: + subcc %g7, 1, %g7 + bl Lend_regular_divide + nop + sub %o3, %o5, %o3 + mov 1, %o2 + b Lend_single_divloop + nop + Lsingle_divloop: + sll %o2, 1, %o2 + bl 1f + srl %o5, 1, %o5 + ! %o3 >= 0 + sub %o3, %o5, %o3 + b 2f + add %o2, 1, %o2 + 1: ! %o3 < 0 + add %o3, %o5, %o3 + sub %o2, 1, %o2 + 2: + Lend_single_divloop: + subcc %g7, 1, %g7 + bge Lsingle_divloop + tst %o3 + b,a Lend_regular_divide + +Lnot_really_big: +1: + sll %o5, 4, %o5 + cmp %o5, %o3 + bleu 1b + addcc %o4, 1, %o4 + be Lgot_result + sub %o4, 1, %o4 + + tst %o3 ! set up for initial iteration +Ldivloop: + sll %o2, 4, %o2 + ! depth 1, accumulated bits 0 + bl L.1.16 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 2, accumulated bits 1 + bl L.2.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits 3 + bl L.3.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 7 + bl L.4.23 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (7*2+1), %o2 + +L.4.23: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (7*2-1), %o2 + + +L.3.19: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 5 + bl L.4.21 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (5*2+1), %o2 + +L.4.21: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (5*2-1), %o2 + + + +L.2.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits 1 + bl L.3.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 3 + bl L.4.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (3*2+1), %o2 + +L.4.19: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (3*2-1), %o2 + + +L.3.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 1 + bl L.4.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (1*2+1), %o2 + +L.4.17: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (1*2-1), %o2 + + + + +L.1.16: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 2, accumulated bits -1 + bl L.2.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits -1 + bl L.3.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -1 + bl L.4.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2+1), %o2 + +L.4.15: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2-1), %o2 + + +L.3.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -3 + bl L.4.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2+1), %o2 + +L.4.13: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2-1), %o2 + + + +L.2.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits -3 + bl L.3.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -5 + bl L.4.11 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2+1), %o2 + +L.4.11: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2-1), %o2 + + +L.3.13: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -7 + bl L.4.9 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2+1), %o2 + +L.4.9: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2-1), %o2 + + + + + 9: +Lend_regular_divide: + subcc %o4, 1, %o4 + bge Ldivloop + tst %o3 + bl,a Lgot_result + ! non-restoring fixup here (one instruction only!) + add %o3, %o1, %o3 + + +Lgot_result: + + retl + mov %o3, %o0 diff --git a/arch/sparc/lib/sdiv.S b/arch/sparc/lib/sdiv.S new file mode 100644 index 000000000..2fa7a9794 --- /dev/null +++ b/arch/sparc/lib/sdiv.S @@ -0,0 +1,363 @@ +/* sdiv.S: This routine was taken from glibc-1.09 and is covered + * by the GNU Library General Public License Version 2. + */ + + +/* This file is generated from divrem.m4; DO NOT EDIT! */ +/* + * Division and remainder, from Appendix E of the Sparc Version 8 + * Architecture Manual, with fixes from Gordon Irlam. + */ + +/* + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * m4 parameters: + * .div name of function to generate + * div div=div => %o0 / %o1; div=rem => %o0 % %o1 + * true true=true => signed; true=false => unsigned + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TOPBITS number of bits in the top decade of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ + + + .globl .div +.div: + ! compute sign of result; if neither is negative, no problem + orcc %o1, %o0, %g0 ! either negative? + bge 2f ! no, go do the divide + xor %o1, %o0, %g6 ! compute sign in any case + tst %o1 + bge 1f + tst %o0 + ! %o1 is definitely negative; %o0 might also be negative + bge 2f ! if %o0 not negative... + sub %g0, %o1, %o1 ! in any case, make %o1 nonneg +1: ! %o0 is negative, %o1 is nonnegative + sub %g0, %o0, %o0 ! make %o0 nonnegative +2: + + ! Ready to divide. Compute size of quotient; scale comparand. + orcc %o1, %g0, %o5 + bne 1f + mov %o0, %o3 + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + ta ST_DIV0 + retl + clr %o0 + +1: + cmp %o3, %o5 ! if %o1 exceeds %o0, done + blu Lgot_result ! (and algorithm fails otherwise) + clr %o2 + sethi %hi(1 << (32 - 4 - 1)), %g1 + cmp %o3, %g1 + blu Lnot_really_big + clr %o4 + + ! Here the dividend is >= 2**(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R. + 1: + cmp %o5, %g1 + bgeu 3f + mov 1, %g7 + sll %o5, 4, %o5 + b 1b + add %o4, 1, %o4 + + ! Now compute %g7. + 2: addcc %o5, %o5, %o5 + bcc Lnot_too_big + add %g7, 1, %g7 + + ! We get here if the %o1 overflowed while shifting. + ! This means that %o3 has the high-order bit set. + ! Restore %o5 and subtract from %o3. + sll %g1, 4, %g1 ! high order bit + srl %o5, 1, %o5 ! rest of %o5 + add %o5, %g1, %o5 + b Ldo_single_div + sub %g7, 1, %g7 + + Lnot_too_big: + 3: cmp %o5, %o3 + blu 2b + nop + be Ldo_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! %o5 > %o3: went too far: back up 1 step + ! srl %o5, 1, %o5 + ! dec %g7 + ! do single-bit divide steps + ! + ! We have to be careful here. We know that %o3 >= %o5, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + Ldo_single_div: + subcc %g7, 1, %g7 + bl Lend_regular_divide + nop + sub %o3, %o5, %o3 + mov 1, %o2 + b Lend_single_divloop + nop + Lsingle_divloop: + sll %o2, 1, %o2 + bl 1f + srl %o5, 1, %o5 + ! %o3 >= 0 + sub %o3, %o5, %o3 + b 2f + add %o2, 1, %o2 + 1: ! %o3 < 0 + add %o3, %o5, %o3 + sub %o2, 1, %o2 + 2: + Lend_single_divloop: + subcc %g7, 1, %g7 + bge Lsingle_divloop + tst %o3 + b,a Lend_regular_divide + +Lnot_really_big: +1: + sll %o5, 4, %o5 + cmp %o5, %o3 + bleu 1b + addcc %o4, 1, %o4 + be Lgot_result + sub %o4, 1, %o4 + + tst %o3 ! set up for initial iteration +Ldivloop: + sll %o2, 4, %o2 + ! depth 1, accumulated bits 0 + bl L.1.16 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 2, accumulated bits 1 + bl L.2.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits 3 + bl L.3.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 7 + bl L.4.23 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (7*2+1), %o2 + +L.4.23: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (7*2-1), %o2 + + +L.3.19: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 5 + bl L.4.21 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (5*2+1), %o2 + +L.4.21: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (5*2-1), %o2 + + + +L.2.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits 1 + bl L.3.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 3 + bl L.4.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (3*2+1), %o2 + +L.4.19: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (3*2-1), %o2 + + +L.3.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 1 + bl L.4.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (1*2+1), %o2 + +L.4.17: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (1*2-1), %o2 + + + + +L.1.16: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 2, accumulated bits -1 + bl L.2.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits -1 + bl L.3.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -1 + bl L.4.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2+1), %o2 + +L.4.15: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2-1), %o2 + + +L.3.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -3 + bl L.4.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2+1), %o2 + +L.4.13: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2-1), %o2 + + + +L.2.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits -3 + bl L.3.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -5 + bl L.4.11 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2+1), %o2 + +L.4.11: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2-1), %o2 + + +L.3.13: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -7 + bl L.4.9 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2+1), %o2 + +L.4.9: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2-1), %o2 + + + + + 9: +Lend_regular_divide: + subcc %o4, 1, %o4 + bge Ldivloop + tst %o3 + bl,a Lgot_result + ! non-restoring fixup here (one instruction only!) + sub %o2, 1, %o2 + + +Lgot_result: + ! check to see if answer should be < 0 + tst %g6 + bl,a 1f + sub %g0, %o2, %o2 +1: + retl + mov %o2, %o0 diff --git a/arch/sparc/lib/udiv.S b/arch/sparc/lib/udiv.S new file mode 100644 index 000000000..53cfeac90 --- /dev/null +++ b/arch/sparc/lib/udiv.S @@ -0,0 +1,346 @@ +/* udiv.S: This routine was taken from glibc-1.09 and is covered + * by the GNU Library General Public License Version 2. + */ + + +/* This file is generated from divrem.m4; DO NOT EDIT! */ +/* + * Division and remainder, from Appendix E of the Sparc Version 8 + * Architecture Manual, with fixes from Gordon Irlam. + */ + +/* + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * m4 parameters: + * .udiv name of function to generate + * div div=div => %o0 / %o1; div=rem => %o0 % %o1 + * false false=true => signed; false=false => unsigned + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TOPBITS number of bits in the top decade of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ + + + .globl .udiv +.udiv: + + ! Ready to divide. Compute size of quotient; scale comparand. + orcc %o1, %g0, %o5 + bne 1f + mov %o0, %o3 + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + ta ST_DIV0 + retl + clr %o0 + +1: + cmp %o3, %o5 ! if %o1 exceeds %o0, done + blu Lgot_result ! (and algorithm fails otherwise) + clr %o2 + sethi %hi(1 << (32 - 4 - 1)), %g1 + cmp %o3, %g1 + blu Lnot_really_big + clr %o4 + + ! Here the dividend is >= 2**(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R. + 1: + cmp %o5, %g1 + bgeu 3f + mov 1, %g7 + sll %o5, 4, %o5 + b 1b + add %o4, 1, %o4 + + ! Now compute %g7. + 2: addcc %o5, %o5, %o5 + bcc Lnot_too_big + add %g7, 1, %g7 + + ! We get here if the %o1 overflowed while shifting. + ! This means that %o3 has the high-order bit set. + ! Restore %o5 and subtract from %o3. + sll %g1, 4, %g1 ! high order bit + srl %o5, 1, %o5 ! rest of %o5 + add %o5, %g1, %o5 + b Ldo_single_div + sub %g7, 1, %g7 + + Lnot_too_big: + 3: cmp %o5, %o3 + blu 2b + nop + be Ldo_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! %o5 > %o3: went too far: back up 1 step + ! srl %o5, 1, %o5 + ! dec %g7 + ! do single-bit divide steps + ! + ! We have to be careful here. We know that %o3 >= %o5, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + Ldo_single_div: + subcc %g7, 1, %g7 + bl Lend_regular_divide + nop + sub %o3, %o5, %o3 + mov 1, %o2 + b Lend_single_divloop + nop + Lsingle_divloop: + sll %o2, 1, %o2 + bl 1f + srl %o5, 1, %o5 + ! %o3 >= 0 + sub %o3, %o5, %o3 + b 2f + add %o2, 1, %o2 + 1: ! %o3 < 0 + add %o3, %o5, %o3 + sub %o2, 1, %o2 + 2: + Lend_single_divloop: + subcc %g7, 1, %g7 + bge Lsingle_divloop + tst %o3 + b,a Lend_regular_divide + +Lnot_really_big: +1: + sll %o5, 4, %o5 + cmp %o5, %o3 + bleu 1b + addcc %o4, 1, %o4 + be Lgot_result + sub %o4, 1, %o4 + + tst %o3 ! set up for initial iteration +Ldivloop: + sll %o2, 4, %o2 + ! depth 1, accumulated bits 0 + bl L.1.16 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 2, accumulated bits 1 + bl L.2.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits 3 + bl L.3.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 7 + bl L.4.23 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (7*2+1), %o2 + +L.4.23: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (7*2-1), %o2 + + +L.3.19: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 5 + bl L.4.21 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (5*2+1), %o2 + +L.4.21: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (5*2-1), %o2 + + + +L.2.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits 1 + bl L.3.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 3 + bl L.4.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (3*2+1), %o2 + +L.4.19: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (3*2-1), %o2 + + +L.3.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 1 + bl L.4.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (1*2+1), %o2 + +L.4.17: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (1*2-1), %o2 + + + + +L.1.16: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 2, accumulated bits -1 + bl L.2.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits -1 + bl L.3.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -1 + bl L.4.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2+1), %o2 + +L.4.15: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2-1), %o2 + + +L.3.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -3 + bl L.4.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2+1), %o2 + +L.4.13: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2-1), %o2 + + + +L.2.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits -3 + bl L.3.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -5 + bl L.4.11 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2+1), %o2 + +L.4.11: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2-1), %o2 + + +L.3.13: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -7 + bl L.4.9 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2+1), %o2 + +L.4.9: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2-1), %o2 + + + + + 9: +Lend_regular_divide: + subcc %o4, 1, %o4 + bge Ldivloop + tst %o3 + bl,a Lgot_result + ! non-restoring fixup here (one instruction only!) + sub %o2, 1, %o2 + + +Lgot_result: + + retl + mov %o2, %o0 diff --git a/arch/sparc/lib/umul.S b/arch/sparc/lib/umul.S new file mode 100644 index 000000000..24f7c3cda --- /dev/null +++ b/arch/sparc/lib/umul.S @@ -0,0 +1,158 @@ +/* umul.S: This routine was taken from glibc-1.09 and is covered + * by the GNU Library General Public License Version 2. + */ + + +/* + * Unsigned multiply. Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the + * upper 32 bits of the 64-bit product). + * + * This code optimizes short (less than 13-bit) multiplies. Short + * multiplies require 25 instruction cycles, and long ones require + * 45 instruction cycles. + * + * On return, overflow has occurred (%o1 is not zero) if and only if + * the Z condition code is clear, allowing, e.g., the following: + * + * call .umul + * nop + * bnz overflow (or tnz) + */ + + .globl .umul +.umul: + or %o0, %o1, %o4 + mov %o0, %y ! multiplier -> Y + andncc %o4, 0xfff, %g0 ! test bits 12..31 of *both* args + be Lmul_shortway ! if zero, can do it the short way + andcc %g0, %g0, %o4 ! zero the partial product and clear N and V + + /* + * Long multiply. 32 steps, followed by a final shift step. + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %o1, %o4 ! 13 + mulscc %o4, %o1, %o4 ! 14 + mulscc %o4, %o1, %o4 ! 15 + mulscc %o4, %o1, %o4 ! 16 + mulscc %o4, %o1, %o4 ! 17 + mulscc %o4, %o1, %o4 ! 18 + mulscc %o4, %o1, %o4 ! 19 + mulscc %o4, %o1, %o4 ! 20 + mulscc %o4, %o1, %o4 ! 21 + mulscc %o4, %o1, %o4 ! 22 + mulscc %o4, %o1, %o4 ! 23 + mulscc %o4, %o1, %o4 ! 24 + mulscc %o4, %o1, %o4 ! 25 + mulscc %o4, %o1, %o4 ! 26 + mulscc %o4, %o1, %o4 ! 27 + mulscc %o4, %o1, %o4 ! 28 + mulscc %o4, %o1, %o4 ! 29 + mulscc %o4, %o1, %o4 ! 30 + mulscc %o4, %o1, %o4 ! 31 + mulscc %o4, %o1, %o4 ! 32 + mulscc %o4, %g0, %o4 ! final shift + + + /* + * Normally, with the shift-and-add approach, if both numbers are + * positive you get the correct result. With 32-bit two's-complement + * numbers, -x is represented as + * + * x 32 + * ( 2 - ------ ) mod 2 * 2 + * 32 + * 2 + * + * (the `mod 2' subtracts 1 from 1.bbbb). To avoid lots of 2^32s, + * we can treat this as if the radix point were just to the left + * of the sign bit (multiply by 2^32), and get + * + * -x = (2 - x) mod 2 + * + * Then, ignoring the `mod 2's for convenience: + * + * x * y = xy + * -x * y = 2y - xy + * x * -y = 2x - xy + * -x * -y = 4 - 2x - 2y + xy + * + * For signed multiplies, we subtract (x << 32) from the partial + * product to fix this problem for negative multipliers (see mul.s). + * Because of the way the shift into the partial product is calculated + * (N xor V), this term is automatically removed for the multiplicand, + * so we don't have to adjust. + * + * But for unsigned multiplies, the high order bit wasn't a sign bit, + * and the correction is wrong. So for unsigned multiplies where the + * high order bit is one, we end up with xy - (y << 32). To fix it + * we add y << 32. + */ +#if 0 + tst %o1 + bl,a 1f ! if %o1 < 0 (high order bit = 1), + add %o4, %o0, %o4 ! %o4 += %o0 (add y to upper half) +1: rd %y, %o0 ! get lower half of product + retl + addcc %o4, %g0, %o1 ! put upper half in place and set Z for %o1==0 +#else + /* Faster code from tege@sics.se. */ + sra %o1, 31, %o2 ! make mask from sign bit + and %o0, %o2, %o2 ! %o2 = 0 or %o0, depending on sign of %o1 + rd %y, %o0 ! get lower half of product + retl + addcc %o4, %o2, %o1 ! add compensation and put upper half in place +#endif + +Lmul_shortway: + /* + * Short multiply. 12 steps, followed by a final shift step. + * The resulting bits are off by 12 and (32-12) = 20 bit positions, + * but there is no problem with %o0 being negative (unlike above), + * and overflow is impossible (the answer is at most 24 bits long). + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %g0, %o4 ! final shift + + /* + * %o4 has 20 of the bits that should be in the result; %y has + * the bottom 12 (as %y's top 12). That is: + * + * %o4 %y + * +----------------+----------------+ + * | -12- | -20- | -12- | -20- | + * +------(---------+------)---------+ + * -----result----- + * + * The 12 bits of %o4 left of the `result' area are all zero; + * in fact, all top 20 bits of %o4 are zero. + */ + + rd %y, %o5 + sll %o4, 12, %o0 ! shift middle bits left 12 + srl %o5, 20, %o5 ! shift low bits right 20 + or %o5, %o0, %o0 + retl + addcc %g0, %g0, %o1 ! %o1 = zero, and set Z diff --git a/arch/sparc/lib/urem.S b/arch/sparc/lib/urem.S new file mode 100644 index 000000000..c84aa81e5 --- /dev/null +++ b/arch/sparc/lib/urem.S @@ -0,0 +1,344 @@ +/* urem.S: This routine was taken from glibc-1.09 and is covered + * by the GNU Library General Public License Version 2. + */ + +/* This file is generated from divrem.m4; DO NOT EDIT! */ +/* + * Division and remainder, from Appendix E of the Sparc Version 8 + * Architecture Manual, with fixes from Gordon Irlam. + */ + +/* + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * m4 parameters: + * .urem name of function to generate + * rem rem=div => %o0 / %o1; rem=rem => %o0 % %o1 + * false false=true => signed; false=false => unsigned + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TOPBITS number of bits in the top decade of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ + + .globl .urem +.urem: + + ! Ready to divide. Compute size of quotient; scale comparand. + orcc %o1, %g0, %o5 + bne 1f + mov %o0, %o3 + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + ta ST_DIV0 + retl + clr %o0 + +1: + cmp %o3, %o5 ! if %o1 exceeds %o0, done + blu Lgot_result ! (and algorithm fails otherwise) + clr %o2 + sethi %hi(1 << (32 - 4 - 1)), %g1 + cmp %o3, %g1 + blu Lnot_really_big + clr %o4 + + ! Here the dividend is >= 2**(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R. + 1: + cmp %o5, %g1 + bgeu 3f + mov 1, %g7 + sll %o5, 4, %o5 + b 1b + add %o4, 1, %o4 + + ! Now compute %g7. + 2: addcc %o5, %o5, %o5 + bcc Lnot_too_big + add %g7, 1, %g7 + + ! We get here if the %o1 overflowed while shifting. + ! This means that %o3 has the high-order bit set. + ! Restore %o5 and subtract from %o3. + sll %g1, 4, %g1 ! high order bit + srl %o5, 1, %o5 ! rest of %o5 + add %o5, %g1, %o5 + b Ldo_single_div + sub %g7, 1, %g7 + + Lnot_too_big: + 3: cmp %o5, %o3 + blu 2b + nop + be Ldo_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! %o5 > %o3: went too far: back up 1 step + ! srl %o5, 1, %o5 + ! dec %g7 + ! do single-bit divide steps + ! + ! We have to be careful here. We know that %o3 >= %o5, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + Ldo_single_div: + subcc %g7, 1, %g7 + bl Lend_regular_divide + nop + sub %o3, %o5, %o3 + mov 1, %o2 + b Lend_single_divloop + nop + Lsingle_divloop: + sll %o2, 1, %o2 + bl 1f + srl %o5, 1, %o5 + ! %o3 >= 0 + sub %o3, %o5, %o3 + b 2f + add %o2, 1, %o2 + 1: ! %o3 < 0 + add %o3, %o5, %o3 + sub %o2, 1, %o2 + 2: + Lend_single_divloop: + subcc %g7, 1, %g7 + bge Lsingle_divloop + tst %o3 + b,a Lend_regular_divide + +Lnot_really_big: +1: + sll %o5, 4, %o5 + cmp %o5, %o3 + bleu 1b + addcc %o4, 1, %o4 + be Lgot_result + sub %o4, 1, %o4 + + tst %o3 ! set up for initial iteration +Ldivloop: + sll %o2, 4, %o2 + ! depth 1, accumulated bits 0 + bl L.1.16 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 2, accumulated bits 1 + bl L.2.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits 3 + bl L.3.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 7 + bl L.4.23 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (7*2+1), %o2 + +L.4.23: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (7*2-1), %o2 + + +L.3.19: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 5 + bl L.4.21 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (5*2+1), %o2 + +L.4.21: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (5*2-1), %o2 + + + +L.2.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits 1 + bl L.3.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 3 + bl L.4.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (3*2+1), %o2 + +L.4.19: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (3*2-1), %o2 + + +L.3.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 1 + bl L.4.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (1*2+1), %o2 + +L.4.17: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (1*2-1), %o2 + + + + +L.1.16: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 2, accumulated bits -1 + bl L.2.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits -1 + bl L.3.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -1 + bl L.4.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2+1), %o2 + +L.4.15: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2-1), %o2 + + +L.3.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -3 + bl L.4.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2+1), %o2 + +L.4.13: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2-1), %o2 + + + +L.2.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits -3 + bl L.3.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -5 + bl L.4.11 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2+1), %o2 + +L.4.11: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2-1), %o2 + + +L.3.13: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -7 + bl L.4.9 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2+1), %o2 + +L.4.9: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2-1), %o2 + + + + + 9: +Lend_regular_divide: + subcc %o4, 1, %o4 + bge Ldivloop + tst %o3 + bl,a Lgot_result + ! non-restoring fixup here (one instruction only!) + add %o3, %o1, %o3 + + +Lgot_result: + + retl + mov %o3, %o0 diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile new file mode 100644 index 000000000..a4148d013 --- /dev/null +++ b/arch/sparc/mm/Makefile @@ -0,0 +1,32 @@ +# +# Makefile for the linux Sparc-specific parts of the memory manager. +# +# 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 definition is now in the main makefile... + +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) -S $< + +OBJS = fault.o vac-flush.o init.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 diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c new file mode 100644 index 000000000..4c5fd0bc3 --- /dev/null +++ b/arch/sparc/mm/fault.c @@ -0,0 +1,173 @@ +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/signal.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/openprom.h> +#include <asm/page.h> +#include <asm/pgtable.h> + +extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */ +extern struct sparc_phys_banks sp_banks[14]; + +extern void die_if_kernel(char *,struct pt_regs *,long); + +struct linux_romvec *romvec; + +/* foo */ + +int tbase_needs_unmapping; + +/* At boot time we determine these two values necessary for setting + * up the segment maps and page table entries (pte's). + */ + +int num_segmaps, num_contexts; +int invalid_segment; + +/* various Virtual Address Cache parameters we find at boot time... */ + +int vac_size, vac_linesize, vac_do_hw_vac_flushes; +int vac_entries_per_context, vac_entries_per_segment; +int vac_entries_per_page; + +/* + * Define this if things work differently on a i386 and a i486: + * it will (on a i486) warn about kernel memory accesses that are + * done without a 'verify_area(VERIFY_WRITE,..)' + */ +#undef CONFIG_TEST_VERIFY_AREA + +/* Traverse the memory lists in the prom to see how much physical we + * have. + */ + +unsigned long +probe_memory(void) +{ + register struct linux_romvec *lprom; + register struct linux_mlist_v0 *mlist; + register unsigned long bytes, base_paddr, tally; + register int i; + + bytes = tally = 0; + base_paddr = 0; + i=0; + lprom = romvec; + switch(lprom->pv_romvers) + { + case 0: + mlist=(*(lprom->pv_v0mem.v0_totphys)); + bytes = tally = mlist->num_bytes; + base_paddr = (unsigned long) mlist->start_adr; + + sp_banks[0].base_addr = base_paddr; + sp_banks[0].num_bytes = bytes; + + if(mlist->theres_more != (void *)0) { + i++; + mlist=mlist->theres_more; + bytes=mlist->num_bytes; + tally += bytes; + sp_banks[i].base_addr = (unsigned long) mlist->start_adr; + sp_banks[i].num_bytes = mlist->num_bytes; + } + break; + case 2: + printk("no v2 memory probe support yet.\n"); + (*(lprom->pv_halt))(); + break; + } + + i++; + sp_banks[i].base_addr = 0xdeadbeef; + sp_banks[i].num_bytes = 0; + + return tally; +} + +/* Sparc routine to reserve the mapping of the open boot prom */ + +/* uncomment this for FAME and FORTUNE! */ +/* #define DEBUG_MAP_PROM */ + +int +map_the_prom(int curr_num_segs) +{ + register unsigned long prom_va_begin; + register unsigned long prom_va_end; + register int segmap_entry, i; + + prom_va_begin = LINUX_OPPROM_BEGVM; + prom_va_end = LINUX_OPPROM_ENDVM; + +#ifdef DEBUG_MAP_PROM + printk("\ncurr_num_segs = 0x%x\n", curr_num_segs); +#endif + + while( prom_va_begin < prom_va_end) + { + segmap_entry=get_segmap(prom_va_begin); + + curr_num_segs = ((segmap_entry<curr_num_segs) + ? segmap_entry : curr_num_segs); + + for(i = num_contexts; --i > 0;) + (*romvec->pv_setctxt)(i, (char *) prom_va_begin, + segmap_entry); + + if(segmap_entry == invalid_segment) + { + +#ifdef DEBUG_MAP_PROM + printk("invalid_segments, virt_addr 0x%x\n", prom_va_begin); +#endif + + prom_va_begin += 0x40000; /* num bytes per segment entry */ + continue; + } + + /* DUH, prom maps itself so that users can access it. This is + * broken. + */ + +#ifdef DEBUG_MAP_PROM + printk("making segmap for prom privileged, va = 0x%x\n", + prom_va_begin); +#endif + + for(i = 0x40; --i >= 0; prom_va_begin+=4096) + { + put_pte(prom_va_begin, get_pte(prom_va_begin) | 0x20000000); + } + + } + + printk("Mapped the PROM in all contexts...\n"); + +#ifdef DEBUG_MAP_PROM + printk("curr_num_segs = 0x%x\n", curr_num_segs); +#endif + + return curr_num_segs; + +} + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + die_if_kernel("Oops", regs, error_code); + do_exit(SIGKILL); +} + + + + diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c new file mode 100644 index 000000000..a65e9e094 --- /dev/null +++ b/arch/sparc/mm/init.c @@ -0,0 +1,364 @@ +/* + * linux/arch/sparc/mm/init.c + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/vac-ops.h> +#include <asm/page.h> +#include <asm/pgtable.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); + +extern int map_the_prom(int); + +struct sparc_phys_banks sp_banks[14]; +unsigned long *sun4c_mmu_table; +extern int invalid_segment, num_segmaps, num_contexts; + +/* + * 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 + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +pte_t *__bad_pagetable(void) +{ + memset((void *) EMPTY_PGT, 0, PAGE_SIZE); + return (pte_t *) EMPTY_PGT; +} + +pte_t __bad_page(void) +{ + memset((void *) EMPTY_PGE, 0, PAGE_SIZE); + return pte_mkdirty(mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED)); +} + +unsigned long __zero_page(void) +{ + memset((void *) ZERO_PGE, 0, PAGE_SIZE); + return ZERO_PGE; +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = high_memory >> PAGE_SHIFT; + while (i-- > 0) { + total++; + if (mem_map[i] & MAP_PAGE_RESERVED) + reserved++; + else if (!mem_map[i]) + free++; + else + shared += mem_map[i]-1; + } + printk("%d pages of RAM\n",total); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + show_buffers(); +#ifdef CONFIG_NET + show_net_buffers(); +#endif +} + +extern unsigned long free_area_init(unsigned long, unsigned long); + +/* + * paging_init() sets up the page tables: in the alpha version this actually + * unmaps the bootup page table (as we're now in KSEG, so we don't need it). + * + * The bootup sequence put the virtual page table into high memory: that + * means that we can change the L1 page table by just using VL1p below. + */ + +unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) +{ + unsigned long i, a, b, mask=0; + unsigned long curseg, curpte, num_inval; + unsigned long address; + pte_t *pg_table; + + register int num_segs, num_ctx; + register char * c; + + num_segs = num_segmaps; + num_ctx = num_contexts; + + num_segs -= 1; + invalid_segment = num_segs; + + start_mem = free_area_init(start_mem, end_mem); + +/* On the sparc we first need to allocate the segmaps for the + * PROM's virtual space, and make those segmaps unusable. We + * map the PROM in ALL contexts therefore the break key and the + * sync command work no matter what state you took the machine + * out of + */ + + printk("mapping the prom...\n"); + num_segs = map_the_prom(num_segs); + + start_mem = PAGE_ALIGN(start_mem); + + /* Set up static page tables in kernel space, this will be used + * so that the low-level page fault handler can fill in missing + * TLB entries since all mmu entries cannot be loaded at once + * on the sun4c. + */ + +#if 0 + /* ugly debugging code */ + for(i=0; i<40960; i+=PAGE_SIZE) + printk("address=0x%x vseg=%d pte=0x%x\n", (unsigned int) i, + (int) get_segmap(i), (unsigned int) get_pte(i)); +#endif + + printk("Setting up kernel static mmu table... bounce bounce\n"); + + address = 0; /* ((unsigned long) &end) + 524288; */ + sun4c_mmu_table = (unsigned long *) start_mem; + pg_table = (pte_t *) start_mem; + curseg = curpte = num_inval = 0; + while(address < end_mem) { + if(curpte == 0) + put_segmap((address&PGDIR_MASK), curseg); + for(i=0; sp_banks[i].num_bytes != 0; i++) + if((address >= sp_banks[i].base_addr) && + (address <= (sp_banks[i].base_addr + sp_banks[i].num_bytes))) + goto good_address; + /* No physical memory here, so set the virtual segment to + * the invalid one, and put an invalid pte in the static + * kernel table. + */ + *pg_table = mk_pte((address >> PAGE_SHIFT), PAGE_INVALID); + pg_table++; curpte++; num_inval++; + if(curpte > 63) { + if(curpte == num_inval) { + put_segmap((address&PGDIR_MASK), invalid_segment); + } else { + put_segmap((address&PGDIR_MASK), curseg); + curseg++; + } + curpte = num_inval = 0; + } + address += PAGE_SIZE; + continue; + + good_address: + /* create pte entry */ + if(address < (((unsigned long) &end) + 524288)) { + pte_val(*pg_table) = get_pte(address); + } else { + *pg_table = mk_pte((address >> PAGE_SHIFT), PAGE_KERNEL); + put_pte(address, pte_val(*pg_table)); + } + + pg_table++; curpte++; + if(curpte > 63) { + put_segmap((address&PGDIR_MASK), curseg); + curpte = num_inval = 0; + curseg++; + } + address += PAGE_SIZE; + } + + start_mem = (unsigned long) pg_table; + /* ok, allocate the kernel pages, map them in all contexts + * (with help from the prom), and lock them. Isn't the sparc + * fun kiddies? TODO + */ + +#if 0 + /* ugly debugging code */ + for(i=0x1a3000; i<(0x1a3000+40960); i+=PAGE_SIZE) + printk("address=0x%x vseg=%d pte=0x%x\n", (unsigned int) i, + (int) get_segmap(i), (unsigned int) get_pte(i)); + halt(); +#endif + + b=PGDIR_ALIGN(start_mem)>>18; + c= (char *)0x0; + + printk("mapping kernel in all contexts...\n"); + + for(a=0; a<b; a++) + { + for(i=0; i<num_contexts; i++) + { + /* map the kernel virt_addrs */ + (*(romvec->pv_setctxt))(i, (char *) c, a); + } + c += 0x40000; + } + + /* Ok, since now mapped in all contexts, we can free up + * context zero to be used amongst user processes. + */ + + /* free context 0 here TODO */ + + /* invalidate all user pages and initialize the pte struct + * for userland. TODO + */ + + /* Make the kernel text unwritable and cacheable, the prom + * loaded our text as writable, only sneaky sunos kernels need + * self-modifying code. + */ + + a= (unsigned long) &etext; + mask=~(PTE_NC|PTE_W); /* make cacheable + not writable */ + + /* must do for every segment since kernel uses all contexts + * and unlike some sun kernels I know of, we can't hard wire + * context 0 just for the kernel, that is unnecessary. + */ + + for(i=0; i<8; i++) + { + b=PAGE_ALIGN((unsigned long) &trapbase); + + switch_to_context(i); + + for(;b<a; b+=4096) + { + put_pte(b, (get_pte(b) & mask)); + } + } + + invalidate(); /* flush the virtual address cache */ + + printk("\nCurrently in context - "); + for(i=0; i<num_contexts; i++) + { + switch_to_context(i); + printk("%d ", (int) i); + } + printk("\n"); + + switch_to_context(0); + + invalidate(); + return start_mem; +} + +void mem_init(unsigned long start_mem, unsigned long end_mem) +{ + unsigned long start_low_mem = PAGE_SIZE; + int codepages = 0; + int reservedpages = 0; + int datapages = 0; + int i = 0; + unsigned long tmp, limit, tmp2, addr; + extern char etext; + + end_mem &= PAGE_MASK; + high_memory = end_mem; + + start_low_mem = PAGE_ALIGN(start_low_mem); + start_mem = PAGE_ALIGN(start_mem); + + for(i = 0; sp_banks[i].num_bytes != 0; i++) { + tmp = sp_banks[i].base_addr; + limit = (sp_banks[i].base_addr + sp_banks[i].num_bytes); + if(tmp<start_mem) { + if(limit>start_mem) + tmp = start_mem; + else continue; + } + + while(tmp<limit) { + mem_map[MAP_NR(tmp)] = 0; + tmp += PAGE_SIZE; + } + if(sp_banks[i+1].num_bytes != 0) + while(tmp < sp_banks[i+1].base_addr) { + mem_map[MAP_NR(tmp)] = MAP_PAGE_RESERVED; + tmp += PAGE_SIZE; + } + } + +#ifdef CONFIG_SCSI + scsi_mem_init(high_memory); +#endif + + for (addr = 0; addr < high_memory; addr += PAGE_SIZE) { + if(mem_map[MAP_NR(addr)]) { + if (addr < (unsigned long) &etext) + codepages++; + else if(addr < start_mem) + datapages++; + else + reservedpages++; + continue; + } + mem_map[MAP_NR(addr)] = 1; + free_page(addr); + } + + tmp2 = nr_free_pages << PAGE_SHIFT; + + printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n", + tmp2 >> 10, + high_memory >> 10, + codepages << (PAGE_SHIFT-10), + reservedpages << (PAGE_SHIFT-10), + datapages << (PAGE_SHIFT-10)); + + invalidate(); + return; +} + +void si_meminfo(struct sysinfo *val) +{ + int i; + + i = high_memory >> PAGE_SHIFT; + 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) + continue; + val->totalram++; + if (!mem_map[i]) + continue; + val->sharedram += mem_map[i]-1; + } + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + return; +} diff --git a/arch/sparc/mm/vac-flush.c b/arch/sparc/mm/vac-flush.c new file mode 100644 index 000000000..796366b53 --- /dev/null +++ b/arch/sparc/mm/vac-flush.c @@ -0,0 +1,94 @@ +/* vac.c: Routines for flushing various amount of the Sparc VAC + (virtual address cache). + + Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) +*/ + +#include <asm/vac-ops.h> +#include <asm/page.h> + +/* Flush all VAC entries for the current context */ + +extern int vac_do_hw_vac_flushes, vac_size, vac_linesize; +extern int vac_entries_per_context, vac_entries_per_segment; +extern int vac_entries_per_page; + +void +flush_vac_context() +{ + register int entries_left, offset; + register char* address; + + entries_left = vac_entries_per_context; + address = (char *) 0; + + if(vac_do_hw_vac_flushes) + { + while(entries_left-- >=0) + { + hw_flush_vac_context_entry(address); + address += PAGE_SIZE; + } + } + else + { + offset = vac_linesize; + while(entries_left-- >=0) + { + sw_flush_vac_context_entry(address); + address += offset; + } + } +} + +void +flush_vac_segment(register unsigned int segment) +{ + register int entries_left, offset; + register char* address = (char *) 0; + + entries_left = vac_entries_per_segment; + __asm__ __volatile__("sll %0, 18, %1\n\t" + "sra %1, 0x2, %1\n\t" + : "=r" (segment) : "0" (address)); + + if(vac_do_hw_vac_flushes) + { + while(entries_left-- >=0) + { + hw_flush_vac_segment_entry(address); + address += PAGE_SIZE; + } + } + else + { + offset = vac_linesize; + while(entries_left-- >=0) + { + sw_flush_vac_segment_entry(address); + address += offset; + } + } +} + +void +flush_vac_page(register unsigned int addr) +{ + register int entries_left, offset; + + if(vac_do_hw_vac_flushes) + { + hw_flush_vac_page_entry((unsigned long *) addr); + } + else + { + entries_left = vac_entries_per_page; + offset = vac_linesize; + while(entries_left-- >=0) + { + sw_flush_vac_page_entry((unsigned long *) addr); + addr += offset; + } + } +} + |