diff options
Diffstat (limited to 'arch/alpha')
-rw-r--r-- | arch/alpha/Makefile | 74 | ||||
-rw-r--r-- | arch/alpha/boot/Makefile | 53 | ||||
-rw-r--r-- | arch/alpha/boot/head.S | 76 | ||||
-rw-r--r-- | arch/alpha/boot/main.c | 217 | ||||
-rw-r--r-- | arch/alpha/boot/tools/build.c | 173 | ||||
-rw-r--r-- | arch/alpha/config.in | 95 | ||||
-rw-r--r-- | arch/alpha/kernel/Makefile | 47 | ||||
-rw-r--r-- | arch/alpha/kernel/bios32.c | 478 | ||||
-rw-r--r-- | arch/alpha/kernel/entry.S | 554 | ||||
-rw-r--r-- | arch/alpha/kernel/head.S | 97 | ||||
-rw-r--r-- | arch/alpha/kernel/irq.c | 414 | ||||
-rw-r--r-- | arch/alpha/kernel/lca.c | 304 | ||||
-rw-r--r-- | arch/alpha/kernel/osf_sys.c | 494 | ||||
-rw-r--r-- | arch/alpha/kernel/process.c | 186 | ||||
-rw-r--r-- | arch/alpha/kernel/setup.c | 172 | ||||
-rw-r--r-- | arch/alpha/kernel/signal.c | 319 | ||||
-rw-r--r-- | arch/alpha/kernel/traps.c | 159 | ||||
-rw-r--r-- | arch/alpha/lib/Makefile | 38 | ||||
-rw-r--r-- | arch/alpha/lib/divide.S | 160 | ||||
-rw-r--r-- | arch/alpha/lib/io.c | 98 | ||||
-rw-r--r-- | arch/alpha/lib/memcpy.c | 74 | ||||
-rw-r--r-- | arch/alpha/lib/memset.c | 44 | ||||
-rw-r--r-- | arch/alpha/mm/Makefile | 32 | ||||
-rw-r--r-- | arch/alpha/mm/fault.c | 100 | ||||
-rw-r--r-- | arch/alpha/mm/init.c | 204 |
25 files changed, 4550 insertions, 112 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; +} |