diff -u --recursive --new-file v2.1.47/linux/CREDITS linux/CREDITS --- v2.1.47/linux/CREDITS Thu Jul 17 10:06:03 1997 +++ linux/CREDITS Thu Jul 31 13:09:16 1997 @@ -335,6 +335,15 @@ S: 4850 Moresnet S: Belgium +N: Cort Dougan +E: cort@cs.nmt.edu +W: http://www.cs.nmt.edu/~cort/ +D: PowerPC PReP port +S: Computer Science Department +S: New Mexico Tech +S: Socorro NM 87801 +S: USA + N: Thomas Dunbar E: tdunbar@vtaix.cc.vt.edu D: TeX & METAFONT hacking/maintenance @@ -911,6 +920,14 @@ N: Peter MacDonald D: SLS distribution D: Initial implementation of VC's, pty's and select() + +N: Paul Mackerras +E: paulus@cs.anu.edu.au +D: Linux port for PCI Power Macintosh +S: Dept. of Computer Science +S: Australian National University +S: Canberra ACT 0200 +S: AUSTRALIA N: James B. MacLean E: macleajb@ednet.ns.ca diff -u --recursive --new-file v2.1.47/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.47/linux/Documentation/Configure.help Thu Jul 17 10:06:03 1997 +++ linux/Documentation/Configure.help Sun Aug 3 19:39:57 1997 @@ -1672,20 +1672,62 @@ want to compile it as a module, say M here and read Documentation/modules.txt. -Adaptec AHA274X/284X/294X support +Adaptec AIC7xxx support (includes 274x/284x/294x) CONFIG_SCSI_AIC7XXX Information about this SCSI host adapter is contained in drivers/scsi/README.aic7xxx and in the SCSI-HOWTO, available via ftp - (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it - doesn't work out of the box, you may have to change some settings in - drivers/scsi/aic7xxx.h. It has been reported that the "wide - negotiation" on these cards is not quite working and should be - disabled. Note that the AHA2920 SCSI host adapter is *not* supported - by this driver; choose "Future Domain 16xx SCSI support" instead. If - you want to compile this driver a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called aic7xxx.o. + (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that + the AHA2920 SCSI host adapter is *not* supported by this driver; choose + "Future Domain 16xx SCSI support" instead. If you want to compile this + driver as module ( = code which can be inserted in and removed from the + running kernel whenever you want), say M here and read Documentation/ + modules.txt. The module will be called aic7xxx.o. + +Enable tagged command queueing +CONFIG_AIC7XXX_TAGGED_QUEUEING + This option allows you to enable tagged command queueing for this + driver. Some scsi devices do not properly support this + feature. Tagged command queueing will improve performance. + +Maximum number of commands per LUN + CONFIG_AIC7XXX_CMDS_PER_LUN + This option allows you to set the maximum number of commands queued + per LUN. If tagged queueing is enabled, then you may want to try + increasing AIC7XXX_CMDS_PER_LUN to more than 2. By default, we limit + the SCBs per LUN to 2 with or without tagged queueing enabled. If + tagged queueing is disabled, the sequencer will keep the 2nd SCB in + the input queue until the first one completes - so it is OK to to have + more than 1 SCB queued. If tagged queueing is enabled, then the + sequencer will attempt to send the 2nd SCB to the device while the + first SCB is executing and the device is disconnected. For adapters + limited to 4 SCBs, you may want to actually decrease the commands per + LUN to 1, if you often have more than 2 devices active at the same + time. This will allocate 1 SCB for each device and ensure that there + will always be a free SCB for up to 4 devices active at the same time. + When SCB paging is enabled, set the commands per LUN to 8 or higher + (see SCB paging support below). Note that if AIC7XXX_CMDS_PER_LUN is + not defined and tagged queueing is enabled, the driver will attempt to + set the commands per LUN using its own heuristic based on the number + of available SCBs. + +Enable SCB paging +CONFIG_AIC7XXX_PAGE_ENABLE + This option enables SCB paging. This will increase performance when + tagged queueing is enabled. Note that you should increase the + AIC7XXX_CMDS_PER_LUN to 8 as most tagged queueing devices allow at + least this many. Note that EISA and VLB controllers do not support + SCB paging due to chip limitations; enabling it on these controllers + has no effect. + +Collect statistics to report in /proc +CONFIG_AIC7XXX_PROC_STATS + This option enables collection of SCSI transfer statistics for the + /proc filesystem. This does affect performance since it has to + maintain statistics. + +Delay in seconds after SCSI bus reset +CONFIG_AIC7XXX_RESET_DELAY + This option sets the delay in seconds after a SCSI bus reset. BusLogic SCSI support CONFIG_SCSI_BUSLOGIC @@ -4645,6 +4687,18 @@ fashion. (They should also read Documentation/smp.) If you think you have a use for such a device (such as periodic data sampling), then say Y here, and go read the file Documentation/rtc.txt for details. + +/dev/nvram support +CONFIG_NVRAM + If you say Y here and create a character special file /dev/nvram + with major number 10 and minor number 144 using mknod ("man mknod"), + you get access to the non-volatile memory in the real time clock + (RTC). This is conventionally called "CMOS RAM" on PCs and "NVRAM" + on Ataris. /dev/nvram may be used to view settings there, or to + change them (with some utility). It could also be used to frequently + save a few bits of very important data, that may not be lost over + power-off and for which writing to disk is too insecure. On Atari + machines, /dev/nvram is always configured and needs not be selected. ARC console time CONFIG_RTC_ARC diff -u --recursive --new-file v2.1.47/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt --- v2.1.47/linux/Documentation/ioctl-number.txt Thu Jun 26 12:33:36 1997 +++ linux/Documentation/ioctl-number.txt Fri Jul 25 11:11:17 1997 @@ -1,5 +1,5 @@ Ioctl Numbers -10 Apr 1997 +25 Jul 1997 Michael Chastain @@ -119,3 +119,5 @@ 0xA0 all Small Device Project in development: +0xA3 90-9F DoubleTalk driver in development: + diff -u --recursive --new-file v2.1.47/linux/MAINTAINERS linux/MAINTAINERS --- v2.1.47/linux/MAINTAINERS Thu Jul 17 10:06:03 1997 +++ linux/MAINTAINERS Thu Jul 31 13:09:16 1997 @@ -452,6 +452,18 @@ W: http://www.cage.curtin.edu.au/~campbell/parbus/ S: Maintained +LINUX FOR POWERPC (PREP) +P: Cort Dougan +M: cort@cs.nmt.edu +W: http://www.cs.nmt.edu/~linuxppc/ +S: Maintained + +LINUX FOR POWER MACINTOSH +P: Paul Mackerras +M: paulus@cs.anu.edu.au +L: linux-pmac@samba.anu.edu.au +S: Maintained + FPU EMULATOR P: Bill Metzenthen M: billm@suburbia.net diff -u --recursive --new-file v2.1.47/linux/Makefile linux/Makefile --- v2.1.47/linux/Makefile Sun Jul 27 12:11:00 1997 +++ linux/Makefile Sun Aug 3 19:39:57 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 47 +SUBLEVEL = 48 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) @@ -335,7 +335,6 @@ mrproper: clean rm -f include/linux/autoconf.h include/linux/version.h rm -f drivers/sound/local.h drivers/sound/.defines - rm -f drivers/scsi/aic7xxx_asm drivers/scsi/aic7xxx_seq.h rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash rm -f drivers/net/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h rm -f drivers/net/soundmodem/sm_tbl_{hapn4800,psk4800}.h diff -u --recursive --new-file v2.1.47/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c --- v2.1.47/linux/arch/alpha/kernel/ptrace.c Sat May 24 09:10:22 1997 +++ linux/arch/alpha/kernel/ptrace.c Mon Aug 4 14:37:19 1997 @@ -650,7 +650,7 @@ goto out; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); /* * this isn't the same as continuing with a signal, but it will do diff -u --recursive --new-file v2.1.47/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c --- v2.1.47/linux/arch/alpha/kernel/signal.c Wed Apr 23 19:01:14 1997 +++ linux/arch/alpha/kernel/signal.c Mon Aug 4 14:37:32 1997 @@ -315,7 +315,7 @@ if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); single_stepping |= ptrace_cancel_bpt(current); if (!(signr = current->exit_code)) @@ -354,7 +354,7 @@ current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); single_stepping |= ptrace_cancel_bpt(current); continue; diff -u --recursive --new-file v2.1.47/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S --- v2.1.47/linux/arch/i386/boot/setup.S Sun Jul 27 12:11:00 1997 +++ linux/arch/i386/boot/setup.S Fri Jul 25 18:52:51 1997 @@ -272,8 +272,8 @@ ! and fall into the old memory detection code to populate the ! compatability slot. - pop ebx oldstylemem: + pop ebx #endif mov ah,#0x88 int 0x15 diff -u --recursive --new-file v2.1.47/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.1.47/linux/arch/i386/defconfig Sun Jul 27 12:11:00 1997 +++ linux/arch/i386/defconfig Thu Jul 31 14:57:56 1997 @@ -139,6 +139,7 @@ # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set +# CONFIG_SCSI_PPA is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set @@ -171,6 +172,7 @@ # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set +# CONFIG_PLIP is not set # CONFIG_PPP is not set # CONFIG_NET_RADIO is not set # CONFIG_SLIP is not set @@ -234,6 +236,7 @@ # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set +# CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set # diff -u --recursive --new-file v2.1.47/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v2.1.47/linux/arch/i386/kernel/ptrace.c Thu May 15 16:48:01 1997 +++ linux/arch/i386/kernel/ptrace.c Mon Aug 4 13:57:54 1997 @@ -577,7 +577,7 @@ return; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); /* * this isn't the same as continuing with a signal, but it will do diff -u --recursive --new-file v2.1.47/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c --- v2.1.47/linux/arch/i386/kernel/signal.c Thu May 15 16:48:01 1997 +++ linux/arch/i386/kernel/signal.c Mon Aug 4 13:58:13 1997 @@ -346,7 +346,7 @@ if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); if (!(signr = current->exit_code)) continue; @@ -386,7 +386,7 @@ current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); continue; diff -u --recursive --new-file v2.1.47/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.1.47/linux/arch/i386/kernel/smp.c Mon Jun 16 16:35:53 1997 +++ linux/arch/i386/kernel/smp.c Thu Jul 31 13:09:16 1997 @@ -156,6 +156,9 @@ volatile unsigned long smp_proc_in_lock[NR_CPUS] = {0,};/* for computing process time */ volatile int smp_process_available=0; +const char lk_lockmsg[] = "lock from interrupt context at %p\n"; + + /*#define SMP_DEBUG*/ #ifdef SMP_DEBUG diff -u --recursive --new-file v2.1.47/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c --- v2.1.47/linux/arch/i386/mm/init.c Tue May 13 22:41:01 1997 +++ linux/arch/i386/mm/init.c Wed Jul 30 16:17:21 1997 @@ -209,7 +209,7 @@ pgd_val(pg_dir[0]) = 0; /* Map whole memory from 0xC0000000 */ - + pg_dir += 768; while (address < end_mem) { /* * If we're running on a Pentium CPU, we can use the 4MB @@ -224,13 +224,13 @@ set_in_cr4(X86_CR4_PSE); wp_works_ok = 1; - __pe = _PAGE_TABLE + _PAGE_4M + __pa(address); + __pe = _KERNPG_TABLE + _PAGE_4M + __pa(address); /* Make it "global" too if supported */ if (x86_capability & X86_FEATURE_PGE) { set_in_cr4(X86_CR4_PGE); __pe += _PAGE_GLOBAL; } - pgd_val(pg_dir[768]) = _PAGE_TABLE + _PAGE_4M + __pa(address); + pgd_val(*pg_dir) = __pe; pg_dir++; address += 4*1024*1024; continue; @@ -239,13 +239,13 @@ * We're on a [34]86, use normal page tables. * pg_table is physical at this point */ - pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768])); + pg_table = (pte_t *) (PAGE_MASK & pgd_val(*pg_dir)); if (!pg_table) { pg_table = (pte_t *) __pa(start_mem); start_mem += PAGE_SIZE; } - pgd_val(pg_dir[768]) = _PAGE_TABLE | (unsigned long) pg_table; + pgd_val(*pg_dir) = _PAGE_TABLE | (unsigned long) pg_table; pg_dir++; /* now change pg_table to kernel virtual addresses */ pg_table = (pte_t *) __va(pg_table); diff -u --recursive --new-file v2.1.47/linux/arch/m68k/Makefile linux/arch/m68k/Makefile --- v2.1.47/linux/arch/m68k/Makefile Sat May 24 09:10:22 1997 +++ linux/arch/m68k/Makefile Thu Jul 31 13:09:16 1997 @@ -79,8 +79,6 @@ SUBDIRS := $(SUBDIRS) arch/m68k/ifpsp060 endif -MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot - lilo: vmlinux if [ -f $(INSTALL_PATH)/vmlinux ]; then mv -f $(INSTALL_PATH)/vmlinux $(INSTALL_PATH)/vmlinux.old; fi if [ -f $(INSTALL_PATH)/System.map ]; then mv -f $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi @@ -106,7 +104,5 @@ archclean: rm -f vmlinux.gz - @$(MAKEBOOT) clean archdep: - $(MAKEBOOT) dep diff -u --recursive --new-file v2.1.47/linux/arch/m68k/amiga/amikeyb.c linux/arch/m68k/amiga/amikeyb.c --- v2.1.47/linux/arch/m68k/amiga/amikeyb.c Mon Jun 16 16:35:53 1997 +++ linux/arch/m68k/amiga/amikeyb.c Thu Jul 31 13:09:16 1997 @@ -195,7 +195,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) { - unsigned char scancode, break_flag; + unsigned char scancode, break_flag, keycode; static int reset_warning = 0; /* save frame for register dump */ @@ -207,6 +207,7 @@ /* switch SP pin to output for handshake */ ciaa.cra |= 0x40; +#if 0 // No longer used /* * On receipt of the second RESET_WARNING, we must not pull KDAT high * again to delay the hard reset as long as possible. @@ -222,6 +223,7 @@ } else /* Probably a mistake, cancel the alert */ reset_warning = 0; +#endif /* wait until 85 us have expired */ udelay(85); @@ -237,27 +239,27 @@ * Check make/break first */ break_flag = scancode & BREAK_MASK; - scancode &= (unsigned char )~BREAK_MASK; + keycode = scancode & (unsigned char)~BREAK_MASK; - if (scancode == AMIKEY_CAPS) { + if (keycode == AMIKEY_CAPS) { /* if the key is CAPS, fake a press/release. */ handle_scancode(AMIKEY_CAPS); handle_scancode(BREAK_MASK | AMIKEY_CAPS); - } else if (scancode < 0x78) { + } else if (keycode < 0x78) { /* handle repeat */ if (break_flag) { del_timer(&amikeyb_rep_timer); rep_scancode = 0; } else { del_timer(&amikeyb_rep_timer); - rep_scancode = scancode; + rep_scancode = keycode; amikeyb_rep_timer.expires = jiffies + key_repeat_delay; amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; add_timer(&amikeyb_rep_timer); } - handle_scancode(break_flag | scancode); + handle_scancode(scancode); } else - switch (scancode) { + switch (keycode) { case 0x78: reset_warning = 1; break; @@ -288,7 +290,7 @@ #endif default: printk(KERN_WARNING "amikeyb: unknown keyboard communication code 0x%02x\n", - break_flag | scancode); + scancode); break; } } diff -u --recursive --new-file v2.1.47/linux/arch/m68k/amiga/zorro.c linux/arch/m68k/amiga/zorro.c --- v2.1.47/linux/arch/m68k/amiga/zorro.c Tue May 13 22:41:02 1997 +++ linux/arch/m68k/amiga/zorro.c Thu Jul 31 13:09:16 1997 @@ -730,7 +730,7 @@ { int key; struct ConfigDev *cd; - + if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(ZORRO)) return(0); diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/Makefile linux/arch/m68k/boot/Makefile --- v2.1.47/linux/arch/m68k/boot/Makefile Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/boot/Makefile Wed Dec 31 16:00:00 1969 @@ -1,65 +0,0 @@ -# -# linux/arch/m68k/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. - -ifdef CONFIG_AMIGA -AMIGA_BOOTSTRAP = amiga_bootstrap -AMIGA_BOOTOBJS := amiga/bootstrap.o amiga/linuxboot.o -AMIGA_HOSTCC = m68k-cbm-amigados-gcc -AMIGA_HOSTINC = -I$(TOPDIR)/include -AMIGA_HOSTFLAGS=-m68030 -O2 -Wall -Dlinux -endif - -ifdef CONFIG_ATARI -ATARI_BOOTSTRAP = atari_bootstrap -ATARI_BOOTOBJS := atari/bootstrap.o -ATARI_HOSTCC = m68k-mint-gcc -ATARI_HOSTINC = -I$(TOPDIR)/include -ATARI_HOSTFLAGS = -m68030 -m68881 -Dlinux -O2 -Wall - -# BOOTP/TFTP support in bootstrap? -# USE_BOOTP = y - -ifdef USE_BOOTP -ATARI_BOOTOBJS += atari/bootp.o -ATARI_HOSTFLAGS += -DUSE_BOOTP - -# low-level Ethernet drivers: - -# Lance (RieblCard, PAM-VME) -ATARI_BOOTOBJS += atari/ethlance.o -ATARI_HOSTFLAGS += -DETHLL_LANCE - -endif -endif - -ifdef CONFIG_ATARI -atari_bootstrap: $(ATARI_BOOTOBJS) - $(ATARI_HOSTCC) $(ATARI_HOSTINC) $(ATARI_HOSTFLAGS) -o $@ $(ATARI_BOOTOBJS) - rm -f ../../../bootstrap - ln $@ ../../../bootstrap -endif - -ifdef CONFIG_AMIGA -amiga_bootstrap: $(AMIGA_BOOTOBJS) - $(AMIGA_HOSTCC) $(AMIGA_HOSTINC) $(AMIGA_HOSTFLAGS) -o $@ -s -noixemul $(AMIGA_BOOTOBJS) - rm -f ../../../bootstrap - ln $@ ../../../bootstrap -endif - -$(AMIGA_BOOTOBJS): %.o: %.c - $(AMIGA_HOSTCC) $(AMIGA_HOSTINC) $(AMIGA_HOSTFLAGS) -c $< -o $@ - -$(ATARI_BOOTOBJS): %.o: %.c - $(ATARI_HOSTCC) $(ATARI_HOSTINC) $(ATARI_HOSTFLAGS) -c $< -o $@ - -bootstrap: $(AMIGA_BOOTSTRAP) $(ATARI_BOOTSTRAP) - -clean: - rm -f *.o amiga/*.o atari/*.o amiga_bootstrap atari_bootstrap \ - ../../../bootstrap - -dep: diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/amiga/bootstrap.c linux/arch/m68k/boot/amiga/bootstrap.c --- v2.1.47/linux/arch/m68k/boot/amiga/bootstrap.c Mon Jul 7 08:18:53 1997 +++ linux/arch/m68k/boot/amiga/bootstrap.c Wed Dec 31 16:00:00 1969 @@ -1,335 +0,0 @@ -/* -** linux/arch/m68k/boot/amiga/bootstrap.c -- This program loads the Linux/m68k -** kernel into an Amiga and launches -** it. -** -** Copyright 1993,1994 by Hamish Macdonald, Greg Harp -** -** Modified 11-May-94 by Geert Uytterhoeven -** (Geert.Uytterhoeven@cs.kuleuven.ac.be) -** - A3640 MapROM check -** Modified 31-May-94 by Geert Uytterhoeven -** - Memory thrash problem solved -** Modified 07-March-95 by Geert Uytterhoeven -** - Memory block sizes are rounded to a multiple of 256K instead of 1M -** This _requires_ >0.9pl5 to work! -** (unless all block sizes are multiples of 1M :-) -** Modified 11-July-95 by Andreas Schwab -** - Support for ELF kernel (untested!) -** Modified 10-Jan-96 by Geert Uytterhoeven -** - The real Linux/m68k boot code moved to linuxboot.[ch] -** Modified 9-Sep-96 by Geert Uytterhoeven -** - Rewritten option parsing -** - New parameter passing to linuxboot() (linuxboot_args) -** Modified 6-Oct-96 by Geert Uytterhoeven -** - Updated for the new boot information structure -** -** This file is subject to the terms and conditions of the GNU General Public -** License. See the file COPYING in the main directory of this archive -** for more details. -** -*/ - -#include -#include -#include -#include -#include -#include -#include - -/* required Linux/m68k include files */ -#define __KERNEL_STRICT_NAMES /* This is ugly, I know */ -#define _LINUX_POSIX_TYPES_H -#include -#include -#include -#include -#include - -/* Amiga bootstrap include files */ -#include "linuxboot.h" -#include "bootstrap.h" - - -/* Library Bases */ -long __oslibversion = 36; -extern const struct ExecBase *SysBase; - -static const char *memfile_name = NULL; - -static int model = AMI_UNKNOWN; - -static const char *ProgramName; - -struct linuxboot_args args; - - - /* - * Function Prototypes - */ - -static void Usage(void) __attribute__ ((noreturn)); -int main(int argc, char *argv[]); -static void Puts(const char *str); -static long GetChar(void); -static void PutChar(char c); -static void Printf(const char *fmt, ...); -static int Open(const char *path); -static int Seek(int fd, int offset); -static int Read(int fd, char *buf, int count); -static void Close(int fd); -static int FileSize(const char *path); -static void Sleep(u_long micros); - - -static void Usage(void) -{ - fprintf(stderr, - "Linux/m68k Amiga Bootstrap version " AMIBOOT_VERSION "\n\n" - "Usage: %s [options] [kernel command line]\n\n" - "Basic options:\n" - " -h, --help Display this usage information\n" - " -k, --kernel file Use kernel image `file' (default is `vmlinux')\n" - " -r, --ramdisk file Use ramdisk image `file'\n" - "Advanced options:\n" - " -d, --debug Enable debug mode\n" - " -b, --baud speed Set the serial port speed (default is 9600)\n" - " -m, --memfile file Use memory file `file'\n" - " -v, --keep-video Don't reset the video mode\n" - " -t, --model id Set the Amiga model to `id'\n" - " -p, --processor cfm Set the processor type to `cfm\n\n", - ProgramName); - exit(EXIT_FAILURE); -} - - -int main(int argc, char *argv[]) -{ - int i; - int processor = 0, debugflag = 0, keep_video = 0; - u_int baud = 0; - const char *kernel_name = NULL; - const char *ramdisk_name = NULL; - char commandline[CL_SIZE] = ""; - - ProgramName = argv[0]; - while (--argc) { - argv++; - if (!strcmp(argv[0], "-h") || !strcmp(argv[0], "--help")) - Usage(); - else if (!strcmp(argv[0], "-k") || !strcmp(argv[0], "--kernel")) - if (--argc && !kernel_name) { - kernel_name = argv[1]; - argv++; - } else - Usage(); - else if (!strcmp(argv[0], "-r") || !strcmp(argv[0], "--ramdisk")) - if (--argc && !ramdisk_name) { - ramdisk_name = argv[1]; - argv++; - } else - Usage(); - else if (!strcmp(argv[0], "-d") || !strcmp(argv[0], "--debug")) - debugflag = 1; - else if (!strcmp(argv[0], "-b") || !strcmp(argv[0], "--baud")) - if (--argc && !baud) { - baud = atoi(argv[1]); - argv++; - } else - Usage(); - else if (!strcmp(argv[0], "-m") || !strcmp(argv[0], "--memfile")) - if (--argc && !memfile_name) { - memfile_name = argv[1]; - argv++; - } else - Usage(); - else if (!strcmp(argv[0], "-v") || !strcmp(argv[0], "--keep-video")) - keep_video = 1; - else if (!strcmp(argv[0], "-t") || !strcmp(argv[0], "--model")) - if (--argc && !model) { - model = atoi(argv[1]); - argv++; - } else - Usage(); - else if (!strcmp(argv[0], "-p") || !strcmp(argv[0], "--processor")) - if (--argc && !processor) { - processor = atoi(argv[1]); - argv++; - } else - Usage(); - else - break; - } - if (!kernel_name) - kernel_name = "vmlinux"; - - SysBase = *(struct ExecBase **)4; - - /* - * Join command line options - */ - i = 0; - while (argc--) { - if ((i+strlen(*argv)+1) < CL_SIZE) { - i += strlen(*argv) + 1; - if (commandline[0]) - strcat(commandline, " "); - strcat(commandline, *argv++); - } - } - - memset(&args.bi, 0, sizeof(args.bi)); - if (processor) { - int cpu = processor/100%10; - int fpu = processor/10%10; - int mmu = processor%10; - if (cpu) - args.bi.cputype = 1<<(cpu-1); - if (fpu) - args.bi.fputype = 1<<(fpu-1); - if (mmu) - args.bi.mmutype = 1<<(mmu-1); - } - /* - * If we have a memory file, read the memory information from it - */ - if (memfile_name) { - FILE *fp; - int i; - - if ((fp = fopen(memfile_name, "r")) == NULL) { - perror("open memory file"); - fprintf(stderr, "Cannot open memory file %s\n", memfile_name); - return(FALSE); - } - - if (fscanf(fp, "%lu", &args.bi.chip_size) != 1) { - fprintf(stderr, "memory file does not contain chip memory size\n"); - fclose(fp); - return(FALSE); - } - - for (i = 0; i < NUM_MEMINFO; i++) - if (fscanf(fp, "%lx %lu", &args.bi.memory[i].addr, - &args.bi.memory[i].size) != 2) - break; - - fclose(fp); - args.bi.num_memory = i; - } - strncpy(args.bi.command_line, commandline, CL_SIZE); - args.bi.command_line[CL_SIZE-1] = '\0'; - if (model != AMI_UNKNOWN) - args.bi.model = model; - - args.kernelname = kernel_name; - args.ramdiskname = ramdisk_name; - args.debugflag = debugflag; - args.keep_video = keep_video; - args.reset_boards = 1; - args.baud = baud; - args.puts = Puts; - args.getchar = GetChar; - args.putchar = PutChar; - args.printf = Printf; - args.open = Open; - args.seek = Seek; - args.read = Read; - args.close = Close; - args.filesize = FileSize; - args.sleep = Sleep; - - /* Do The Right Stuff */ - linuxboot(&args); - - /* if we ever get here, something went wrong */ - exit(EXIT_FAILURE); -} - - - /* - * Routines needed by linuxboot - */ - -static void Puts(const char *str) -{ - fputs(str, stderr); - fflush(stderr); -} - -static long GetChar(void) -{ - return(getchar()); -} - -static void PutChar(char c) -{ - fputc(c, stderr); - fflush(stderr); -} - -static void Printf(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fflush(stderr); -} - -static int Open(const char *path) -{ - return(open(path, O_RDONLY)); -} - -static int Seek(int fd, int offset) -{ - return(lseek(fd, offset, SEEK_SET)); -} - - -static int Read(int fd, char *buf, int count) -{ - return(read(fd, buf, count)); -} - -static void Close(int fd) -{ - close(fd); -} - -static int FileSize(const char *path) -{ - int fd, size = -1; - - if ((fd = open(path, O_RDONLY)) != -1) { - size = lseek(fd, 0, SEEK_END); - close(fd); - } - return(size); -} - -static void Sleep(u_long micros) -{ - struct MsgPort *TimerPort; - struct timerequest *TimerRequest; - - if ((TimerPort = CreateMsgPort())) { - if ((TimerRequest = CreateIORequest(TimerPort, - sizeof(struct timerequest)))) { - if (!OpenDevice("timer.device", UNIT_VBLANK, - (struct IORequest *)TimerRequest, 0)) { - TimerRequest->io_Command = TR_ADDREQUEST; - TimerRequest->io_Flags = IOF_QUICK; - TimerRequest->tv_secs = micros/1000000; - TimerRequest->tv_micro = micros%1000000; - DoIO((struct IORequest *)TimerRequest); - CloseDevice((struct IORequest *)TimerRequest); - } - DeleteIORequest(TimerRequest); - } - DeleteMsgPort(TimerPort); - } -} diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/amiga/bootstrap.h linux/arch/m68k/boot/amiga/bootstrap.h --- v2.1.47/linux/arch/m68k/boot/amiga/bootstrap.h Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/boot/amiga/bootstrap.h Wed Dec 31 16:00:00 1969 @@ -1,149 +0,0 @@ -/* -** linux/arch/m68k/boot/amiga/bootstrap.h -- This file is part of the Amiga -** bootloader. -** -** Copyright 1993, 1994 by Hamish Macdonald -** -** Some minor additions by Michael Rausch 1-11-94 -** Modified 11-May-94 by Geert Uytterhoeven -** (Geert.Uytterhoeven@cs.kuleuven.ac.be) -** - inline Supervisor() call -** Modified 10-Jan-96 by Geert Uytterhoeven -** - The real Linux/m68k boot code moved to linuxboot.[ch] -** Modified 9-Sep-96 by Geert Uytterhoeven -** - const library bases -** - fixed register naming for m68k-cbm-amigados-gcc -** -** 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. -** -*/ - - -struct MsgPort { - u_char fill1[15]; - u_char mp_SigBit; - u_char fill2[18]; -}; - -struct IOStdReq { - u_char fill1[20]; - struct Device *io_Device; - u_char fill2[4]; - u_short io_Command; - u_char io_Flags; - char io_Error; - u_long io_Actual; - u_long io_Length; - void *io_Data; - u_char fill4[4]; -}; - -#define IOF_QUICK (1<<0) - -struct timerequest { - u_char fill1[28]; - u_short io_Command; - u_char io_Flags; - u_char fill2[1]; - u_long tv_secs; - u_long tv_micro; -}; - -#define UNIT_VBLANK 1 -#define TR_ADDREQUEST 9 - - -struct Library; -struct IORequest; - - -static __inline char OpenDevice(u_char *devName, u_long unit, - struct IORequest *ioRequest, u_long flags) -{ - register char _res __asm("d0"); - register const struct ExecBase *a6 __asm("a6") = SysBase; - register u_char *a0 __asm("a0") = devName; - register u_long d0 __asm("d0") = unit; - register struct IORequest *a1 __asm("a1") = ioRequest; - register u_long d1 __asm("d1") = flags; - - __asm __volatile ("jsr a6@(-0x1bc)" - : "=r" (_res) - : "r" (a6), "r" (a0), "r" (a1), "r" (d0), "r" (d1) - : "a0","a1","d0","d1", "memory"); - return(_res); -} - -static __inline void CloseDevice(struct IORequest *ioRequest) -{ - register const struct ExecBase *a6 __asm("a6") = SysBase; - register struct IORequest *a1 __asm("a1") = ioRequest; - - __asm __volatile ("jsr a6@(-0x1c2)" - : /* no output */ - : "r" (a6), "r" (a1) - : "a0","a1","d0","d1", "memory"); -} - -static __inline char DoIO(struct IORequest *ioRequest) -{ - register char _res __asm("d0"); - register const struct ExecBase *a6 __asm("a6") = SysBase; - register struct IORequest *a1 __asm("a1") = ioRequest; - - __asm __volatile ("jsr a6@(-0x1c8)" - : "=r" (_res) - : "r" (a6), "r" (a1) - : "a0","a1","d0","d1", "memory"); - return(_res); -} - -static __inline void *CreateIORequest(struct MsgPort *port, u_long size) -{ - register struct Library *_res __asm("d0"); - register const struct ExecBase *a6 __asm("a6") = SysBase; - register struct MsgPort *a0 __asm("a0") = port; - register u_long d0 __asm("d0") = size; - - __asm __volatile ("jsr a6@(-0x28e)" - : "=r" (_res) - : "r" (a6), "r" (a0), "r" (d0) - : "a0","a1","d0","d1", "memory"); - return(_res); -} - -static __inline void DeleteIORequest(void *ioRequest) -{ - register const struct ExecBase *a6 __asm("a6") = SysBase; - register void *a0 __asm("a0") = ioRequest; - - __asm __volatile ("jsr a6@(-0x294)" - : /* no output */ - : "r" (a6), "r" (a0) - : "a0","a1","d0","d1", "memory"); -} - -static __inline struct MsgPort *CreateMsgPort(void) -{ - register struct MsgPort *_res __asm("d0"); - register const struct ExecBase *a6 __asm("a6") = SysBase; - - __asm __volatile ("jsr a6@(-0x29a)" - : "=r" (_res) - : "r" (a6) - : "a0","a1","d0","d1", "memory"); - return(_res); -} - -static __inline void DeleteMsgPort(struct MsgPort *port) -{ - register const struct ExecBase *a6 __asm("a6") = SysBase; - register struct MsgPort *a0 __asm("a0") = port; - - __asm __volatile ("jsr a6@(-0x2a0)" - : /* no output */ - : "r" (a6), "r" (a0) - : "a0","a1","d0","d1", "memory"); -} diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/amiga/linuxboot.c linux/arch/m68k/boot/amiga/linuxboot.c --- v2.1.47/linux/arch/m68k/boot/amiga/linuxboot.c Mon Jul 7 08:18:53 1997 +++ linux/arch/m68k/boot/amiga/linuxboot.c Wed Dec 31 16:00:00 1969 @@ -1,1692 +0,0 @@ -/* - * linux/arch/m68k/boot/amiga/linuxboot.c -- Generic routine to boot Linux/m68k - * on Amiga, used by both Amiboot and - * Amiga-Lilo. - * - * Created 1996 by Geert Uytterhoeven - * - * - * This file is based on the original bootstrap code (bootstrap.c): - * - * Copyright (C) 1993, 1994 Hamish Macdonald - * Greg Harp - * - * with work by Michael Rausch - * Geert Uytterhoeven - * Frank Neumann - * Andreas Schwab - * - * - * 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. - * - * History: - * 11 Jun 1997 Fix for unpadded gzipped ramdisks with bootinfo interface - * version 1.0 - * 27 Mar 1997 FPU-less machines couldn't boot kernels that use bootinfo - * interface version 1.0 (Geert) - * 3 Feb 1997 Implemented kernel decompression (Geert, based on Roman's - * code for ataboot) - * 30 Dec 1996 Reverted the CPU detection to the old scheme - * New boot parameter override scheme (Geert) - * 27 Nov 1996 Compatibility with bootinfo interface version 1.0 (Geert) - * 9 Sep 1996 Rewritten option parsing - * New parameter passing to linuxboot() (linuxboot_args) - * (Geert) - * 18 Aug 1996 Updated for the new boot information structure (Geert) - * 10 Jan 1996 The real Linux/m68k boot code moved to linuxboot.[ch] - * (Geert) - * 11 Jul 1995 Support for ELF kernel (untested!) (Andreas) - * 7 Mar 1995 Memory block sizes are rounded to a multiple of 256K - * instead of 1M (Geert) - * 31 May 1994 Memory thrash problem solved (Geert) - * 11 May 1994 A3640 MapROM check (Geert) - */ - - -#ifndef __GNUC__ -#error GNU CC is required to compile this program -#endif /* __GNUC__ */ - - -#define BOOTINFO_COMPAT_1_0 /* bootinfo interface version 1.0 compatible */ -/* support compressed kernels? */ -#define ZKERNEL - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "linuxboot.h" - - -#undef custom -#define custom ((*(volatile struct CUSTOM *)(CUSTOM_PHYSADDR))) - -/* a.out linkage conventions */ -#undef SYMBOL_NAME_STR -#define SYMBOL_NAME_STR(X) "_"#X - -/* temporary stack size */ -#define TEMP_STACKSIZE (256) - -#define DEFAULT_BAUD (9600) - -extern char copyall, copyallend; - -static struct exec kexec; -static Elf32_Ehdr kexec_elf; -static const struct linuxboot_args *linuxboot_args; - -/* Bootinfo */ -struct amiga_bootinfo bi; - -#ifdef BOOTINFO_COMPAT_1_0 -static struct compat_bootinfo compat_bootinfo; -#endif /* BOOTINFO_COMPAT_1_0 */ - -#define MAX_BI_SIZE (4096) -static u_long bi_size; -static union { - struct bi_record record; - u_char fake[MAX_BI_SIZE]; -} bi_union; - -#define kernelname linuxboot_args->kernelname -#define ramdiskname linuxboot_args->ramdiskname -#define debugflag linuxboot_args->debugflag -#define keep_video linuxboot_args->keep_video -#define reset_boards linuxboot_args->reset_boards -#define baud linuxboot_args->baud - -#define Puts linuxboot_args->puts -#define GetChar linuxboot_args->getchar -#define PutChar linuxboot_args->putchar -#define Printf linuxboot_args->printf -#define Open linuxboot_args->open -#define Seek linuxboot_args->seek -#define Read linuxboot_args->read -#define Close linuxboot_args->close -#define FileSize linuxboot_args->filesize -#define Sleep linuxboot_args->sleep - - /* - * Function Prototypes - */ - -static u_long get_chipset(void); -static void get_processor(u_long *cpu, u_long *fpu, u_long *mmu); -static u_long get_model(u_long chipset); -static int probe_resident(const char *name); -static int probe_resource(const char *name); -static int create_bootinfo(void); -#ifdef BOOTINFO_COMPAT_1_0 -static int create_compat_bootinfo(void); -#endif /* BOOTINFO_COMPAT_1_0 */ -static int add_bi_record(u_short tag, u_short size, const void *data); -static int add_bi_string(u_short tag, const u_char *s); -static int check_bootinfo_version(const char *memptr); -static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, - u_long start_mem, u_long kernel_size, u_long rd_dest, - u_long rd_size) __attribute__ ((noreturn)); -asmlinkage u_long maprommed(void); -#ifdef ZKERNEL -static int load_zkernel(int fd); -static int KRead(int fd, void *buf, int cnt); -static int KSeek(int fd, int offset); -static int KClose(int fd); -#else -#define KRead Read -#define KSeek Seek -#define KClose Close -#endif - - - /* - * Reset functions for nasty Zorro boards - */ - -static void reset_rb3(const struct ConfigDev *cd); -static void reset_piccolo(const struct ConfigDev *cd); -static void reset_sd64(const struct ConfigDev *cd); -static void reset_ariadne(const struct ConfigDev *cd); -static void reset_hydra(const struct ConfigDev *cd); -#if 0 -static void reset_a2060(const struct ConfigDev *cd); -#endif - -struct boardreset { - u_short manuf; - u_short prod; - const char *name; - void (*reset)(const struct ConfigDev *cd); -}; - -static struct boardreset boardresetdb[] = { - { MANUF_HELFRICH1, PROD_RAINBOW3, "Rainbow 3", reset_rb3 }, - { MANUF_HELFRICH2, PROD_PICCOLO_REG, "Piccolo", reset_piccolo }, - { MANUF_HELFRICH2, PROD_SD64_REG, "SD64", reset_sd64 }, - { MANUF_VILLAGE_TRONIC, PROD_ARIADNE, "Ariadne", reset_ariadne }, - { MANUF_HYDRA_SYSTEMS, PROD_AMIGANET, "Hydra", reset_hydra }, -#if 0 - { MANUF_COMMODORE, PROD_A2060, "A2060", reset_a2060 }, -#endif -}; -#define NUM_BOARDRESET sizeof(boardresetdb)/sizeof(*boardresetdb) - -static void (*boardresetfuncs[ZORRO_NUM_AUTO])(const struct ConfigDev *cd); - - -const char *amiga_models[] = { - "Amiga 500", "Amiga 500+", "Amiga 600", "Amiga 1000", "Amiga 1200", - "Amiga 2000", "Amiga 2500", "Amiga 3000", "Amiga 3000T", "Amiga 3000+", - "Amiga 4000", "Amiga 4000T", "CDTV", "CD32", "Draco" -}; -const u_long first_amiga_model = AMI_500; -const u_long last_amiga_model = AMI_DRACO; - - -#define MASK(model) (1<bi; - - /* machine is Amiga */ - bi.machtype = MACH_AMIGA; - - /* determine chipset */ - if (!bi.chipset) - bi.chipset = get_chipset(); - - /* determine CPU, FPU and MMU type */ - if (!bi.cputype) - get_processor(&bi.cputype, &bi.fputype, &bi.mmutype); - - /* determine Amiga model */ - if (!bi.model) - bi.model = get_model(bi.chipset); - model_mask = (bi.model != AMI_UNKNOWN) ? 1<cd_BoardAddr); - - do_fast = bi.num_memory ? 0 : 1; - do_chip = bi.chip_size ? 0 : 1; - /* find out the memory in the system */ - for (mnp = (struct MemHeader *)SysBase->MemList.lh_Head; - mnp->mh_Node.ln_Succ; - mnp = (struct MemHeader *)mnp->mh_Node.ln_Succ) { - struct MemHeader mh; - - /* copy the information */ - mh = *mnp; - - /* skip virtual memory */ - if (!(mh.mh_Attributes & MEMF_PUBLIC)) - continue; - - /* if we suspect that Kickstart is shadowed in an A3000, - modify the entry to show 512K more at the top of RAM - Check first for a MapROMmed A3640 board: overwriting the - Kickstart image causes an infinite lock-up on reboot! */ - if ((mh.mh_Upper == (void *)0x07f80000) && - (model_mask & (CLASS_A3000 | CLASS_A4000))) - if ((bi.cputype & CPU_68040) && Supervisor(maprommed)) - Puts("A3640 MapROM detected.\n"); - else if (model_mask & CLASS_A3000) { - mh.mh_Upper = (void *)0x08000000; - Puts("A3000 shadowed Kickstart detected.\n"); - } - - /* if we suspect that Kickstart is zkicked, - modify the entry to show 512K more at the botton of RAM */ - if ((mh.mh_Lower == (void *)0x00280020) && - (model_mask & CLASS_ZKICK)) { - mh.mh_Lower = (void *)0x00200000; - Puts("ZKick detected.\n"); - } - - /* mask the memory limit values */ - mh.mh_Upper = (void *)((u_long)mh.mh_Upper & 0xfffff000); - mh.mh_Lower = (void *)((u_long)mh.mh_Lower & 0xfffff000); - - /* if fast memory */ - if (do_fast && mh.mh_Attributes & MEMF_FAST) { - /* set the size value to the size of this block and mask off to a - 256K increment */ - u_long size = ((u_long)mh.mh_Upper-(u_long)mh.mh_Lower)&0xfffc0000; - if (size > 0) - if (bi.num_memory < NUM_MEMINFO) { - /* record the start and size */ - bi.memory[bi.num_memory].addr = (u_long)mh.mh_Lower; - bi.memory[bi.num_memory].size = size; - /* count this block */ - bi.num_memory++; - } else - Printf("Warning: too many memory blocks. Ignoring block " - "of %ldK at 0x%08x\n", size>>10, - (u_long)mh.mh_Lower); - } else if (do_chip && mh.mh_Attributes & MEMF_CHIP) - /* if CHIP memory, record the size */ - bi.chip_size = (u_long)mh.mh_Upper; - } - - /* get info from ExecBase */ - if (!bi.vblank) - bi.vblank = SysBase->VBlankFrequency; - if (!bi.psfreq) - bi.psfreq = SysBase->PowerSupplyFrequency; - if (!bi.eclock) - bi.eclock = SysBase->ex_EClockFrequency; - - /* serial port */ - if (!bi.serper) { - realbaud = baud ? baud : DEFAULT_BAUD; - bi.serper = (5*bi.eclock+realbaud/2)/realbaud-1; - } - - /* display Amiga model */ - if (bi.model >= first_amiga_model && bi.model <= last_amiga_model) - Printf("%s ", amiga_models[bi.model-first_amiga_model]); - else - Puts("Amiga "); - - /* display the CPU type */ - Puts("CPU: "); - switch (bi.cputype) { - case CPU_68020: - Puts("68020 (Do you have an MMU?)"); - break; - case CPU_68030: - Puts("68030"); - break; - case CPU_68040: - Puts("68040"); - break; - case CPU_68060: - Puts("68060"); - break; - default: - Puts("Insufficient for Linux. Aborting...\n"); - Printf("SysBase->AttnFlags = 0x%08lx\n", SysBase->AttnFlags); - goto Fail; - } - switch (bi.fputype) { - case FPU_68881: - Puts(" with 68881 FPU"); - break; - case FPU_68882: - Puts(" with 68882 FPU"); - break; - case FPU_68040: - case FPU_68060: - Puts(" with internal FPU"); - break; - default: - Puts(" without FPU"); - break; - } - - /* display the chipset */ - switch (bi.chipset) { - case CS_STONEAGE: - Puts(", old or unknown chipset"); - break; - case CS_OCS: - Puts(", OCS"); - break; - case CS_ECS: - Puts(", ECS"); - break; - case CS_AGA: - Puts(", AGA chipset"); - break; - } - - Puts("\n\n"); - - /* display the command line */ - Printf("Command line is '%s'\n", bi.command_line); - - /* display the clock statistics */ - Printf("Vertical Blank Frequency: %ldHz\n", bi.vblank); - Printf("Power Supply Frequency: %ldHz\n", bi.psfreq); - Printf("EClock Frequency: %ldHz\n\n", bi.eclock); - - /* display autoconfig devices */ - if (bi.num_autocon) { - Printf("Found %ld AutoConfig Device%s\n", bi.num_autocon, - bi.num_autocon > 1 ? "s" : ""); - for (i = 0; i < bi.num_autocon; i++) { - Printf("Device %ld: addr = 0x%08lx", i, - (u_long)bi.autocon[i].cd_BoardAddr); - boardresetfuncs[i] = NULL; - if (reset_boards) { - manuf = bi.autocon[i].cd_Rom.er_Manufacturer; - prod = bi.autocon[i].cd_Rom.er_Product; - for (j = 0; j < NUM_BOARDRESET; j++) - if ((manuf == boardresetdb[j].manuf) && - (prod == boardresetdb[j].prod)) { - Printf(" [%s - will be reset at kernel boot time]", - boardresetdb[j].name); - boardresetfuncs[i] = boardresetdb[j].reset; - break; - } - } - PutChar('\n'); - } - } else - Puts("No AutoConfig Devices Found\n"); - - /* display memory */ - if (bi.num_memory) { - Printf("\nFound %ld Block%sof Memory\n", bi.num_memory, - bi.num_memory > 1 ? "s " : " "); - for (i = 0; i < bi.num_memory; i++) - Printf("Block %ld: 0x%08lx to 0x%08lx (%ldK)\n", i, - bi.memory[i].addr, bi.memory[i].addr+bi.memory[i].size, - bi.memory[i].size>>10); - } else { - Puts("No memory found?! Aborting...\n"); - goto Fail; - } - - /* display chip memory size */ - Printf("%ldK of CHIP memory\n", bi.chip_size>>10); - - start_mem = bi.memory[0].addr; - mem_size = bi.memory[0].size; - - /* tell us where the kernel will go */ - Printf("\nThe kernel will be located at 0x%08lx\n", start_mem); - - /* verify that there is enough Chip RAM */ - if (bi.chip_size < 512*1024) { - Puts("Not enough Chip RAM in this system. Aborting...\n"); - goto Fail; - } - - /* verify that there is enough Fast RAM */ - for (fast_total = 0, i = 0; i < bi.num_memory; i++) - fast_total += bi.memory[i].size; - if (fast_total < 2*1024*1024) { - Puts("Not enough Fast RAM in this system. Aborting...\n"); - goto Fail; - } - - /* support for ramdisk */ - if (ramdiskname) { - int size; - - if ((size = FileSize(ramdiskname)) == -1) { - Printf("Unable to find size of ramdisk file `%s'\n", ramdiskname); - goto Fail; - } - /* record ramdisk size */ - bi.ramdisk.size = size; - } else - bi.ramdisk.size = 0; - rd_size = bi.ramdisk.size; - bi.ramdisk.addr = (u_long)start_mem+mem_size-rd_size; - - /* create the bootinfo structure */ - if (!create_bootinfo()) - goto Fail; - - /* open kernel executable and read exec header */ - if ((kfd = Open(kernelname)) == -1) { - Printf("Unable to open kernel file `%s'\n", kernelname); - goto Fail; - } - if (KRead(kfd, (void *)&kexec, sizeof(kexec)) != sizeof(kexec)) { - Puts("Unable to read exec header from kernel file\n"); - goto Fail; - } - -#ifdef ZKERNEL - if (((unsigned char *)&kexec)[0] == 037 && - (((unsigned char *)&kexec)[1] == 0213 || - ((unsigned char *)&kexec)[1] == 0236)) { - /* That's a compressed kernel */ - Puts("Kernel is compressed\n"); - if (load_zkernel(kfd)) { - Puts("Decompression error -- aborting\n"); - goto Fail; - } - } -#endif - - switch (N_MAGIC(kexec)) { - case ZMAGIC: - if (debugflag) - Puts("\nLoading a.out (ZMAGIC) Linux/m68k kernel...\n"); - text_offset = N_TXTOFF(kexec); - break; - - case QMAGIC: - if (debugflag) - Puts("\nLoading a.out (QMAGIC) Linux/m68k kernel...\n"); - text_offset = sizeof(kexec); - /* the text size includes the exec header; remove this */ - kexec.a_text -= sizeof(kexec); - break; - - default: - /* Try to parse it as an ELF header */ - KSeek(kfd, 0); - if ((KRead(kfd, (void *)&kexec_elf, sizeof(kexec_elf)) == - sizeof(kexec_elf)) && - (memcmp(&kexec_elf.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0)) { - elf_kernel = 1; - if (debugflag) - Puts("\nLoading ELF Linux/m68k kernel...\n"); - /* A few plausibility checks */ - if ((kexec_elf.e_type != ET_EXEC) || - (kexec_elf.e_machine != EM_68K) || - (kexec_elf.e_version != EV_CURRENT)) { - Puts("Invalid ELF header contents in kernel\n"); - goto Fail; - } - /* Load the program headers */ - if (!(kernel_phdrs = - (Elf32_Phdr *)AllocMem(kexec_elf.e_phnum*sizeof(Elf32_Phdr), - MEMF_FAST | MEMF_PUBLIC | - MEMF_CLEAR))) { - Puts("Unable to allocate memory for program headers\n"); - goto Fail; - } - KSeek(kfd, kexec_elf.e_phoff); - if (KRead(kfd, (void *)kernel_phdrs, - kexec_elf.e_phnum*sizeof(*kernel_phdrs)) != - kexec_elf.e_phnum*sizeof(*kernel_phdrs)) { - Puts("Unable to read program headers from kernel file\n"); - goto Fail; - } - break; - } - Printf("Wrong magic number 0x%08lx in kernel header\n", - N_MAGIC(kexec)); - goto Fail; - } - - /* Load the kernel at one page after start of mem */ - start_mem += PAGE_SIZE; - mem_size -= PAGE_SIZE; - /* Align bss size to multiple of four */ - if (!elf_kernel) - kexec.a_bss = (kexec.a_bss+3) & ~3; - - /* calculate the total required amount of memory */ - if (elf_kernel) { - u_long min_addr = 0xffffffff, max_addr = 0; - for (i = 0; i < kexec_elf.e_phnum; i++) { - if (min_addr > kernel_phdrs[i].p_vaddr) - min_addr = kernel_phdrs[i].p_vaddr; - if (max_addr < kernel_phdrs[i].p_vaddr+kernel_phdrs[i].p_memsz) - max_addr = kernel_phdrs[i].p_vaddr+kernel_phdrs[i].p_memsz; - } - /* This is needed for newer linkers that include the header in - the first segment. */ - if (min_addr == 0) { - min_addr = PAGE_SIZE; - kernel_phdrs[0].p_vaddr += PAGE_SIZE; - kernel_phdrs[0].p_offset += PAGE_SIZE; - kernel_phdrs[0].p_filesz -= PAGE_SIZE; - kernel_phdrs[0].p_memsz -= PAGE_SIZE; - } - kernel_size = max_addr-min_addr; - } else - kernel_size = kexec.a_text+kexec.a_data+kexec.a_bss; - memreq = kernel_size+bi_size+rd_size; -#ifdef BOOTINFO_COMPAT_1_0 - if (sizeof(compat_bootinfo) > bi_size) - memreq = kernel_size+sizeof(compat_bootinfo)+rd_size; -#endif /* BOOTINFO_COMPAT_1_0 */ - if (!(memptr = (char *)AllocMem(memreq, MEMF_FAST | MEMF_PUBLIC | - MEMF_CLEAR))) { - Puts("Unable to allocate memory\n"); - goto Fail; - } - - /* read the text and data segments from the kernel image */ - if (elf_kernel) - for (i = 0; i < kexec_elf.e_phnum; i++) { - if (KSeek(kfd, kernel_phdrs[i].p_offset) == -1) { - Printf("Failed to seek to segment %ld\n", i); - goto Fail; - } - if (KRead(kfd, memptr+kernel_phdrs[i].p_vaddr-PAGE_SIZE, - kernel_phdrs[i].p_filesz) != kernel_phdrs[i].p_filesz) { - Printf("Failed to read segment %ld\n", i); - goto Fail; - } - } - else { - if (KSeek(kfd, text_offset) == -1) { - Puts("Failed to seek to text\n"); - goto Fail; - } - if (KRead(kfd, memptr, kexec.a_text) != kexec.a_text) { - Puts("Failed to read text\n"); - goto Fail; - } - /* data follows immediately after text */ - if (KRead(kfd, memptr+kexec.a_text, kexec.a_data) != kexec.a_data) { - Puts("Failed to read data\n"); - goto Fail; - } - } - KClose(kfd); - kfd = -1; - - /* Check kernel's bootinfo version */ - switch (check_bootinfo_version(memptr)) { - case BI_VERSION_MAJOR(AMIGA_BOOTI_VERSION): - bi_ptr = &bi_union.record; - break; - -#ifdef BOOTINFO_COMPAT_1_0 - case BI_VERSION_MAJOR(COMPAT_AMIGA_BOOTI_VERSION): - if (!create_compat_bootinfo()) - goto Fail; - bi_ptr = &compat_bootinfo; - bi_size = sizeof(compat_bootinfo); - break; -#endif /* BOOTINFO_COMPAT_1_0 */ - - default: - goto Fail; - } - - /* copy the bootinfo to the end of the kernel image */ - memcpy((void *)(memptr+kernel_size), bi_ptr, bi_size); - - if (ramdiskname) { - if ((rfd = Open(ramdiskname)) == -1) { - Printf("Unable to open ramdisk file `%s'\n", ramdiskname); - goto Fail; - } - if (Read(rfd, memptr+kernel_size+bi_size, rd_size) != rd_size) { - Puts("Failed to read ramdisk file\n"); - goto Fail; - } - Close(rfd); - rfd = -1; - } - - /* allocate temporary chip ram stack */ - if (!(stack = (u_long *)AllocMem(TEMP_STACKSIZE, MEMF_CHIP | MEMF_CLEAR))) { - Puts("Unable to allocate memory for stack\n"); - goto Fail; - } - - /* allocate chip ram for copy of startup code */ - startcodesize = ©allend-©all; - if (!(startfunc = (void (*)(void))AllocMem(startcodesize, - MEMF_CHIP | MEMF_CLEAR))) { - Puts("Unable to allocate memory for startcode\n"); - goto Fail; - } - - /* copy startup code to CHIP RAM */ - memcpy(startfunc, ©all, startcodesize); - - if (debugflag) { - if (bi.ramdisk.size) - Printf("RAM disk at 0x%08lx, size is %ldK\n", - (u_long)memptr+kernel_size+bi_size, bi.ramdisk.size>>10); - - if (elf_kernel) { - PutChar('\n'); - for (i = 0; i < kexec_elf.e_phnum; i++) - Printf("Kernel segment %ld at 0x%08lx, size %ld\n", i, - start_mem+kernel_phdrs[i].p_vaddr-PAGE_SIZE, - kernel_phdrs[i].p_memsz); - Printf("Boot info at 0x%08lx\n", start_mem+kernel_size); - } else { - Printf("\nKernel text at 0x%08lx, code size 0x%08lx\n", start_mem, - kexec.a_text); - Printf("Kernel data at 0x%08lx, data size 0x%08lx\n", - start_mem+kexec.a_text, kexec.a_data); - Printf("Kernel bss at 0x%08lx, bss size 0x%08lx\n", - start_mem+kexec.a_text+kexec.a_data, kexec.a_bss); - Printf("Boot info at 0x%08lx\n", start_mem+kernel_size); - } - Printf("\nKernel entry is 0x%08lx\n", elf_kernel ? kexec_elf.e_entry : - kexec.a_entry); - - Printf("ramdisk dest is 0x%08lx\n", bi.ramdisk.addr); - Printf("ramdisk lower limit is 0x%08lx\n", - (u_long)memptr+kernel_size+bi_size); - Printf("ramdisk src top is 0x%08lx\n", - (u_long)memptr+kernel_size+bi_size+rd_size); - - Puts("\nType a key to continue the Linux/m68k boot..."); - GetChar(); - PutChar('\n'); - } - - /* wait for things to settle down */ - Sleep(1000000); - - if (!keep_video) - /* set graphics mode to a nice normal one */ - LoadView(NULL); - - Disable(); - - /* reset nasty Zorro boards */ - if (reset_boards) - for (i = 0; i < bi.num_autocon; i++) - if (boardresetfuncs[i]) - boardresetfuncs[i](&bi.autocon[i]); - - /* Turn off all DMA */ - custom.dmacon = DMAF_ALL | DMAF_MASTER; - - /* turn off caches */ - CacheControl(0, ~0); - - /* Go into supervisor state */ - SuperState(); - - /* turn off any mmu translation */ - disable_mmu(); - - /* execute the copy-and-go code (from CHIP RAM) */ - start_kernel(startfunc, (char *)stack+TEMP_STACKSIZE, memptr, start_mem, - kernel_size, bi.ramdisk.addr, rd_size); - - /* Clean up and exit in case of a failure */ -Fail: - if (kfd != -1) - KClose(kfd); - if (rfd != -1) - Close(rfd); - if (memptr) - FreeMem((void *)memptr, memreq); - if (stack) - FreeMem((void *)stack, TEMP_STACKSIZE); - if (kernel_phdrs) - FreeMem((void *)kernel_phdrs, kexec_elf.e_phnum*sizeof(Elf32_Phdr)); - return(FALSE); -} - - - /* - * Determine the Chipset - */ - -static u_long get_chipset(void) -{ - u_char cs; - u_long chipset; - - if (GfxBase->Version >= 39) - cs = SetChipRev(SETCHIPREV_BEST); - else - cs = GfxBase->ChipRevBits0; - if ((cs & GFXG_AGA) == GFXG_AGA) - chipset = CS_AGA; - else if ((cs & GFXG_ECS) == GFXG_ECS) - chipset = CS_ECS; - else if ((cs & GFXG_OCS) == GFXG_OCS) - chipset = CS_OCS; - else - chipset = CS_STONEAGE; - return(chipset); -} - - - /* - * Determine the CPU Type - */ - -static void get_processor(u_long *cpu, u_long *fpu, u_long *mmu) -{ - *cpu = *fpu = 0; - - if (SysBase->AttnFlags & AFF_68060) - *cpu = CPU_68060; - else if (SysBase->AttnFlags & AFF_68040) - *cpu = CPU_68040; - else if (SysBase->AttnFlags & AFF_68030) - *cpu = CPU_68030; - else if (SysBase->AttnFlags & AFF_68020) - *cpu = CPU_68020; - - if (*cpu == CPU_68040 || *cpu == CPU_68060) { - if (SysBase->AttnFlags & AFF_FPU40) - *fpu = *cpu; - } else if (SysBase->AttnFlags & AFF_68882) - *fpu = FPU_68882; - else if (SysBase->AttnFlags & AFF_68881) - *fpu = FPU_68881; - - *mmu = *cpu; -} - - /* - * Determine the Amiga Model - */ - -static u_long get_model(u_long chipset) -{ - u_long model = AMI_UNKNOWN; - - if (debugflag) - Puts("Amiga model identification:\n"); - if (probe_resource("draco.resource")) - model = AMI_DRACO; - else { - if (debugflag) - Puts(" Chipset: "); - switch (chipset) { - case CS_STONEAGE: - if (debugflag) - Puts("Old or unknown\n"); - goto OCS; - break; - - case CS_OCS: - if (debugflag) - Puts("OCS\n"); -OCS: if (probe_resident("cd.device")) - model = AMI_CDTV; - else - /* let's call it an A2000 (may be A500, A1000, A2500) */ - model = AMI_2000; - break; - - case CS_ECS: - if (debugflag) - Puts("ECS\n"); - if (probe_resident("Magic 36.7") || - probe_resident("kickad 36.57") || - probe_resident("A3000 Bonus") || - probe_resident("A3000 bonus")) - /* let's call it an A3000 (may be A3000T) */ - model = AMI_3000; - else if (probe_resource("card.resource")) - model = AMI_600; - else - /* let's call it an A2000 (may be A500[+], A1000, A2500) */ - model = AMI_2000; - break; - - case CS_AGA: - if (debugflag) - Puts("AGA\n"); - if (probe_resident("A1000 Bonus") || - probe_resident("A4000 bonus")) - model = probe_resident("NCR scsi.device") ? AMI_4000T : - AMI_4000; - else if (probe_resource("card.resource")) - model = AMI_1200; - else if (probe_resident("cd.device")) - model = AMI_CD32; - else - model = AMI_3000PLUS; - break; - } - } - if (debugflag) { - Puts("\nType a key to continue..."); - GetChar(); - Puts("\n\n"); - } - return(model); -} - - - /* - * Probe for a Resident Modules - */ - -static int probe_resident(const char *name) -{ - const struct Resident *res; - - if (debugflag) - Printf(" Module `%s': ", name); - res = FindResident(name); - if (debugflag) - if (res) - Printf("0x%08lx\n", res); - else - Puts("not present\n"); - return(res ? TRUE : FALSE); -} - - - /* - * Probe for an available Resource - */ - -static int probe_resource(const char *name) -{ - const void *res; - - if (debugflag) - Printf(" Resource `%s': ", name); - res = OpenResource(name); - if (debugflag) - if (res) - Printf("0x%08lx\n", res); - else - Puts("not present\n"); - return(res ? TRUE : FALSE); -} - - - /* - * Create the Bootinfo structure - */ - -static int create_bootinfo(void) -{ - int i; - struct bi_record *record; - - /* Initialization */ - bi_size = 0; - - /* Generic tags */ - if (!add_bi_record(BI_MACHTYPE, sizeof(bi.machtype), &bi.machtype)) - return(0); - if (!add_bi_record(BI_CPUTYPE, sizeof(bi.cputype), &bi.cputype)) - return(0); - if (!add_bi_record(BI_FPUTYPE, sizeof(bi.fputype), &bi.fputype)) - return(0); - if (!add_bi_record(BI_MMUTYPE, sizeof(bi.mmutype), &bi.mmutype)) - return(0); - for (i = 0; i < bi.num_memory; i++) - if (!add_bi_record(BI_MEMCHUNK, sizeof(bi.memory[i]), &bi.memory[i])) - return(0); - if (bi.ramdisk.size) - if (!add_bi_record(BI_RAMDISK, sizeof(bi.ramdisk), &bi.ramdisk)) - return(0); - if (!add_bi_string(BI_COMMAND_LINE, bi.command_line)) - return(0); - - /* Amiga tags */ - if (!add_bi_record(BI_AMIGA_MODEL, sizeof(bi.model), &bi.model)) - return(0); - for (i = 0; i < bi.num_autocon; i++) - if (!add_bi_record(BI_AMIGA_AUTOCON, sizeof(bi.autocon[i]), - &bi.autocon[i])) - return(0); - if (!add_bi_record(BI_AMIGA_CHIP_SIZE, sizeof(bi.chip_size), &bi.chip_size)) - return(0); - if (!add_bi_record(BI_AMIGA_VBLANK, sizeof(bi.vblank), &bi.vblank)) - return(0); - if (!add_bi_record(BI_AMIGA_PSFREQ, sizeof(bi.psfreq), &bi.psfreq)) - return(0); - if (!add_bi_record(BI_AMIGA_ECLOCK, sizeof(bi.eclock), &bi.eclock)) - return(0); - if (!add_bi_record(BI_AMIGA_CHIPSET, sizeof(bi.chipset), &bi.chipset)) - return(0); - if (!add_bi_record(BI_AMIGA_SERPER, sizeof(bi.serper), &bi.serper)) - return(0); - - /* Trailer */ - record = (struct bi_record *)((u_long)&bi_union.record+bi_size); - record->tag = BI_LAST; - bi_size += sizeof(bi_union.record.tag); - - return(1); -} - - - /* - * Add a Record to the Bootinfo Structure - */ - -static int add_bi_record(u_short tag, u_short size, const void *data) -{ - struct bi_record *record; - u_int size2; - - size2 = (sizeof(struct bi_record)+size+3)&-4; - if (bi_size+size2+sizeof(bi_union.record.tag) > MAX_BI_SIZE) { - Puts("Can't add bootinfo record. Ask a wizard to enlarge me.\n"); - return(0); - } - record = (struct bi_record *)((u_long)&bi_union.record+bi_size); - record->tag = tag; - record->size = size2; - memcpy(record->data, data, size); - bi_size += size2; - return(1); -} - - - /* - * Add a String Record to the Bootinfo Structure - */ - -static int add_bi_string(u_short tag, const u_char *s) -{ - return(add_bi_record(tag, strlen(s)+1, (void *)s)); -} - - -#ifdef BOOTINFO_COMPAT_1_0 - - /* - * Create the Bootinfo structure for backwards compatibility mode - */ - -static int create_compat_bootinfo(void) -{ - u_int i; - - compat_bootinfo.machtype = bi.machtype; - if (bi.cputype & CPU_68020) - compat_bootinfo.cputype = COMPAT_CPU_68020; - else if (bi.cputype & CPU_68030) - compat_bootinfo.cputype = COMPAT_CPU_68030; - else if (bi.cputype & CPU_68040) - compat_bootinfo.cputype = COMPAT_CPU_68040; - else if (bi.cputype & CPU_68060) - compat_bootinfo.cputype = COMPAT_CPU_68060; - else { - Printf("CPU type 0x%08lx not supported by kernel\n", bi.cputype); - return(0); - } - if (bi.fputype & FPU_68881) - compat_bootinfo.cputype |= COMPAT_FPU_68881; - else if (bi.fputype & FPU_68882) - compat_bootinfo.cputype |= COMPAT_FPU_68882; - else if (bi.fputype & FPU_68040) - compat_bootinfo.cputype |= COMPAT_FPU_68040; - else if (bi.fputype & FPU_68060) - compat_bootinfo.cputype |= COMPAT_FPU_68060; - else if (bi.fputype) { - Printf("FPU type 0x%08lx not supported by kernel\n", bi.fputype); - return(0); - } - compat_bootinfo.num_memory = bi.num_memory; - if (compat_bootinfo.num_memory > COMPAT_NUM_MEMINFO) { - Printf("Warning: using only %ld blocks of memory\n", - COMPAT_NUM_MEMINFO); - compat_bootinfo.num_memory = COMPAT_NUM_MEMINFO; - } - for (i = 0; i < compat_bootinfo.num_memory; i++) { - compat_bootinfo.memory[i].addr = bi.memory[i].addr; - compat_bootinfo.memory[i].size = bi.memory[i].size; - } - if (bi.ramdisk.size) { - bi.ramdisk.addr &= 0xfffffc00; - compat_bootinfo.ramdisk_size = (bi.ramdisk.size+1023)/1024; - compat_bootinfo.ramdisk_addr = bi.ramdisk.addr; - } else { - compat_bootinfo.ramdisk_size = 0; - compat_bootinfo.ramdisk_addr = 0; - } - strncpy(compat_bootinfo.command_line, bi.command_line, COMPAT_CL_SIZE); - compat_bootinfo.command_line[COMPAT_CL_SIZE-1] = '\0'; - - compat_bootinfo.bi_amiga.model = bi.model; - compat_bootinfo.bi_amiga.num_autocon = bi.num_autocon; - if (compat_bootinfo.bi_amiga.num_autocon > COMPAT_NUM_AUTO) { - Printf("Warning: using only %ld AutoConfig devices\n", - COMPAT_NUM_AUTO); - compat_bootinfo.bi_amiga.num_autocon = COMPAT_NUM_AUTO; - } - for (i = 0; i < compat_bootinfo.bi_amiga.num_autocon; i++) - compat_bootinfo.bi_amiga.autocon[i] = bi.autocon[i]; - compat_bootinfo.bi_amiga.chip_size = bi.chip_size; - compat_bootinfo.bi_amiga.vblank = bi.vblank; - compat_bootinfo.bi_amiga.psfreq = bi.psfreq; - compat_bootinfo.bi_amiga.eclock = bi.eclock; - compat_bootinfo.bi_amiga.chipset = bi.chipset; - compat_bootinfo.bi_amiga.hw_present = 0; - return(1); -} -#endif /* BOOTINFO_COMPAT_1_0 */ - - - /* - * Compare the Bootstrap and Kernel Versions - */ - -static int check_bootinfo_version(const char *memptr) -{ - const struct bootversion *bv = (struct bootversion *)memptr; - unsigned long version = 0; - int i, kernel_major, kernel_minor, boots_major, boots_minor; - - if (bv->magic == BOOTINFOV_MAGIC) - for (i = 0; bv->machversions[i].machtype != 0; ++i) - if (bv->machversions[i].machtype == MACH_AMIGA) { - version = bv->machversions[i].version; - break; - } - if (!version) - Puts("Kernel has no bootinfo version info, assuming 0.0\n"); - - kernel_major = BI_VERSION_MAJOR(version); - kernel_minor = BI_VERSION_MINOR(version); - boots_major = BI_VERSION_MAJOR(AMIGA_BOOTI_VERSION); - boots_minor = BI_VERSION_MINOR(AMIGA_BOOTI_VERSION); - Printf("Bootstrap's bootinfo version: %ld.%ld\n", boots_major, - boots_minor); - Printf("Kernel's bootinfo version : %ld.%ld\n", kernel_major, - kernel_minor); - - switch (kernel_major) { - case BI_VERSION_MAJOR(AMIGA_BOOTI_VERSION): - if (kernel_minor > boots_minor) { - Puts("Warning: Bootinfo version of bootstrap and kernel " - "differ!\n"); - Puts(" Certain features may not work.\n"); - } - break; - -#ifdef BOOTINFO_COMPAT_1_0 - case BI_VERSION_MAJOR(COMPAT_AMIGA_BOOTI_VERSION): - Puts("(using backwards compatibility mode)\n"); - break; -#endif /* BOOTINFO_COMPAT_1_0 */ - - default: - Printf("\nThis bootstrap is too %s for this kernel!\n", - boots_major < kernel_major ? "old" : "new"); - return(0); - } - return(kernel_major); -} - - - /* - * Call the copy-and-go-code - */ - -static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, - u_long start_mem, u_long kernel_size, u_long rd_dest, - u_long rd_size) -{ - register void (*a0)() __asm("a0") = startfunc; - register char *a2 __asm("a2") = stackp; - register char *a3 __asm("a3") = memptr; - register u_long a4 __asm("a4") = start_mem; - register u_long d0 __asm("d0") = rd_dest; - register u_long d1 __asm("d1") = rd_size; - register u_long d2 __asm("d2") = kernel_size; - register u_long d3 __asm("d3") = bi_size; - - __asm __volatile ("movel a2,sp;" - "jmp a0@" - : /* no outputs */ - : "r" (a0), "r" (a2), "r" (a3), "r" (a4), "r" (d0), - "r" (d1), "r" (d2), "r" (d3) - /* no return */); - /* fake a noreturn */ - for (;;); -} - - - /* - * This assembler code is copied to chip ram, and then executed. - * It copies the kernel to it's final resting place. - * - * It is called with: - * - * a3 = memptr - * a4 = start_mem - * d0 = rd_dest - * d1 = rd_size - * d2 = kernel_size - * d3 = bi_size - */ - -asm(".text\n" -ALIGN_STR "\n" -SYMBOL_NAME_STR(copyall) ": - | /* copy kernel text and data */ - movel a3,a0 | src = (u_long *)memptr; - movel a0,a2 | limit = (u_long *)(memptr+kernel_size); - addl d2,a2 - movel a4,a1 | dest = (u_long *)start_mem; -1: cmpl a0,a2 - jeq 2f | while (src < limit) - moveb a0@+,a1@+ | *dest++ = *src++; - jra 1b -2: - | /* copy bootinfo to end of bss */ - movel a3,a0 | src = (u_long *)(memptr+kernel_size); - addl d2,a0 | dest = end of bss (already in a1) - movel d3,d7 | count = bi_size - subql #1,d7 -1: moveb a0@+,a1@+ | while (--count > -1) - dbra d7,1b | *dest++ = *src++ - - | /* copy the ramdisk to the top of memory */ - movel a3,a0 | src = (u_long *)(memptr+kernel_size+bi_size); - addl d2,a0 - addl d3,a0 - movel d0,a1 | dest = (u_long *)rd_dest; - movel a0,a2 | limit = (u_long *)(memptr+kernel_size+ - addl d1,a2 | bi_size+rd_size); -1: cmpl a0,a2 - jeq 2f | while (src > limit) - moveb a0@+,a1@+ | *dest++ = *src++; - jra 1b -2: - | /* jump to start of kernel */ - movel a4,a0 | jump_to (start_mem); - jmp a0@ -" -SYMBOL_NAME_STR(copyallend) ": -"); - - - /* - * Test for a MapROMmed A3640 Board - */ - -asm(".text\n" -ALIGN_STR "\n" -SYMBOL_NAME_STR(maprommed) ": - oriw #0x0700,sr - moveml #0x3f20,sp@- - | /* Save cache settings */ - .long 0x4e7a1002 | movec cacr,d1 */ - | /* Save MMU settings */ - .long 0x4e7a2003 | movec tc,d2 - .long 0x4e7a3004 | movec itt0,d3 - .long 0x4e7a4005 | movec itt1,d4 - .long 0x4e7a5006 | movec dtt0,d5 - .long 0x4e7a6007 | movec dtt1,d6 - moveq #0,d0 - movel d0,a2 - | /* Disable caches */ - .long 0x4e7b0002 | movec d0,cacr - | /* Disable MMU */ - .long 0x4e7b0003 | movec d0,tc - .long 0x4e7b0004 | movec d0,itt0 - .long 0x4e7b0005 | movec d0,itt1 - .long 0x4e7b0006 | movec d0,dtt0 - .long 0x4e7b0007 | movec d0,dtt1 - lea 0x07f80000,a0 - lea 0x00f80000,a1 - movel a0@,d7 - cmpl a1@,d7 - jne 1f - movel d7,d0 - notl d0 - movel d0,a0@ - nop | /* Thanks to Jörg Mayer! */ - cmpl a1@,d0 - jne 1f - moveq #-1,d0 | /* MapROMmed A3640 present */ - movel d0,a2 -1: movel d7,a0@ - | /* Restore MMU settings */ - .long 0x4e7b2003 | movec d2,tc - .long 0x4e7b3004 | movec d3,itt0 - .long 0x4e7b4005 | movec d4,itt1 - .long 0x4e7b5006 | movec d5,dtt0 - .long 0x4e7b6007 | movec d6,dtt1 - | /* Restore cache settings */ - .long 0x4e7b1002 | movec d1,cacr - movel a2,d0 - moveml sp@+,#0x04fc - rte -"); - - - /* - * Reset functions for nasty Zorro boards - */ - -static void reset_rb3(const struct ConfigDev *cd) -{ - volatile u_char *rb3_reg = (u_char *)(cd->cd_BoardAddr+0x01002000); - - /* FN: If a Rainbow III board is present, reset it to disable */ - /* its (possibly activated) vertical blank interrupts as the */ - /* kernel is not yet prepared to handle them (level 6). */ - - /* set RESET bit in special function register */ - *rb3_reg = 0x01; - /* actually, only a few cycles delay are required... */ - Sleep(1000000); - /* clear reset bit */ - *rb3_reg = 0x00; -} - -static void reset_piccolo(const struct ConfigDev *cd) -{ - volatile u_char *piccolo_reg = (u_char *)(cd->cd_BoardAddr+0x8000); - - /* FN: the same stuff as above, for the Piccolo board. */ - /* this also has the side effect of resetting the board's */ - /* output selection logic to use the Amiga's display in single */ - /* monitor systems - which is currently what we want. */ - - /* set RESET bit in special function register */ - *piccolo_reg = 0x01; - /* actually, only a few cycles delay are required... */ - Sleep(1000000); - /* clear reset bit */ - *piccolo_reg = 0x51; -} - -static void reset_sd64(const struct ConfigDev *cd) -{ - volatile u_char *sd64_reg = (u_char *)(cd->cd_BoardAddr+0x8000); - - /* FN: the same stuff as above, for the SD64 board. */ - /* just as on the Piccolo, this also resets the monitor switch */ - - /* set RESET bit in special function register */ - *sd64_reg = 0x1f; - /* actually, only a few cycles delay are required... */ - Sleep(1000000); - /* clear reset bit AND switch monitor bit (0x20) */ - *sd64_reg = 0x4f; -} - -static void reset_ariadne(const struct ConfigDev *cd) -{ - volatile u_short *lance_rdp = (u_short *)(cd->cd_BoardAddr+0x0370); - volatile u_short *lance_rap = (u_short *)(cd->cd_BoardAddr+0x0372); - volatile u_short *lance_reset = (u_short *)(cd->cd_BoardAddr+0x0374); - - volatile u_char *pit_paddr = (u_char *)(cd->cd_BoardAddr+0x1004); - volatile u_char *pit_pbddr = (u_char *)(cd->cd_BoardAddr+0x1006); - volatile u_char *pit_pacr = (u_char *)(cd->cd_BoardAddr+0x100b); - volatile u_char *pit_pbcr = (u_char *)(cd->cd_BoardAddr+0x100e); - volatile u_char *pit_psr = (u_char *)(cd->cd_BoardAddr+0x101a); - - u_short in; - - Disable(); - - /* - * Reset the Ethernet part (Am79C960 PCnet-ISA) - */ - - in = *lance_reset; /* Reset Chip on Read Access */ - *lance_rap = 0x0000; /* PCnet-ISA Controller Status (CSR0) */ - *lance_rdp = 0x0400; /* STOP */ - - /* - * Reset the Parallel part (MC68230 PI/T) - */ - - *pit_pacr &= 0xfd; /* Port A Control Register */ - *pit_pbcr &= 0xfd; /* Port B Control Register */ - *pit_psr = 0x05; /* Port Status Register */ - *pit_paddr = 0x00; /* Port A Data Direction Register */ - *pit_pbddr = 0x00; /* Port B Data Direction Register */ - - Enable(); -} - -static void reset_hydra(const struct ConfigDev *cd) -{ - volatile u_char *nic_cr = (u_char *)(cd->cd_BoardAddr+0xffe1); - volatile u_char *nic_isr = (u_char *)(cd->cd_BoardAddr+0xffe1 + 14); - int n = 5000; - - Disable(); - - *nic_cr = 0x21; /* nic command register: software reset etc. */ - while (((*nic_isr & 0x80) == 0) && --n) /* wait for reset to complete */ - ; - - Enable(); -} - -#if 0 -static void reset_a2060(const struct ConfigDev *cd) -{ -#error reset_a2060: not yet implemented -} -#endif - - -#ifdef ZKERNEL - -#define ZFILE_CHUNK_BITS 16 /* chunk is 64 KB */ -#define ZFILE_CHUNK_SIZE (1 << ZFILE_CHUNK_BITS) -#define ZFILE_CHUNK_MASK (ZFILE_CHUNK_SIZE-1) -#define ZFILE_N_CHUNKS (2*1024*1024/ZFILE_CHUNK_SIZE) - -/* variables for storing the uncompressed data */ -static char *ZFile[ZFILE_N_CHUNKS]; -static int ZFileSize = 0; -static int ZFpos = 0; -static int Zwpos = 0; - -static int Zinfd = 0; /* fd of compressed file */ - -/* - * gzip declarations - */ - -#define OF(args) args - -#define memzero(s, n) memset ((s), 0, (n)) - -typedef unsigned char uch; -typedef unsigned short ush; -typedef unsigned long ulg; - -#define INBUFSIZ 4096 -#define WSIZE 0x8000 /* window size--must be a power of two, and */ - /* at least 32K for zip's deflate method */ - -static uch *inbuf; -static uch *window; - -static unsigned insize = 0; /* valid bytes in inbuf */ -static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ -static unsigned outcnt = 0; /* bytes in output buffer */ -static int exit_code = 0; -static long bytes_out = 0; - -#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) - -/* Diagnostic functions (stubbed out) */ -#define Assert(cond,msg) -#define Trace(x) -#define Tracev(x) -#define Tracevv(x) -#define Tracec(c,x) -#define Tracecv(c,x) - -#define STATIC static - -static int fill_inbuf(void); -static void flush_window(void); -static void error(char *m); -static void gzip_mark(void **); -static void gzip_release(void **); - -#define malloc(x) AllocVec(x, MEMF_FAST | MEMF_PUBLIC) -#define free(x) FreeVec(x) - -#ifdef LILO -#include "inflate.c" -#else -#include "../../../../lib/inflate.c" -#endif - -static void gzip_mark(void **ptr) -{ -} - -static void gzip_release(void **ptr) -{ -} - - -/* - * Fill the input buffer. This is called only when the buffer is empty - * and at least one byte is really needed. - */ -static int fill_inbuf(void) -{ - if (exit_code) - return -1; - - insize = Read(Zinfd, inbuf, INBUFSIZ); - if (insize <= 0) - return -1; - - inptr = 1; - return(inbuf[0]); -} - -/* - * Write the output window window[0..outcnt-1] and update crc and bytes_out. - * (Used for the decompressed data only.) - */ -static void flush_window(void) -{ - ulg c = crc; /* temporary variable */ - unsigned n; - uch *in, ch; - int chunk = Zwpos >> ZFILE_CHUNK_BITS; - - if (exit_code) - return; - - if (chunk >= ZFILE_N_CHUNKS) { - error("Compressed image too large! Aborting.\n"); - return; - } - if (!ZFile[chunk]) { - if (!(ZFile[chunk] = (char *)AllocMem(ZFILE_CHUNK_SIZE, - MEMF_FAST | MEMF_PUBLIC))) { - error("Out of memory for decompresing kernel image\n"); - return; - } - } - memcpy(ZFile[chunk] + (Zwpos & ZFILE_CHUNK_MASK), window, outcnt); - Zwpos += outcnt; - -#define DISPLAY_BITS 10 - if ((Zwpos & ((1 << DISPLAY_BITS)-1)) == 0) - PutChar('.'); - - in = window; - for (n = 0; n < outcnt; n++) { - ch = *in++; - c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); - } - crc = c; - bytes_out += (ulg)outcnt; - outcnt = 0; -} - -static void error(char *x) -{ - Printf("\n%s", x); - exit_code = 1; -} - -static inline int call_sub(int (*func)(void), void *stackp) -{ - register int _res __asm("d0"); - register int (*a0)(void) __asm("a0") = func; - register int (*a1)(void) __asm("a1") = stackp; - - __asm __volatile ("movel sp,a2;" - "movel a1,sp;" - "jsr a0@;" - "movel a2,sp" - : "=r" (_res) - : "r" (a0), "r" (a1) - : "a0", "a1", "a2", "d0", "d1", "memory"); - return(_res); -} - -static int load_zkernel(int fd) -{ - int i, err = -1; -#define ZSTACKSIZE (16384) - u_long *zstack; - - for (i = 0; i < ZFILE_N_CHUNKS; ++i) - ZFile[i] = NULL; - Zinfd = fd; - Seek(fd, 0); - - if (!(inbuf = (uch *)AllocMem(INBUFSIZ, MEMF_FAST | MEMF_PUBLIC))) - Puts("Couldn't allocate gunzip buffer\n"); - else { - if (!(window = (uch *)AllocMem(WSIZE, MEMF_FAST | MEMF_PUBLIC))) - Puts("Couldn't allocate gunzip window\n"); - else { - if (!(zstack = (u_long *)AllocMem(ZSTACKSIZE, - MEMF_FAST | MEMF_PUBLIC))) - Puts("Couldn't allocate gunzip stack\n"); - else { - Puts("Uncompressing kernel image "); - makecrc(); - if (!(err = call_sub(gunzip, (char *)zstack+ZSTACKSIZE))) - Puts("done\n"); - ZFileSize = Zwpos; - FreeMem(zstack, ZSTACKSIZE); - } - FreeMem(window, WSIZE); - window = NULL; - } - FreeMem(inbuf, INBUFSIZ); - inbuf = NULL; - } - Close(Zinfd); /* input file not needed anymore */ - return(err); -} - - -/* Note about the read/lseek wrapper and its memory management: It assumes - * that all seeks are only forward, and thus data already read or skipped can - * be freed. This is true for current organization of bootstrap and kernels. - * Little exception: The struct kexec at the start of the file. After reading - * it, there may be a seek back to the end of the file. But this currently - * doesn't hurt. (Roman) - */ - -static int KRead(int fd, void *buf, int cnt) -{ - unsigned done = 0; - - if (!ZFileSize) - return(Read(fd, buf, cnt)); - - if (ZFpos + cnt > ZFileSize) - cnt = ZFileSize - ZFpos; - - while (cnt > 0) { - unsigned chunk = ZFpos >> ZFILE_CHUNK_BITS; - unsigned endchunk = (chunk+1) << ZFILE_CHUNK_BITS; - unsigned n = cnt; - - if (ZFpos + n > endchunk) - n = endchunk - ZFpos; - memcpy(buf, ZFile[chunk] + (ZFpos & ZFILE_CHUNK_MASK), n); - cnt -= n; - buf += n; - done += n; - ZFpos += n; - - if (ZFpos == endchunk) { - FreeMem(ZFile[chunk], ZFILE_CHUNK_SIZE); - ZFile[chunk] = NULL; - } - } - - return(done); -} - - -static int KSeek(int fd, int offset) -{ - unsigned oldpos, oldchunk, newchunk; - - if (!ZFileSize) - return(Seek(fd, offset)); - - oldpos = ZFpos; - ZFpos = offset; - if (ZFpos < 0) { - ZFpos = 0; - return(-1); - } else if (ZFpos > ZFileSize) { - ZFpos = ZFileSize; - return(-1); - } - - /* free memory of skipped-over data */ - oldchunk = oldpos >> ZFILE_CHUNK_BITS; - newchunk = ZFpos >> ZFILE_CHUNK_BITS; - while(oldchunk < newchunk) { - if (ZFile[oldchunk]) { - FreeMem(ZFile[oldchunk], ZFILE_CHUNK_SIZE); - ZFile[oldchunk] = NULL; - } - ++oldchunk; - } - return(ZFpos); -} - - -static void free_zfile(void) -{ - int i; - - for (i = 0; i < ZFILE_N_CHUNKS; ++i) - if (ZFile[i]) { - FreeMem(ZFile[i], ZFILE_CHUNK_SIZE); - ZFile[i] = NULL; - } -} - -static int KClose(int fd) -{ - if (ZFileSize) { - free_zfile(); - ZFileSize = 0; - } else - Close(fd); - return(0); -} -#endif /* ZKERNEL */ diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/amiga/linuxboot.h linux/arch/m68k/boot/amiga/linuxboot.h --- v2.1.47/linux/arch/m68k/boot/amiga/linuxboot.h Tue May 13 22:41:02 1997 +++ linux/arch/m68k/boot/amiga/linuxboot.h Wed Dec 31 16:00:00 1969 @@ -1,455 +0,0 @@ -/* - * linux/arch/m68k/boot/amiga/linuxboot.h -- Generic routine to boot Linux/m68k - * on Amiga, used by both Amiboot and - * Amiga-Lilo. - * - * Created 1996 by Geert Uytterhoeven - * - * - * This file is based on the original bootstrap code (bootstrap.c): - * - * Copyright (C) 1993, 1994 Hamish Macdonald - * Greg Harp - * - * with work by Michael Rausch - * Geert Uytterhoeven - * Frank Neumann - * Andreas Schwab - * - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - - -#include -#include - - - /* - * Amiboot Version - */ - -#define AMIBOOT_VERSION "5.5" - - - /* - * Amiga Bootinfo Definitions - * - * All limits herein are `soft' limits, i.e. they don't put constraints - * on the actual parameters in the kernel. - */ - -struct amiga_bootinfo { - u_long machtype; /* machine type = MACH_AMIGA */ - u_long cputype; /* system CPU */ - u_long fputype; /* system FPU */ - u_long mmutype; /* system MMU */ - int num_memory; /* # of memory blocks found */ - struct mem_info memory[NUM_MEMINFO];/* memory description */ - struct mem_info ramdisk; /* ramdisk description */ - char command_line[CL_SIZE]; /* kernel command line parameters */ - u_long model; /* Amiga Model */ - int num_autocon; /* # of autoconfig devices found */ - struct ConfigDev autocon[ZORRO_NUM_AUTO]; /* autoconfig devices */ - u_long chip_size; /* size of chip memory (bytes) */ - u_char vblank; /* VBLANK frequency */ - u_char psfreq; /* power supply frequency */ - u_long eclock; /* EClock frequency */ - u_long chipset; /* native chipset present */ - u_short serper; /* serial port period */ -}; - - - /* - * Parameters passed to linuxboot() - */ - -struct linuxboot_args { - struct amiga_bootinfo bi; /* Initial values override detected values */ - const char *kernelname; - const char *ramdiskname; - int debugflag; - int keep_video; - int reset_boards; - u_int baud; - void (*puts)(const char *str); - long (*getchar)(void); - void (*putchar)(char c); - void (*printf)(const char *fmt, ...); - int (*open)(const char *path); - int (*seek)(int fd, int offset); - int (*read)(int fd, char *buf, int count); - void (*close)(int fd); - int (*filesize)(const char *path); - void (*sleep)(u_long micros); -}; - - - /* - * Boot the Linux/m68k Operating System - */ - -extern u_long linuxboot(const struct linuxboot_args *args); - - - /* - * Amiga Models - */ - -extern const char *amiga_models[]; -extern const u_long first_amiga_model; -extern const u_long last_amiga_model; - - - /* - * Exec Library Definitions - */ - -#define TRUE (1) -#define FALSE (0) - - -struct List { - struct Node *lh_Head; - struct Node *lh_Tail; - struct Node *lh_TailPred; - u_char lh_Type; - u_char l_pad; -}; - -struct MemChunk { - struct MemChunk *mc_Next; /* pointer to next chunk */ - u_long mc_Bytes; /* chunk byte size */ -}; - -#define MEMF_PUBLIC (1<<0) -#define MEMF_CHIP (1<<1) -#define MEMF_FAST (1<<2) -#define MEMF_LOCAL (1<<8) -#define MEMF_CLEAR (1<<16) - -struct MemHeader { - struct Node mh_Node; - u_short mh_Attributes; /* characteristics of this region */ - struct MemChunk *mh_First; /* first free region */ - void *mh_Lower; /* lower memory bound */ - void *mh_Upper; /* upper memory bound+1 */ - u_long mh_Free; /* total number of free bytes */ -}; - -struct ExecBase { - u_char fill1[20]; - u_short Version; - u_char fill2[274]; - u_short AttnFlags; - u_char fill3[24]; - struct List MemList; - u_char fill4[194]; - u_char VBlankFrequency; - u_char PowerSupplyFrequency; - u_char fill5[36]; - u_long ex_EClockFrequency; - u_char fill6[60]; -}; - -#define AFB_68020 (1) -#define AFF_68020 (1<AttnFlags & AFF_68040) - __asm __volatile ("moveq #0,d0;" - ".long 0x4e7b0003;" /* movec d0,tc */ - ".long 0x4e7b0004;" /* movec d0,itt0 */ - ".long 0x4e7b0005;" /* movec d0,itt1 */ - ".long 0x4e7b0006;" /* movec d0,dtt0 */ - ".long 0x4e7b0007" /* movec d0,dtt1 */ - : /* no outputs */ - : /* no inputs */ - : "d0"); - else { - __asm __volatile ("subl #4,sp;" - "pmove tc,sp@;" - "bclr #7,sp@;" - "pmove sp@,tc;" - "addl #4,sp"); - if (SysBase->AttnFlags & AFF_68030) - __asm __volatile ("clrl sp@-;" - ".long 0xf0170800;" /* pmove sp@,tt0 */ - ".long 0xf0170c00;" /* pmove sp@,tt1 */ - "addql #4,sp"); - } -} diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/atari/bootp.c linux/arch/m68k/boot/atari/bootp.c --- v2.1.47/linux/arch/m68k/boot/atari/bootp.c Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/boot/atari/bootp.c Wed Dec 31 16:00:00 1969 @@ -1,814 +0,0 @@ - -#include -#include -#include -#include - -#include "bootp.h" - - -/* --------------------------------------------------------------------- */ -/* Protocol Header Structures */ - -struct etherhdr { - HWADDR dst_addr; - HWADDR src_addr; - unsigned short type; -}; - -struct arphdr { - unsigned short hrd; /* format of hardware address */ - unsigned short pro; /* format of protocol address */ - unsigned char hln; /* length of hardware address */ - unsigned char pln; /* length of protocol address */ - unsigned short op; /* ARP opcode (command) */ - unsigned char addr[0]; /* addresses (var len) */ -}; - -struct iphdr { - unsigned char version : 4; - unsigned char ihl : 4; - unsigned char tos; - unsigned short tot_len; - unsigned short id; - unsigned short frag_off; - unsigned char ttl; - unsigned char protocol; - unsigned short chksum; - IPADDR src_addr; - IPADDR dst_addr; -}; - -struct udphdr { - unsigned short src_port; - unsigned short dst_port; - unsigned short len; - unsigned short chksum; -}; - -struct bootp { - unsigned char op; /* packet opcode type */ - unsigned char htype; /* hardware addr type */ - unsigned char hlen; /* hardware addr length */ - unsigned char hops; /* gateway hops */ - unsigned long xid; /* transaction ID */ - unsigned short secs; /* seconds since boot began */ - unsigned short unused; - IPADDR ciaddr; /* client IP address */ - IPADDR yiaddr; /* 'your' IP address */ - IPADDR siaddr; /* server IP address */ - IPADDR giaddr; /* gateway IP address */ - unsigned char chaddr[16]; /* client hardware address */ - unsigned char sname[64]; /* server host name */ - unsigned char file[128]; /* boot file name */ - unsigned char vend[64]; /* vendor-specific area */ -}; - -struct tftp_req { - unsigned short opcode; - char name[512]; -}; - -struct tftp_data { - unsigned short opcode; - unsigned short nr; - unsigned char data[512]; -}; - -struct tftp_ack { - unsigned short opcode; - unsigned short nr; -}; - -struct tftp_error { - unsigned short opcode; - unsigned short errcode; - char str[512]; -}; - - -typedef struct { - struct etherhdr ether; - struct arphdr arp; -} ARP; - -typedef struct { - struct etherhdr ether; - struct iphdr ip; - struct udphdr udp; -} UDP; - -#define UDP_BOOTPS 67 -#define UDP_BOOTPC 68 -#define UDP_TFTP 69 - -typedef struct { - struct etherhdr ether; - struct iphdr ip; - struct udphdr udp; - struct bootp bootp; -} BOOTP; - -#define BOOTREQUEST 1 -#define BOOTREPLY 2 -#define BOOTP_RETRYS 5 - -typedef struct { - struct etherhdr ether; - struct iphdr ip; - struct udphdr udp; - union tftp { - unsigned short opcode; - struct tftp_req req; - struct tftp_data data; - struct tftp_ack ack; - struct tftp_error error; - } tftp; -} TFTP; - -#define TFTP_RRQ 1 -#define TFTP_WRQ 2 -#define TFTP_DATA 3 -#define TFTP_ACK 4 -#define TFTP_ERROR 5 - - -/* --------------------------------------------------------------------- */ -/* Addresses */ - -static HWADDR MyHwaddr; -static HWADDR ServerHwaddr; -static IPADDR MyIPaddr; -static IPADDR ServerIPaddr; - -static IPADDR IP_Unknown_Addr = 0x00000000; -static IPADDR IP_Broadcast_Addr = 0xffffffff; -static HWADDR Eth_Broadcast_Addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - - -#define HZ 200 -#define _hz_200 (*(volatile unsigned long *)0x4ba) - - -/* --------------------------------------------------------------------- */ -/* Error Strings */ - -static char *ErrStr[] = { - "timeout", - "general Ethernet transmit error", - "general Ethernet receive error", - "Ethernet framing error", - "Ethernet overflow error", - "Ethernet CRC error" -}; - - -/* --------------------------------------------------------------------- */ -/* Kfile Emulation Definitions */ - -#define KFILE_CHUNK_BITS 16 /* chunk is 64 KB */ -#define KFILE_CHUNK_SIZE (1 << KFILE_CHUNK_BITS) -#define KFILE_CHUNK_MASK (KFILE_CHUNK_SIZE-1) -#define KFILE_N_CHUNKS (2*1024*1024/KFILE_CHUNK_SIZE) - -char *KFile[KFILE_N_CHUNKS]; -int KFileSize = 0; -int KFpos = 0; - - - - -/***************************** Prototypes *****************************/ - -static void free_kfile( void ); -static int bootp( char *image_name ); -static int tftp( char *image_name ); -static int udp_send( UDP *pkt, int len, int fromport, int toport ); -static unsigned short ip_checksum( struct iphdr *buf ); -static int udp_rcv( UDP *pkt, int *len, int fromport, int atport ); -static void print_ip( IPADDR addr ); -static void print_hw( HWADDR addr ); -static int check_ethif( void ); -static int eth_send( Packet *pkt, int len ); -static int eth_rcv( Packet *pkt, int *len ); - -/************************* End of Prototypes **************************/ - - - - -/* --------------------------------------------------------------------- */ -/* Interface to bootstrap.c */ - -/* get_remote_kernel(): - * Perform all necessary steps to get the kernel image - * from the boot server. If successfull (retval == 0), subsequent calls to - * kread() can access the data. Fatal errors (i.e., retrying is useless) - * return -2, others -1. - */ - -int get_remote_kernel( const char *kname /* optional */ ) - -{ char image_name[256]; - int rv; - - /* Check if a Ethernet interface is present and determine the Ethernet - * address */ - if (check_ethif() < 0) { - printf( "No Ethernet interface found -- no remote boot possible.\n" ); - return( -2 ); - } - - /* Do a BOOTP request to find out our IP address and the kernel image's - * name; we also learn the IP and Ethernet address of our server */ - if (kname) - strcpy( image_name, kname ); - else - *image_name = 0; - if ((rv = bootp( image_name )) < 0) - return( rv ); - - /* Now start a TFTP connection to receive the kernel image */ - if ((rv = tftp( image_name )) < 0) - return( rv ); - - return( 0 ); -} - - -/* ll_read(), ll_lseek(), ll_close(): - * Functions for accessing the received kernel image like with read(), - * lseek(), close(). - */ - -int ll_read( int fd, void *buf, unsigned cnt ) - -{ unsigned done = 0; - - if (!KFileSize) - return( read( fd, buf, cnt ) ); - - if (KFpos + cnt > KFileSize) - cnt = KFileSize - KFpos; - - while( cnt > 0 ) { - unsigned chunk = KFpos >> KFILE_CHUNK_BITS; - unsigned endchunk = (chunk+1) << KFILE_CHUNK_BITS; - unsigned n = cnt; - - if (KFpos + n > endchunk) - n = endchunk - KFpos; - memcpy( buf, KFile[chunk] + (KFpos & KFILE_CHUNK_MASK), n ); - cnt -= n; - buf += n; - done += n; - KFpos += n; - - if (KFpos == endchunk) { - free( KFile[chunk] ); - KFile[chunk] = NULL; - } - } - - return( done ); -} - - -int ll_lseek( int fd, int where, int whence ) - -{ - unsigned oldpos, oldchunk, newchunk; - - if (!KFileSize) - return( lseek( fd, where, whence ) ); - - oldpos = KFpos; - switch( whence ) { - case SEEK_SET: - KFpos = where; - break; - case SEEK_CUR: - KFpos += where; - break; - case SEEK_END: - KFpos = KFileSize + where; - break; - default: - return( -1 ); - } - if (KFpos < 0) { - KFpos = 0; - return( -1 ); - } - else if (KFpos > KFileSize) { - KFpos = KFileSize; - return( -1 ); - } - - /* free memory of skipped-over data */ - oldchunk = oldpos >> KFILE_CHUNK_BITS; - newchunk = KFpos >> KFILE_CHUNK_BITS; - while( oldchunk < newchunk ) { - if (KFile[oldchunk]) { - free( KFile[oldchunk] ); - KFile[oldchunk] = NULL; - } - ++oldchunk; - } - - return( KFpos ); -} - - -int ll_close( int fd ) - -{ - if (!KFileSize) - return( close( fd ) ); - - free_kfile(); - return( 0 ); -} - - -static void free_kfile( void ) - -{ int i; - - for( i = 0; i < KFILE_N_CHUNKS; ++i ) - if (KFile[i]) free( KFile[i] ); -} - - - -/* --------------------------------------------------------------------- */ -/* BOOTP Procedure */ - - -static int bootp( char *image_name ) - -{ BOOTP req; - Packet _reply; - BOOTP *reply = (BOOTP *)_reply; - static unsigned char mincookie[] = { 99, 130, 83, 99, 255 }; - unsigned long starttime, rancopy; - int err, len, retry; - - memset( (char *)&req, 0, sizeof(req) ); - /* Now fill in the packet... */ - req.bootp.op = BOOTREQUEST; - req.bootp.htype = 1; /* 10Mb/s Ethernet */ - req.bootp.hlen = 6; - memcpy( req.bootp.chaddr, &MyHwaddr, ETHADDRLEN ); - - /* Put in the minimal RFC1497 Magic cookie */ - memcpy( req.bootp.vend, mincookie, sizeof(mincookie) ); - /* Put the user precified bootfile name in place */ - memcpy( req.bootp.file, image_name, strlen(image_name)+1); - - starttime = _hz_200; - for( retry = 0; retry < BOOTP_RETRYS; ++retry ) { - - /* Initialize server addresses and own IP to defaults */ - ServerIPaddr = IP_Broadcast_Addr; /* 255.255.255.255 */ - MyIPaddr = IP_Unknown_Addr; /* 0.0.0.0 */ - memcpy( ServerHwaddr, Eth_Broadcast_Addr, ETHADDRLEN ); - - if (retry) - sleep( 3 ); - - req.bootp.xid = rancopy = _hz_200; - req.bootp.secs = (_hz_200 - starttime) / HZ; - - if ((err = udp_send( (UDP *)&req, sizeof(req.bootp), - UDP_BOOTPC, UDP_BOOTPS )) < 0) { - printf( "bootp send: %s\n", ErrStr[-err-1] ); - continue; - } - - if ((err = udp_rcv( (UDP *)reply, &len, - UDP_BOOTPS, UDP_BOOTPC )) < 0) { - printf( "bootp rcv: %s\n", ErrStr[-err-1] ); - continue; - } - if (len < sizeof(struct bootp)) { - printf( "received short BOOTP packet (%d bytes)\n", len ); - continue; - } - - if (reply->bootp.xid == rancopy) - /* Ok, got the answer */ - break; - printf( "bootp: xid mismatch\n" ); - } - if (retry >= BOOTP_RETRYS) { - printf( "No response from a bootp server\n" ); - return( -2 ); - } - - ServerIPaddr = reply->bootp.siaddr; - memcpy( ServerHwaddr, reply->ether.src_addr, ETHADDRLEN ); - printf( "\nBoot server is " ); - if (strlen(reply->bootp.sname) > 0) - printf( "%s, IP ", reply->bootp.sname ); - print_ip( ServerIPaddr ); - printf( ", HW address " ); - print_hw( ServerHwaddr ); - printf( "\n" ); - - MyIPaddr = reply->bootp.yiaddr; - printf( "My IP address is " ); - print_ip( MyIPaddr ); - printf( "\n" ); - - strcpy( image_name, reply->bootp.file ); - return( 0 ); -} - - -/* --------------------------------------------------------------------- */ -/* TFTP Procedure */ - - -static int tftp( char *image_name ) - -{ TFTP spkt; - Packet _rpkt; - TFTP *rpkt = (TFTP *)&_rpkt; - unsigned short mytid, rtid = 0; - int blk, retries, i, wpos, err, len, datalen; - static char rotchar[4] = { '|', '/', '-', '\\' }; - - retries = 5; - /* Construct and send a read request */ - repeat_req: - spkt.tftp.req.opcode = TFTP_RRQ; - strcpy( spkt.tftp.req.name, image_name ); - strcpy( spkt.tftp.req.name + strlen(spkt.tftp.req.name) + 1, "octet" ); - mytid = _hz_200 & 0xffff; - - if ((err = udp_send( (UDP *)&spkt, sizeof(spkt.tftp.req.opcode) + - strlen(image_name) + 1 + - strlen( "octect" ) +1, - mytid, UDP_TFTP )) < 0) { - printf( "TFTP RREQ: %s\n", ErrStr[-err-1] ); - if (--retries > 0) - goto repeat_req; - return( err == ETIMEO ? -2 : -1 ); - } - - retries = 5; - for( i = 0; i < KFILE_N_CHUNKS; ++i ) - KFile[i] = NULL; - wpos = 0; - printf( "Receiving kernel image %s:\n", image_name ); - - for( blk = 1; ; ++blk ) { - - repeat_data: - if ((err = udp_rcv( (UDP *)rpkt, &len, rtid, mytid )) < 0) { - printf( "TFTP rcv: %s\n", ErrStr[-err-1] ); - if (--retries > 0) - goto repeat_data; - goto err; - } - if (rtid == 0) - /* Store the remote port at the first packet received */ - rtid = rpkt->udp.src_port; - - if (rpkt->tftp.opcode == TFTP_ERROR) { - if (strlen(rpkt->tftp.error.str) > 0) - printf( "TFTP error: %s\n", rpkt->tftp.error.str ); - else - printf( "TFTP error #%d (no description)\n", - rpkt->tftp.error.errcode ); - goto err; - } - else if (rpkt->tftp.opcode != TFTP_DATA) { - printf( "Bad TFTP packet type: %d\n", rpkt->tftp.opcode ); - if (--retries > 0) - goto repeat_data; - goto err; - } - - if (rpkt->tftp.data.nr != blk) { - /* doubled data packet; ignore it */ - goto repeat_data; - } - datalen = len - sizeof(rpkt->tftp.data.opcode) - - sizeof(rpkt->tftp.data.nr); - - /* store data */ - if (datalen > 0) { - int chunk = wpos >> KFILE_CHUNK_BITS; - if (chunk >= KFILE_N_CHUNKS) { - printf( "TFTP: file too large! Aborting.\n" ); - out_of_mem: - spkt.tftp.error.opcode = TFTP_ERROR; - spkt.tftp.error.errcode = 3; - strcpy( spkt.tftp.error.str, "Out of memory" ); - udp_send( (UDP *)&spkt, sizeof(spkt.tftp.ack), mytid, rtid ); - goto err; - } - if (!KFile[chunk]) { - if (!(KFile[chunk] = malloc( KFILE_CHUNK_SIZE ))) { - printf( "TFTP: Out of memory for kernel image\n" ); - goto out_of_mem; - } - } - memcpy( KFile[chunk] + (wpos & KFILE_CHUNK_MASK), - rpkt->tftp.data.data, datalen ); - wpos += datalen; - -#define DISPLAY_BITS 13 - if ((wpos & ((1 << DISPLAY_BITS)-1)) == 0) { - printf( "\r %c %7d Bytes ", - rotchar[(wpos>>DISPLAY_BITS)&3], wpos ); - fflush( stdout ); - } - } - - /* Send ACK packet */ - repeat_ack: - spkt.tftp.ack.opcode = TFTP_ACK; - spkt.tftp.ack.nr = blk; - if ((err = udp_send( (UDP *)&spkt, sizeof(spkt.tftp.ack), - mytid, rtid )) < 0) { - printf( "TFTP ACK: %s\n", ErrStr[-err-1] ); - if (--retries > 0) - goto repeat_ack; - goto err; - } - - if (datalen < 512) { - /* This was the last packet */ - printf( "\r %7d Bytes done\n\n", wpos ); - break; - } - - retries = 5; - } - - KFileSize = wpos; - return( 0 ); - - err: - free_kfile(); - return( -1 ); -} - - - -/* --------------------------------------------------------------------- */ -/* UDP/IP Protocol Quick Hack Implementation */ - - -static int udp_send( UDP *pkt, int len, int fromport, int toport ) - -{ - /* UDP layer */ - pkt->udp.src_port = fromport; - pkt->udp.dst_port = toport; - pkt->udp.len = (len += sizeof(struct udphdr)); - pkt->udp.chksum = 0; /* Too lazy to calculate :-) */ - - /* IP layer */ - pkt->ip.version = 4; - pkt->ip.ihl = 5; - pkt->ip.tos = 0; - pkt->ip.tot_len = (len += sizeof(struct iphdr)); - pkt->ip.id = 0; - pkt->ip.frag_off = 0; - pkt->ip.ttl = 255; - pkt->ip.protocol = 17; /* UDP */ - pkt->ip.src_addr = MyIPaddr; - pkt->ip.dst_addr = ServerIPaddr; - pkt->ip.chksum = 0; - pkt->ip.chksum = ip_checksum( &pkt->ip ); - - /* Ethernet layer */ - memcpy( &pkt->ether.dst_addr, ServerHwaddr, ETHADDRLEN ); - memcpy( &pkt->ether.src_addr, MyHwaddr, ETHADDRLEN ); - pkt->ether.type = 0x0800; - len += sizeof(struct etherhdr); - - return( eth_send( (Packet *)pkt, len ) ); -} - - -static unsigned short ip_checksum( struct iphdr *buf ) - -{ unsigned long sum = 0, wlen = 5; - - __asm__ ("subqw #1,%2\n" - "1:\t" - "movel %1@+,%/d0\n\t" - "addxl %/d0,%0\n\t" - "dbra %2,1b\n\t" - "movel %0,%/d0\n\t" - "swap %/d0\n\t" - "addxw %/d0,%0\n\t" - "clrw %/d0\n\t" - "addxw %/d0,%0" - : "=d" (sum), "=a" (buf), "=d" (wlen) - : "0" (sum), "1" (buf), "2" (wlen) - : "d0"); - return( (~sum) & 0xffff ); -} - - -static int udp_rcv( UDP *pkt, int *len, int fromport, int atport ) - -{ int err; - - repeat: - if ((err = eth_rcv( (Packet *)pkt, len ))) - return( err ); - - /* Ethernet layer */ - if (pkt->ether.type == 0x0806) { - /* ARP */ - ARP *pk = (ARP *)pkt; - unsigned char *shw, *sip, *thw, *tip; - - if (pk->arp.hrd != 1 || pk->arp.pro != 0x0800 || - pk->arp.op != 1 || MyIPaddr == IP_Unknown_Addr) - /* Wrong hardware type or protocol; or reply -> ignore */ - goto repeat; - shw = pk->arp.addr; - sip = shw + pk->arp.hln; - thw = sip + pk->arp.pln; - tip = thw + pk->arp.hln; - - if (memcmp( tip, &MyIPaddr, pk->arp.pln ) == 0) { - memcpy( thw, shw, pk->arp.hln ); - memcpy( tip, sip, pk->arp.pln ); - memcpy( shw, &MyHwaddr, pk->arp.hln ); - memcpy( sip, &MyIPaddr, pk->arp.pln ); - - memcpy( &pk->ether.dst_addr, thw, ETHADDRLEN ); - memcpy( &pk->ether.src_addr, &MyHwaddr, ETHADDRLEN ); - eth_send( (Packet *)pk, *len ); - } - goto repeat; - } - else if (pkt->ether.type != 0x0800) { - printf( "Unknown Ethernet packet type %04x received\n", - pkt->ether.type ); - goto repeat; - } - - /* IP layer */ - if (MyIPaddr != IP_Unknown_Addr && pkt->ip.dst_addr != MyIPaddr) { - printf( "Received packet for wrong IP address\n" ); - goto repeat; - } - if (ServerIPaddr != IP_Unknown_Addr && - ServerIPaddr != IP_Broadcast_Addr && - pkt->ip.src_addr != ServerIPaddr) { - printf( "Received packet from wrong server\n" ); - goto repeat; - } - /* If IP header is longer than 5 longs, delete the options */ - if (pkt->ip.ihl > 5) { - char *udpstart = (char *)((long *)&pkt->ip + pkt->ip.ihl); - memmove( &pkt->udp, udpstart, *len - (udpstart-(char *)pkt) ); - } - - /* UDP layer */ - if (fromport != 0 && pkt->udp.src_port != fromport) { - printf( "Received packet from wrong port %d\n", pkt->udp.src_port ); - goto repeat; - } - if (pkt->udp.dst_port != atport) { - printf( "Received packet at wrong port %d\n", pkt->udp.dst_port ); - goto repeat; - } - - *len = pkt->udp.len - sizeof(struct udphdr); - return( 0 ); -} - - -/* --------------------------------------------------------------------- */ -/* Address Printing */ - - -static void print_ip( IPADDR addr ) - -{ - printf( "%ld.%ld.%ld.%ld", - (addr >> 24) & 0xff, - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff ); -} - - -static void print_hw( HWADDR addr ) - -{ - printf( "%02x:%02x:%02x:%02x:%02x:%02x", - addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] ); -} - - -/* --------------------------------------------------------------------- */ -/* Ethernet Interface Abstraction Layer */ - - -#ifdef ETHLL_LANCE -#include "ethlance.h" -#endif - -static ETHIF_SWITCH *PossibleInterfaces[] = { -#ifdef ETHLL_LANCE - &LanceSwitch, -#endif -}; - -#define N_PossibleInterfaces (sizeof(PossibleInterfaces)/sizeof(*PossibleInterfaces)) - -/* Detected interface */ -static ETHIF_SWITCH *Ethif = NULL; - - -static int check_ethif( void ) - -{ int i; - - /* Check for configured interfaces */ - Ethif = NULL; - for( i = 0; i < N_PossibleInterfaces; ++i ) { - if (PossibleInterfaces[i]->probe() >= 0) { - Ethif = PossibleInterfaces[i]; - break; - } - } - if (!Ethif) - return( -1 ); - - if (Ethif->init() < 0) { - printf( "Ethernet interface initialization failed\n" ); - return( -1 ); - } - Ethif->get_hwaddr( &MyHwaddr ); - return( 0 ); -} - - -static int eth_send( Packet *pkt, int len ) - -{ - return( Ethif->snd( pkt, len )); -} - - -static int eth_rcv( Packet *pkt, int *len ) - -{ - return( Ethif->rcv( pkt, len )); -} - - -#if 0 -static void dump_packet( UDP *pkt ) - -{ int i, l; - unsigned char *p; - - printf( "Packet dump:\n" ); - - printf( "Ethernet header:\n" ); - printf( " dst addr: " ); print_hw( pkt->ether.dst_addr ); printf( "\n" ); - printf( " src addr: " ); print_hw( pkt->ether.src_addr ); printf( "\n" ); - printf( " type: %04x\n", pkt->ether.type ); - - printf( "IP header:\n" ); - printf( " version: %d\n", pkt->ip.version ); - printf( " hdr len: %d\n", pkt->ip.ihl ); - printf( " tos: %d\n", pkt->ip.tos ); - printf( " tot_len: %d\n", pkt->ip.tot_len ); - printf( " id: %d\n", pkt->ip.id ); - printf( " frag_off: %d\n", pkt->ip.frag_off ); - printf( " ttl: %d\n", pkt->ip.ttl ); - printf( " prot: %d\n", pkt->ip.protocol ); - printf( " src addr: " ); print_ip( pkt->ip.src_addr ); printf( "\n" ); - printf( " dst addr: " ); print_ip( pkt->ip.dst_addr ); printf( "\n" ); - - printf( "UDP header:\n" ); - printf( " src port: %d\n", pkt->udp.src_port ); - printf( " dst port: %d\n", pkt->udp.dst_port ); - printf( " len: %d\n", pkt->udp.len ); - - printf( "Data:" ); - l = pkt->udp.len - sizeof(pkt->udp); - p = (unsigned char *)&pkt->udp + sizeof(pkt->udp); - for( i = 0; i < l; ++i ) { - if ((i % 32) == 0) - printf( "\n %04x ", i ); - printf( "%02x ", *p ); - } - printf( "\n" ); -} -#endif diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/atari/bootp.h linux/arch/m68k/boot/atari/bootp.h --- v2.1.47/linux/arch/m68k/boot/atari/bootp.h Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/boot/atari/bootp.h Wed Dec 31 16:00:00 1969 @@ -1,44 +0,0 @@ -#ifndef _bootp_h -#define _bootp_h - -/* --------------------------------------------------------------------- */ -/* Ethernet Definitions */ - -#define PKTLEN 1544 -typedef unsigned char Packet[PKTLEN]; - -#define ETHADDRLEN 6 -typedef unsigned char HWADDR[ETHADDRLEN]; - -typedef struct { - int (*probe)( void ); - int (*init)( void ); - void (*get_hwaddr)( HWADDR *addr ); - int (*snd)( Packet *pkt, int len ); - int (*rcv)( Packet *pkt, int *len ); -} ETHIF_SWITCH; - - -/* error codes */ -#define ETIMEO -1 /* Timeout */ -#define ESEND -2 /* General send error (carrier, abort, ...) */ -#define ERCV -3 /* General receive error */ -#define EFRAM -4 /* Framing error */ -#define EOVERFL -5 /* Overflow (too long packet) */ -#define ECRC -6 /* CRC error */ - - -typedef unsigned long IPADDR; - - -/***************************** Prototypes *****************************/ - -int get_remote_kernel( const char *kname ); -int ll_read( int fd, void *buf, unsigned cnt ); -int ll_lseek( int fd, int where, int whence ); -int ll_close( int fd ); - -/************************* End of Prototypes **************************/ - -#endif /* _bootp_h */ - diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/atari/bootstrap.c linux/arch/m68k/boot/atari/bootstrap.c --- v2.1.47/linux/arch/m68k/boot/atari/bootstrap.c Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/boot/atari/bootstrap.c Wed Dec 31 16:00:00 1969 @@ -1,1602 +0,0 @@ -/* -** bootstrap.c -- Load and launch the Atari Linux kernel -** -** Copyright 1993 by Arjan Knor -** -** 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. -** -** History: -** 01 Feb 1997 Implemented kernel decompression (Roman) -** 28 Nov 1996 Fixed and tested previous change (James) -** 27 Nov 1996 Compatibility with bootinfo interface version 1.0 (Geert) -** 12 Nov 1996 Fixed and tested previous change (Andreas) -** 18 Aug 1996 Updated for the new boot information structure (untested!) -** (Geert) -** 10 Dec 1995 BOOTP/TFTP support (Roman) -** 03 Oct 1995 Allow kernel to be loaded to TT ram again (Andreas) -** 11 Jul 1995 Add support for ELF format kernel (Andreas) -** 16 Jun 1995 Adapted to Linux 1.2: kernel always loaded into ST ram -** (Andreas) -** 14 Nov 1994 YANML (Yet Another New Memory Layout :-) kernel -** start address is KSTART_ADDR + PAGE_SIZE, this -** does not need the ugly kludge with -** -fwritable-strings (++andreas) -** 09 Sep 1994 Adapted to the new memory layout: All the boot_info entry -** mentions all ST-Ram and the mover is located somewhere -** in the middle of memory (roman) -** Added the default arguments file known from the other -** bootstrap version -** 19 Feb 1994 Changed everything so that it works? (rdv) -** 14 Mar 1994 New mini-copy routine used (rdv) -*/ - - -#define BOOTINFO_COMPAT_1_0 /* bootinfo interface version 1.0 compatible */ -/* support compressed kernels? */ -#define ZKERNEL - -#include -#include -#include -#include -#include -#include -#include "sysvars.h" -#include -#include -#include - -/* linux specific include files */ -#include -#include -#include - -#define _LINUX_TYPES_H /* Hack to prevent including */ -#include -#include - -/* Atari bootstrap include file */ -#include "bootstrap.h" - -#define MIN_RAMSIZE (3) /* 3 MB */ -#define TEMP_STACKSIZE 256 - -extern char *optarg; -extern int optind; -static void get_default_args( int *argc, char ***argv ); -static int create_bootinfo(void); -#ifdef BOOTINFO_COMPAT_1_0 -static int create_compat_bootinfo(void); -#endif /* BOOTINFO_COMPAT_1_0 */ -static int add_bi_record(u_short tag, u_short size, const void *data); -static int add_bi_string(u_short tag, const u_char *s); -/* This is missing in */ -extern int sync (void); - -/* Bootinfo */ -static struct atari_bootinfo bi; - -#ifdef BOOTINFO_COMPAT_1_0 -static struct compat_bootinfo compat_bootinfo; -#endif /* BOOTINFO_COMPAT_1_0 */ - -#define MAX_BI_SIZE (4096) -static u_long bi_size; -static union { -struct bi_record record; - u_char fake[MAX_BI_SIZE]; -} bi_union; - -u_long *cookiejar; -u_long userstk; - -/* getcookie -- function to get the value of the given cookie. */ -static int getcookie(char *cookie, u_long *value) -{ - int i = 0; - - while(cookiejar[i] != 0L) { - if(cookiejar[i] == *(u_long *)cookie) { - *value = cookiejar[i + 1]; - return 1; - } - i += 2; - } - return -1; -} - -static void usage(void) -{ - fprintf(stderr, "Usage:\n" - "\tbootstrap [-dst] [-k kernel_executable] [-r ramdisk_file]" - " [option...]\n"); - exit(EXIT_FAILURE); -} - -/* - * Copy the kernel and the ramdisk to their final resting places. - * - * I assume that the kernel data and the ramdisk reside somewhere - * in the middle of the memory. - * - * This program itself should be somewhere in the first 4096 bytes of memory - * where the kernel never will be. In this way it can never be overwritten - * by itself. - * - * At this point the registers have: - * a0: the start of the final kernel - * a1: the start of the current kernel - * a2: the end of the final ramdisk - * a3: the end of the current ramdisk - * d0: the kernel size - * d1: the ramdisk size - */ -asm (" -.text -.globl _copyall, _copyallend -_copyall: - - movel a0,a4 /* save the start of the kernel for booting */ - -1: movel a1@+,a0@+ /* copy the kernel starting at the beginning */ - subql #4,d0 - jcc 1b - - tstl d1 - beq 3f - -2: movel a3@-,a2@- /* copy the ramdisk starting at the end */ - subql #4,d1 - jcc 2b - -3: jmp a4@ /* jump to the start of the kernel */ -_copyallend: -"); - -extern char copyall, copyallend; - - -/* Test for a Medusa: This is the only machine on which address 0 is - * writeable! - * ...err! On the Afterburner040 (for the Falcon) it's the same... So we do - * another test with 0x00ff82fe, that gives a bus error on the Falcon, but is - * in the range where the Medusa always asserts DTACK. - * On the Hades address 0 is writeable as well and it asserts DTACK on - * address 0x00ff82fe. To test if the machine is a Hades, address 0xb0000000 - * is tested. On the Medusa this gives a bus error. - */ - -int test_medusa( void ) - -{ int rv = 0; - - __asm__ __volatile__ - ( "movel 0x8,a0\n\t" - "movel sp,a1\n\t" - "moveb 0x0,d1\n\t" - "movel #Lberr,0x8\n\t" - "moveq #0,%0\n\t" - "clrb 0x0\n\t" - "nop \n\t" - "moveb d1,0x0\n\t" - "nop \n\t" - "tstb 0x00ff82fe\n\t" - "nop \n\t" - "moveq #1,%0\n\t" - "tstb 0xb0000000\n\t" - "nop \n\t" - "moveq #0,%0\n" - "Lberr:\t" - "movel a1,sp\n\t" - "movel a0,0x8" - : "=d" (rv) - : /* no inputs */ - : "d1", "a0", "a1", "memory" ); - - return( rv ); -} - - -/* Test if FPU instructions are executed in hardware, or if they're - emulated in software. For this, the F-line vector is temporarily - replaced. */ - -int test_software_fpu(void) -{ - int rv = 0; - - __asm__ __volatile__ - ( "movel 0x2c,a0\n\t" - "movel sp,a1\n\t" - "movel #Lfline,0x2c\n\t" - "moveq #1,%0\n\t" - "fnop \n\t" - "nop \n\t" - "moveq #0,%0\n" - "Lfline:\t" - "movel a1,sp\n\t" - "movel a0,0x2c" - : "=d" (rv) - : /* no inputs */ - : "a0", "a1" ); - - return rv; -} - - -void get_medusa_bank_sizes( u_long *bank1, u_long *bank2 ) - -{ static u_long save_addr; - u_long test_base, saved_contents[16]; -#define TESTADDR(i) (*((u_long *)((char *)test_base + i*8*MB))) -#define TESTPAT 0x12345678 - unsigned short oldflags; - int i; - - /* This ensures at least that none of the test addresses conflicts - * with the test code itself */ - test_base = ((unsigned long)&save_addr & 0x007fffff) | 0x20000000; - *bank1 = *bank2 = 0; - - /* Interrupts must be disabled because arbitrary addresses may be - * temporarily overwritten, even code of an interrupt handler */ - __asm__ __volatile__ ( "movew sr,%0; oriw #0x700,sr" : "=g" (oldflags) : ); - disable_cache(); - - /* save contents of the test addresses */ - for( i = 0; i < 16; ++i ) - saved_contents[i] = TESTADDR(i); - - /* write 0s into all test addresses */ - for( i = 0; i < 16; ++i ) - TESTADDR(i) = 0; - - /* test for bank 1 */ -#if 0 - /* This is Freddi's original test, but it didn't work. */ - TESTADDR(0) = TESTADDR(1) = TESTPAT; - if (TESTADDR(1) == TESTPAT) { - if (TESTADDR(2) == TESTPAT) - *bank1 = 8*MB; - else if (TESTADDR(3) == TESTPAT) - *bank1 = 16*MB; - else - *bank1 = 32*MB; - } - else { - if (TESTADDR(2) == TESTPAT) - *bank1 = 0; - else - *bank1 = 16*MB; - } -#else - TESTADDR(0) = TESTPAT; - if (TESTADDR(1) == TESTPAT) - *bank1 = 8*MB; - else if (TESTADDR(2) == TESTPAT) - *bank1 = 16*MB; - else if (TESTADDR(4) == TESTPAT) - *bank1 = 32*MB; - else - *bank1 = 64*MB; -#endif - - /* test for bank2 */ - if (TESTADDR(8) != 0) - *bank2 = 0; - else { - TESTADDR(8) = TESTPAT; - if (TESTADDR(9) != 0) { - if (TESTADDR(10) == TESTPAT) - *bank2 = 8*MB; - else - *bank2 = 32*MB; - } - else { - TESTADDR(9) = TESTPAT; - if (TESTADDR(10) == TESTPAT) - *bank2 = 16*MB; - else - *bank2 = 64*MB; - } - } - - /* restore contents of the test addresses and restore interrupt mask */ - for( i = 0; i < 16; ++i ) - TESTADDR(i) = saved_contents[i]; - __asm__ __volatile__ ( "movew %0,sr" : : "g" (oldflags) ); -} - -#undef TESTADDR -#undef TESTPAT - - -static int check_bootinfo_version(char *memptr) -{ - struct bootversion *bv = (struct bootversion *)memptr; - unsigned long version = 0; - int i, kernel_major, kernel_minor, boots_major, boots_minor; - - printf( "\n" ); - if (bv->magic == BOOTINFOV_MAGIC) { - for( i = 0; bv->machversions[i].machtype != 0; ++i ) { - if (bv->machversions[i].machtype == MACH_ATARI) { - version = bv->machversions[i].version; - break; - } - } - } - if (!version) - printf("Kernel has no bootinfo version info, assuming 0.0\n"); - - kernel_major = BI_VERSION_MAJOR(version); - kernel_minor = BI_VERSION_MINOR(version); - boots_major = BI_VERSION_MAJOR(ATARI_BOOTI_VERSION); - boots_minor = BI_VERSION_MINOR(ATARI_BOOTI_VERSION); - printf("Bootstrap's bootinfo version: %d.%d\n", - boots_major, boots_minor); - printf("Kernel's bootinfo version : %d.%d\n", - kernel_major, kernel_minor); - - switch (kernel_major) { - case BI_VERSION_MAJOR(ATARI_BOOTI_VERSION): - if (kernel_minor > boots_minor) { - printf("Warning: Bootinfo version of bootstrap and kernel " - "differ!\n"); - printf(" Certain features may not work.\n"); - } - break; - -#ifdef BOOTINFO_COMPAT_1_0 - case BI_VERSION_MAJOR(COMPAT_ATARI_BOOTI_VERSION): - printf("(using backwards compatibility mode)\n"); - break; -#endif /* BOOTINFO_COMPAT_1_0 */ - - default: - printf("\nThis bootstrap is too %s for this kernel!\n", - boots_major < kernel_major ? "old" : "new"); - return 0; - } - return kernel_major; -} - - -#ifdef USE_BOOTP -# include "bootp.h" -#else -# define ll_read read -# define ll_lseek lseek -# define ll_close close -#endif - -#ifdef ZKERNEL -static int load_zkernel( int fd ); -static int kread( int fd, void *buf, unsigned cnt ); -static int klseek( int fd, int where, int whence ); -static int kclose( int fd ); -#else -# define kread read -# define klseek lseek -# define kclose close -#endif - -/* ++andreas: this must be inline due to Super */ -static inline void boot_exit (int) __attribute__ ((noreturn)); -static inline void boot_exit(int status) -{ - /* first go back to user mode */ - (void)Super(userstk); - getchar(); - exit(status); -} - -int main(int argc, char *argv[]) -{ - int debugflag = 0, ch, kfd, rfd = -1, i, ignore_ttram = 0; - int load_to_stram = 0; - char *ramdisk_name, *kernel_name, *memptr; - u_long ST_ramsize, TT_ramsize, memreq; - u_long cpu_type, fpu_type, mch_type, mint; - struct exec kexec; - int elf_kernel = 0; - Elf32_Ehdr kexec_elf; - Elf32_Phdr *kernel_phdrs = NULL; - u_long start_mem, mem_size, rd_size, text_offset = 0, kernel_size; - int prefer_bootp = 1, kname_set = 0, n_knames; -#ifdef USE_BOOTP - int err; -#endif - char kname_list[5][64]; - void *bi_ptr; - - ramdisk_name = NULL; - kernel_name = "vmlinux"; - - /* print the startup message */ - puts("\fLinux/68k Atari Bootstrap version 2.2" -#ifdef USE_BOOTP - " (with BOOTP)" -#endif - ); - puts("Copyright 1993,1994 by Arjan Knor, Robert de Vries, Roman Hodek, Andreas Schwab\n"); - - /* ++roman: If no arguments on the command line, read them from - * file */ - if (argc == 1) - get_default_args( &argc, &argv ); - - /* machine is Atari */ - bi.machtype = MACH_ATARI; - - /* check arguments */ - while ((ch = getopt(argc, argv, "bdtsk:r:")) != EOF) - switch (ch) { - case 'd': - debugflag = 1; - break; - case 't': - ignore_ttram = 1; - break; - case 's': - load_to_stram = 1; - break; - case 'k': - kernel_name = optarg; - kname_set = 1; - break; - case 'r': - ramdisk_name = optarg; - break; - case 'b': - prefer_bootp = 0; - break; - case '?': - default: - usage(); - } - - argc -= optind; - argv += optind; - - /* We have to access some system variables to get - * the information we need, so we must switch to - * supervisor mode first. - */ - userstk = Super(0L); - - /* get the info we need from the cookie-jar */ - cookiejar = *_p_cookies; - if(cookiejar == 0L) { - /* if we find no cookies, it's probably an ST */ - fprintf(stderr, "Error: No cookiejar found. Is this an ST?\n"); - boot_exit(EXIT_FAILURE); - } - - /* Exit if MiNT/MultiTOS is running. */ - if(getcookie("MiNT", &mint) != -1) - { - puts("Warning: MiNT is running\n"); -#if 0 - puts("Linux cannot be started when MiNT is running. Aborting...\n"); - boot_exit(EXIT_FAILURE); -#endif - } - - /* get _CPU, _FPU and _MCH */ - getcookie("_CPU", &cpu_type); - getcookie("_FPU", &fpu_type); - getcookie("_MCH", &mch_type); - - /* check if we are on a 68030/40 with FPU */ - if ((cpu_type != 30 && cpu_type != 40 && cpu_type != 60)) - { - puts("Machine type currently not supported. Aborting..."); - boot_exit(EXIT_FAILURE); - } - - switch(cpu_type) { - case 0: - case 10: break; - case 20: bi.cputype = CPU_68020; bi.mmutype = MMU_68851; break; - case 30: bi.cputype = CPU_68030; bi.mmutype = MMU_68030; break; - case 40: bi.cputype = CPU_68040; bi.mmutype = MMU_68040; break; - case 60: bi.cputype = CPU_68060; bi.mmutype = MMU_68060; break; - default: - fprintf(stderr, "Error: Unknown CPU type. Aborting...\n"); - boot_exit(EXIT_FAILURE); - break; - } - - printf("CPU: %ld; ", cpu_type + 68000); - printf("FPU: "); - - /* check for FPU; in case of a '040 or '060, don't look at _FPU itself, - * some software may set it to wrong values (68882 or the like) */ - if (cpu_type == 40) { - bi.fputype = FPU_68040; - puts( "68040\n" ); - } - else if (cpu_type == 60) { - bi.fputype = FPU_68060; - puts( "68060\n" ); - } - else { - switch ((fpu_type >> 16) & 7) { - case 0: - puts("not present\n"); - break; - case 1: - puts("SFP004 not supported. Assuming no FPU."); - break; - case 2: - /* try to determine real type */ - if (fpu_idle_frame_size () != 0x18) - goto m68882; - /* fall through */ - case 4: - bi.fputype = FPU_68881; - puts("68881\n"); - break; - case 6: - m68882: - bi.fputype = FPU_68882; - puts("68882\n"); - break; - default: - puts("Unknown FPU type. Assuming no FPU."); - break; - } - } - /* ++roman: If an FPU was announced in the cookie, test - whether it is a real hardware FPU or a software emulator! */ - if (bi.fputype) { - if (test_software_fpu()) { - bi.fputype = 0; - puts("FPU: software emulated. Assuming no FPU."); - } - } - - /* Get the amounts of ST- and TT-RAM. */ - /* The size must be a multiple of 1MB. */ - i = 0; - - if (!test_medusa()) { - struct { - unsigned short version; /* version - currently 1 */ - unsigned long fr_start; /* start addr FastRAM */ - unsigned long fr_len; /* length FastRAM */ - } *magn_cookie; - struct { - unsigned long version; - unsigned long fr_start; /* start addr */ - unsigned long fr_len; /* length */ - } *fx_cookie; - - TT_ramsize = 0; - if (!ignore_ttram) { - /* "Original" or properly emulated TT-Ram */ - if (*ramtop) { - /* the 'ramtop' variable at 0x05a4 is not - * officially documented. We use it anyway - * because it is the only way to get the TTram size. - * (It is zero if there is no TTram.) - */ - bi.memory[i].addr = TT_RAM_BASE; - bi.memory[i].size = (*ramtop - TT_RAM_BASE) & ~(MB - 1); - TT_ramsize = bi.memory[i].size / MB; - i++; - printf("TT-RAM: %ld Mb; ", TT_ramsize); - } - - /* test for MAGNUM alternate RAM - * added 26.9.1995 M. Schwingen, rincewind@discworld.oche.de - */ - if (getcookie("MAGN", (u_long *)&magn_cookie) != -1) { - bi.memory[i].addr = magn_cookie->fr_start; - bi.memory[i].size = magn_cookie->fr_len & ~(MB - 1); - TT_ramsize += bi.memory[i].size / MB; - printf("MAGNUM alternate RAM: %ld Mb; ", bi.memory[i].size/MB); - i++; - } - - /* BlowUps FX */ - if (getcookie("BPFX", (u_long *)&fx_cookie) != -1 && fx_cookie) { - /* if fx is set (cookie call above), - * we assume that BlowUps FX-card - * is installed. (Nat!) - */ - bi.memory[i].addr = fx_cookie->fr_start; - bi.memory[i].size = fx_cookie->fr_len & ~(MB - 1); - printf("FX alternate RAM: %ld Mb; ", bi.memory[i].size/MB); - i++; - } - } - - bi.memory[i].addr = 0; - bi.memory[i].size = *phystop & ~(MB - 1); - ST_ramsize = bi.memory[i].size / MB; - i++; - printf("ST-RAM: %ld Mb\n", ST_ramsize ); - - bi.num_memory = i; - - if (load_to_stram && i > 1) { - /* Put ST-RAM first in the list of mem blocks */ - struct mem_info temp = bi.memory[i - 1]; - bi.memory[i - 1] = bi.memory[0]; - bi.memory[0] = temp; - } - } - else { - u_long bank1, bank2, medusa_st_ram; - - get_medusa_bank_sizes( &bank1, &bank2 ); - medusa_st_ram = *phystop & ~(MB - 1); - bank1 -= medusa_st_ram; - TT_ramsize = 0; - - bi.memory[i].addr = 0; - bi.memory[i].size = medusa_st_ram; - ST_ramsize = bi.memory[i].size / MB; - i++; - printf("Medusa pseudo ST-RAM from bank 1: %ld Mb; ", ST_ramsize ); - - if (!ignore_ttram && bank1 > 0) { - bi.memory[i].addr = 0x20000000 + medusa_st_ram; - bi.memory[i].size = bank1; - TT_ramsize += bank1; - i++; - printf("TT-RAM bank 1: %ld Mb; ", bank1/MB ); - } - - if (!ignore_ttram && bank2 > 0) { - bi.memory[i].addr = 0x24000000; - bi.memory[i].size = bank2; - TT_ramsize += bank2; - i++; - printf("TT-RAM bank 2: %ld Mb; ", bank2/MB ); - } - - bi.num_memory = i; - printf("\n"); - } - - /* verify that there is enough RAM; ST- and TT-RAM combined */ - if (ST_ramsize + TT_ramsize < MIN_RAMSIZE) { - puts("Not enough RAM. Aborting..."); - boot_exit(10); - } - -#if 0 - /* Get language/keyboard info */ - /* TODO: do we need this ? */ - /* Could be used to auto-select keyboard map later on. (rdv) */ - if (getcookie("_AKP",&language) == -1) - { - /* Get the language info from the OS-header */ - os_header = *_sysbase; - os_header = os_header->os_beg; - lang = (os_header->os_conf) >> 1; - printf("Language: "); - switch(lang) { - case HOL: puts("Dutch"); break; /* Own country first :-) */ - case USA: puts("American"); break; - case SWG: puts("Switzerland (German)"); break; - case FRG: puts("German"); break; - case FRA: puts("French"); break; - case SWF: puts("Switzerland (French)"); break; - case UK: puts("English"); break; - case SPA: puts("Spanish"); break; - case ITA: puts("Italian"); break; - case SWE: puts("Swedish"); break; - case TUR: puts("Turkey"); break; - case FIN: puts("Finnish"); break; - case NOR: puts("Norwegian"); break; - case DEN: puts("Danish"); break; - case SAU: puts("Saudi-Arabian"); break; - default: puts("Unknown"); break; - } - } - else - { - printf("Language: "); - switch(language & 0x0F) - { - case 1: printf("German "); break; - case 2: printf("French "); break; - case 4: printf("Spanish "); break; - case 5: printf("Italian "); break; - case 7: printf("Swiss French "); break; - case 8: printf("Swiss German "); break; - default: printf("English "); - } - printf("Keyboard type :"); - switch(language >> 8) - { - case 1: printf("German "); break; - case 2: printf("French "); break; - case 4: printf("Spanish "); break; - case 5: printf("Italian "); break; - case 7: printf("Swiss French "); break; - case 8: printf("Swiss German "); break; - default: printf("English "); - } - printf("\n"); - } -#endif - - /* Pass contents of the _MCH cookie to the kernel */ - bi.mch_cookie = mch_type; - - /* - * Copy command line options into the kernel command line. - */ - i = 0; - while (argc--) { - if ((i+strlen(*argv)+1) < CL_SIZE) { - i += strlen(*argv) + 1; - if (bi.command_line[0]) - strcat (bi.command_line, " "); - strcat (bi.command_line, *argv++); - } - } - printf ("Command line is '%s'\n", bi.command_line); - - start_mem = bi.memory[0].addr; - mem_size = bi.memory[0].size; - - /* tell us where the kernel will go */ - printf("\nThe kernel will be located at 0x%08lx\n", start_mem); - -#ifdef TEST - /* - ** Temporary exit point for testing - */ - boot_exit(-1); -#endif /* TEST */ - - i = 0; -#ifdef USE_BOOTP - if (!kname_set) - kname_list[i++][0] = '\0'; /* default kernel which BOOTP server says */ -#endif -#ifdef ZKERNEL - strcpy( kname_list[i], kernel_name ); - strcat( kname_list[i], ".gz" ); - ++i; -#endif - strcpy( kname_list[i++], kernel_name ); -#ifdef ZKERNEL - if (!kname_set) - strcpy( kname_list[i++], "vmlinuz" ); -#endif - n_knames = i; - - kfd = -1; -#ifdef USE_BOOTP - if (prefer_bootp) { - for( i = 0; i < n_knames; ++i ) { - if ((err = get_remote_kernel( kname_list[i] )) >= 0) - goto kernel_open; - if (err < -1) /* fatal error; retries don't help... */ - break; - } - printf( "\nremote boot failed; trying local kernel\n" ); - } -#endif - for( i = 0; i < n_knames; ++i ) { - if ((kfd = open( kname_list[i], O_RDONLY )) != -1) - goto kernel_open; - } -#ifdef USE_BOOTP - if (!prefer_bootp) { - printf( "\nlocal kernel failed; trying remote boot\n" ); - for( i = 0; i < n_knames; ++i ) { - if ((err = get_remote_kernel( kname_list[i] )) >= 0) - goto kernel_open; - if (err < -1) /* fatal error; retries don't help... */ - break; - } - } -#endif - fprintf( stderr, "Unable to open any kernel file\n(Tried " ); - for( i = 0; i < n_knames; ++i ) { - fprintf( stderr, "%s%s", kname_list[i], - i < n_knames-2 ? ", " : - i == n_knames-2 ? ", and " : - ")\n" ); - } - boot_exit( EXIT_FAILURE ); - - kernel_open: - - if (kread (kfd, (void *)&kexec, sizeof(kexec)) != sizeof(kexec)) - { - fprintf (stderr, "Unable to read exec header from %s\n", kernel_name); - boot_exit (EXIT_FAILURE); - } - -#ifdef ZKERNEL - if (((unsigned char *)&kexec)[0] == 037 && - (((unsigned char *)&kexec)[1] == 0213 || - ((unsigned char *)&kexec)[1] == 0236)) { - /* That's a compressed kernel */ - printf( "Kernel is compressed\n" ); - if (load_zkernel( kfd )) { - printf( "Decompression error -- aborting\n" ); - boot_exit( EXIT_FAILURE ); - } - } -#endif - - switch (N_MAGIC(kexec)) { - case ZMAGIC: - text_offset = N_TXTOFF(kexec); - break; - case QMAGIC: - text_offset = sizeof(kexec); - /* the text size includes the exec header; remove this */ - kexec.a_text -= sizeof(kexec); - break; - default: - /* Try to parse it as an ELF header */ - klseek (kfd, 0, SEEK_SET); - if (kread (kfd, (void *)&kexec_elf, sizeof (kexec_elf)) == sizeof (kexec_elf) - && memcmp (&kexec_elf.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0) - { - elf_kernel = 1; - /* A few plausibility checks */ - if (kexec_elf.e_type != ET_EXEC || kexec_elf.e_machine != EM_68K - || kexec_elf.e_version != EV_CURRENT) - { - fprintf (stderr, "Invalid ELF header contents in kernel\n"); - boot_exit (EXIT_FAILURE); - } - /* Load the program headers */ - kernel_phdrs = (Elf32_Phdr *) Malloc (kexec_elf.e_phnum * sizeof (Elf32_Phdr)); - if (kernel_phdrs == NULL) - { - fprintf (stderr, "Unable to allocate memory for program headers\n"); - boot_exit (EXIT_FAILURE); - } - klseek (kfd, kexec_elf.e_phoff, SEEK_SET); - if (kread (kfd, (void *) kernel_phdrs, - kexec_elf.e_phnum * sizeof (*kernel_phdrs)) - != kexec_elf.e_phnum * sizeof (*kernel_phdrs)) - { - fprintf (stderr, "Unable to read program headers from %s\n", - kernel_name); - boot_exit (EXIT_FAILURE); - } - break; - } - fprintf (stderr, "Wrong magic number %lo in kernel header\n", - N_MAGIC(kexec)); - boot_exit (EXIT_FAILURE); - } - - /* Load the kernel one page after start of mem */ - start_mem += PAGE_SIZE; - mem_size -= PAGE_SIZE; - /* Align bss size to multiple of four */ - if (!elf_kernel) - kexec.a_bss = (kexec.a_bss + 3) & ~3; - - /* init ramdisk */ - if(ramdisk_name) { - if((rfd = open(ramdisk_name, O_RDONLY)) == -1) { - fprintf(stderr, "Unable to open ramdisk file %s\n", - ramdisk_name); - boot_exit(EXIT_FAILURE); - } - bi.ramdisk.size = lseek(rfd, 0, SEEK_END); - } - else - bi.ramdisk.size = 0; - - /* calculate the total required amount of memory */ - if (elf_kernel) - { - u_long min_addr = 0xffffffff, max_addr = 0; - for (i = 0; i < kexec_elf.e_phnum; i++) - { - if (min_addr > kernel_phdrs[i].p_vaddr) - min_addr = kernel_phdrs[i].p_vaddr; - if (max_addr < kernel_phdrs[i].p_vaddr + kernel_phdrs[i].p_memsz) - max_addr = kernel_phdrs[i].p_vaddr + kernel_phdrs[i].p_memsz; - } - /* This is needed for newer linkers that include the header in - the first segment. */ - if (min_addr == 0) - { - min_addr = PAGE_SIZE; - kernel_phdrs[0].p_vaddr += PAGE_SIZE; - kernel_phdrs[0].p_offset += PAGE_SIZE; - kernel_phdrs[0].p_filesz -= PAGE_SIZE; - kernel_phdrs[0].p_memsz -= PAGE_SIZE; - } - kernel_size = max_addr - min_addr; - } - else - kernel_size = kexec.a_text + kexec.a_data + kexec.a_bss; - - rd_size = bi.ramdisk.size; - if (rd_size + kernel_size > mem_size - MB/2 && bi.num_memory > 1) - /* If running low on ST ram load ramdisk into alternate ram. */ - bi.ramdisk.addr = (u_long) bi.memory[1].addr + bi.memory[1].size - rd_size; - else - /* Else hopefully there is enough ST ram. */ - bi.ramdisk.addr = (u_long)start_mem + mem_size - rd_size; - - /* create the bootinfo structure */ - if (!create_bootinfo()) - boot_exit (EXIT_FAILURE); - - memreq = kernel_size + bi_size; -#ifdef BOOTINFO_COMPAT_1_0 - if (sizeof(compat_bootinfo) > bi_size) - memreq = kernel_size+sizeof(compat_bootinfo); -#endif /* BOOTINFO_COMPAT_1_0 */ - /* align load address of ramdisk image, read() is sloooow on odd addr. */ - memreq = ((memreq + 3) & ~3) + rd_size; - - /* allocate RAM for the kernel */ - if (!(memptr = (char *)Malloc (memreq))) - { - fprintf (stderr, "Unable to allocate memory for kernel and ramdisk\n"); - boot_exit (EXIT_FAILURE); - } - else - fprintf(stderr, "kernel at address %lx\n", (u_long) memptr); - - (void)memset(memptr, 0, memreq); - - /* read the text and data segments from the kernel image */ - if (elf_kernel) - { - for (i = 0; i < kexec_elf.e_phnum; i++) - { - if (klseek (kfd, kernel_phdrs[i].p_offset, SEEK_SET) == -1) - { - fprintf (stderr, "Failed to seek to segment %d\n", i); - boot_exit (EXIT_FAILURE); - } - if (kread (kfd, memptr + kernel_phdrs[i].p_vaddr - PAGE_SIZE, - kernel_phdrs[i].p_filesz) - != kernel_phdrs[i].p_filesz) - { - fprintf (stderr, "Failed to read segment %d\n", i); - boot_exit (EXIT_FAILURE); - } - } - } - else - { - if (klseek (kfd, text_offset, SEEK_SET) == -1) - { - fprintf (stderr, "Failed to seek to text\n"); - Mfree ((void *)memptr); - boot_exit (EXIT_FAILURE); - } - - if (kread (kfd, memptr, kexec.a_text) != kexec.a_text) - { - fprintf (stderr, "Failed to read text\n"); - Mfree ((void *)memptr); - boot_exit (EXIT_FAILURE); - } - - /* data follows immediately after text */ - if (kread (kfd, memptr + kexec.a_text, kexec.a_data) != kexec.a_data) - { - fprintf (stderr, "Failed to read data\n"); - Mfree ((void *)memptr); - boot_exit (EXIT_FAILURE); - } - } - kclose (kfd); - - /* Check kernel's bootinfo version */ - switch (check_bootinfo_version(memptr)) { - case BI_VERSION_MAJOR(ATARI_BOOTI_VERSION): - bi_ptr = &bi_union.record; - break; - -#ifdef BOOTINFO_COMPAT_1_0 - case BI_VERSION_MAJOR(COMPAT_ATARI_BOOTI_VERSION): - if (!create_compat_bootinfo()) { - Mfree ((void *)memptr); - boot_exit (EXIT_FAILURE); - } - bi_ptr = &compat_bootinfo; - bi_size = sizeof(compat_bootinfo); - break; -#endif /* BOOTINFO_COMPAT_1_0 */ - - default: - Mfree ((void *)memptr); - boot_exit (EXIT_FAILURE); - } - - /* copy the boot_info struct to the end of the kernel image */ - memcpy ((void *)(memptr + kernel_size), bi_ptr, bi_size); - - /* read the ramdisk image */ - if (rfd != -1) - { - if (lseek (rfd, 0, SEEK_SET) == -1) - { - fprintf (stderr, "Failed to seek to beginning of ramdisk file\n"); - Mfree ((void *)memptr); - boot_exit (EXIT_FAILURE); - } - if (read (rfd, memptr + memreq - rd_size, - rd_size) != rd_size) - { - fprintf (stderr, "Failed to read ramdisk file\n"); - Mfree ((void *)memptr); - boot_exit (EXIT_FAILURE); - } - close (rfd); - } - - /* for those who want to debug */ - if (debugflag) - { - if (bi.ramdisk.size) - printf ("RAM disk at %#lx, size is %ld\n", - (u_long)(memptr + memreq - rd_size), - bi.ramdisk.size); - - if (elf_kernel) - { - for (i = 0; i < kexec_elf.e_phnum; i++) - { - printf ("Kernel segment %d at %#lx, size %ld\n", i, - start_mem + kernel_phdrs[i].p_vaddr - PAGE_SIZE, - kernel_phdrs[i].p_memsz); - } - } - else - { - printf ("\nKernel text at %#lx, code size %d\n", - start_mem, kexec.a_text); - printf ("Kernel data at %#lx, data size %d\n", - start_mem + kexec.a_text, kexec.a_data ); - printf ("Kernel bss at %#lx, bss size %d\n", - start_mem + kexec.a_text + kexec.a_data, kexec.a_bss ); - } - printf ("\nboot_info is at %#lx\n", - start_mem + kernel_size); - printf ("\nKernel entry is %#lx\n", - elf_kernel ? kexec_elf.e_entry : kexec.a_entry); - printf ("ramdisk dest top is %#lx\n", bi.ramdisk.addr + rd_size); - printf ("ramdisk lower limit is %#lx\n", - (u_long)(memptr + memreq - rd_size)); - printf ("ramdisk src top is %#lx\n", (u_long)(memptr + memreq)); - - printf ("Type a key to continue the Linux boot..."); - fflush (stdout); - getchar(); - } - - printf("Booting Linux...\n"); - - sync (); - - /* turn off interrupts... */ - disable_interrupts(); - - /* turn off caches... */ - disable_cache(); - - /* ..and any MMU translation */ - disable_mmu(); - - /* ++guenther: allow reset if launched with MiNT */ - *(long*)0x426 = 0; - - /* copy mover code to a safe place if needed */ - memcpy ((void *) 0x400, ©all, ©allend - ©all); - - /* setup stack */ - change_stack ((void *) PAGE_SIZE); - - /* - * On the Atari you can have two situations: - * 1. One piece of contiguous RAM (Falcon) - * 2. Two pieces of contiguous RAM (TT) - * In case 2 you can load your program into ST-ram and load your data in - * any old RAM you have left. - * In case 1 you could overwrite your own program when copying the - * kernel and ramdisk to their final positions. - * To solve this the mover code is copied to a safe place first. - * Then this program jumps to the mover code. After the mover code - * has finished it jumps to the start of the kernel in its new position. - * I thought the memory just after the interrupt vector table was a safe - * place because it is used by TOS to store some system variables. - * This range goes from 0x400 to approx. 0x5B0. - * This is more than enough for the miniscule mover routine (16 bytes). - */ - - jump_to_mover((char *) start_mem, memptr, - (char *) bi.ramdisk.addr + rd_size, memptr + memreq, - kernel_size + bi_size, rd_size, - (void *) 0x400); - - for (;;); - /* NOTREACHED */ -} - - - -#define MAXARGS 30 - -static void get_default_args( int *argc, char ***argv ) - -{ FILE *f; - static char *nargv[MAXARGS]; - char arg[256], *p; - int c, quote, state; - - if (!(f = fopen( "bootargs", "r" ))) - return; - - *argc = 1; - if (***argv) - nargv[0] = **argv; - else - nargv[0] = "bootstrap"; - *argv = nargv; - - quote = state = 0; - p = arg; - while( (c = fgetc(f)) != EOF ) { - - if (state == 0) { - /* outside args, skip whitespace */ - if (!isspace(c)) { - state = 1; - p = arg; - } - } - - if (state) { - /* inside an arg: copy it into 'arg', obeying quoting */ - if (!quote && (c == '\'' || c == '"')) - quote = c; - else if (quote && c == quote) - quote = 0; - else if (!quote && isspace(c)) { - /* end of this arg */ - *p = 0; - nargv[(*argc)++] = strdup(arg); - state = 0; - } - else - *p++ = c; - } - } - if (state) { - /* last arg finished by EOF! */ - *p = 0; - nargv[(*argc)++] = strdup(arg); - } - fclose( f ); - - nargv[*argc] = 0; -} - - - /* - * Create the Bootinfo Structure - */ - -static int create_bootinfo(void) -{ - int i; - struct bi_record *record; - - /* Initialization */ - bi_size = 0; - - /* Generic tags */ - if (!add_bi_record(BI_MACHTYPE, sizeof(bi.machtype), &bi.machtype)) - return(0); - if (!add_bi_record(BI_CPUTYPE, sizeof(bi.cputype), &bi.cputype)) - return(0); - if (!add_bi_record(BI_FPUTYPE, sizeof(bi.fputype), &bi.fputype)) - return(0); - if (!add_bi_record(BI_MMUTYPE, sizeof(bi.mmutype), &bi.mmutype)) - return(0); - for (i = 0; i < bi.num_memory; i++) - if (!add_bi_record(BI_MEMCHUNK, sizeof(bi.memory[i]), &bi.memory[i])) - return(0); - if (bi.ramdisk.size) - if (!add_bi_record(BI_RAMDISK, sizeof(bi.ramdisk), &bi.ramdisk)) - return(0); - if (!add_bi_string(BI_COMMAND_LINE, bi.command_line)) - return(0); - - /* Atari tags */ - if (!add_bi_record(BI_ATARI_MCH_COOKIE, sizeof(bi.mch_cookie), - &bi.mch_cookie)) - return(0); - - /* Trailer */ - record = (struct bi_record *)((u_long)&bi_union.record+bi_size); - record->tag = BI_LAST; - bi_size += sizeof(bi_union.record.tag); - - return(1); -} - - - /* - * Add a Record to the Bootinfo Structure - */ - -static int add_bi_record(u_short tag, u_short size, const void *data) -{ - struct bi_record *record; - u_short size2; - - size2 = (sizeof(struct bi_record)+size+3)&-4; - if (bi_size+size2+sizeof(bi_union.record.tag) > MAX_BI_SIZE) { - fprintf (stderr, "Can't add bootinfo record. Ask a wizard to enlarge me.\n"); - return(0); - } - record = (struct bi_record *)((u_long)&bi_union.record+bi_size); - record->tag = tag; - record->size = size2; - memcpy(record->data, data, size); - bi_size += size2; - return(1); -} - - - /* - * Add a String Record to the Bootinfo Structure - */ - -static int add_bi_string(u_short tag, const u_char *s) -{ - return add_bi_record(tag, strlen(s)+1, (void *)s); -} - - -#ifdef BOOTINFO_COMPAT_1_0 - - /* - * Create the Bootinfo structure for backwards compatibility mode - */ - -static int create_compat_bootinfo(void) -{ - u_int i; - - compat_bootinfo.machtype = bi.machtype; - if (bi.cputype & CPU_68020) - compat_bootinfo.cputype = COMPAT_CPU_68020; - else if (bi.cputype & CPU_68030) - compat_bootinfo.cputype = COMPAT_CPU_68030; - else if (bi.cputype & CPU_68040) - compat_bootinfo.cputype = COMPAT_CPU_68040; - else if (bi.cputype & CPU_68060) - compat_bootinfo.cputype = COMPAT_CPU_68060; - else { - printf("CPU type 0x%08lx not supported by kernel\n", bi.cputype); - return(0); - } - if (bi.fputype & FPU_68881) - compat_bootinfo.cputype |= COMPAT_FPU_68881; - else if (bi.fputype & FPU_68882) - compat_bootinfo.cputype |= COMPAT_FPU_68882; - else if (bi.fputype & FPU_68040) - compat_bootinfo.cputype |= COMPAT_FPU_68040; - else if (bi.fputype & FPU_68060) - compat_bootinfo.cputype |= COMPAT_FPU_68060; - else { - printf("FPU type 0x%08lx not supported by kernel\n", bi.fputype); - return(0); - } - compat_bootinfo.num_memory = bi.num_memory; - if (compat_bootinfo.num_memory > COMPAT_NUM_MEMINFO) { - printf("Warning: using only %d blocks of memory\n", - COMPAT_NUM_MEMINFO); - compat_bootinfo.num_memory = COMPAT_NUM_MEMINFO; - } - for (i = 0; i < compat_bootinfo.num_memory; i++) { - compat_bootinfo.memory[i].addr = bi.memory[i].addr; - compat_bootinfo.memory[i].size = bi.memory[i].size; - } - if (bi.ramdisk.size) { - compat_bootinfo.ramdisk_size = (bi.ramdisk.size+1023)/1024; - compat_bootinfo.ramdisk_addr = bi.ramdisk.addr; - } else { - compat_bootinfo.ramdisk_size = 0; - compat_bootinfo.ramdisk_addr = 0; - } - strncpy(compat_bootinfo.command_line, bi.command_line, COMPAT_CL_SIZE); - compat_bootinfo.command_line[COMPAT_CL_SIZE-1] = '\0'; - - compat_bootinfo.bi_atari.hw_present = 0; - compat_bootinfo.bi_atari.mch_cookie = bi.mch_cookie; - return(1); -} -#endif /* BOOTINFO_COMPAT_1_0 */ - - -#ifdef ZKERNEL - -#define ZFILE_CHUNK_BITS 16 /* chunk is 64 KB */ -#define ZFILE_CHUNK_SIZE (1 << ZFILE_CHUNK_BITS) -#define ZFILE_CHUNK_MASK (ZFILE_CHUNK_SIZE-1) -#define ZFILE_N_CHUNKS (2*1024*1024/ZFILE_CHUNK_SIZE) - -/* variables for storing the uncompressed data */ -static char *ZFile[ZFILE_N_CHUNKS]; -static int ZFileSize = 0; -static int ZFpos = 0; -static int Zwpos = 0; - -static int Zinfd = 0; /* fd of compressed file */ - -/* - * gzip declarations - */ - -#define OF(args) args - -#define memzero(s, n) memset ((s), 0, (n)) - -typedef unsigned char uch; -typedef unsigned short ush; -typedef unsigned long ulg; - -#define INBUFSIZ 4096 -#define WSIZE 0x8000 /* window size--must be a power of two, and */ - /* at least 32K for zip's deflate method */ - -static uch *inbuf; -static uch *window; - -static unsigned insize = 0; /* valid bytes in inbuf */ -static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ -static unsigned outcnt = 0; /* bytes in output buffer */ -static int exit_code = 0; -static long bytes_out = 0; - -#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) - -/* Diagnostic functions (stubbed out) */ -#define Assert(cond,msg) -#define Trace(x) -#define Tracev(x) -#define Tracevv(x) -#define Tracec(c,x) -#define Tracecv(c,x) - -#define STATIC static - -static int fill_inbuf(void); -static void flush_window(void); -static void error(char *m); -static void gzip_mark(void **); -static void gzip_release(void **); - -#include "../../../../lib/inflate.c" - -static void gzip_mark( void **ptr ) -{ -} - -static void gzip_release( void **ptr ) -{ -} - - -/* - * Fill the input buffer. This is called only when the buffer is empty - * and at least one byte is really needed. - */ -static int fill_inbuf( void ) -{ - if (exit_code) - return -1; - - insize = ll_read( Zinfd, inbuf, INBUFSIZ ); - if (insize <= 0) - return -1; - - inptr = 1; - return( inbuf[0] ); -} - -/* - * Write the output window window[0..outcnt-1] and update crc and bytes_out. - * (Used for the decompressed data only.) - */ -static void flush_window( void ) -{ - ulg c = crc; /* temporary variable */ - unsigned n; - uch *in, ch; - int chunk = Zwpos >> ZFILE_CHUNK_BITS; - - if (chunk >= ZFILE_N_CHUNKS) { - fprintf( stderr, "compressed image too large! Aborting.\n" ); - boot_exit( EXIT_FAILURE ); - } - if (!ZFile[chunk]) { - if (!(ZFile[chunk] = (char *)Malloc( ZFILE_CHUNK_SIZE ))) { - fprintf( stderr, "Out of memory for decompresing kernel image\n" ); - boot_exit( EXIT_FAILURE ); - } - } - memcpy( ZFile[chunk] + (Zwpos & ZFILE_CHUNK_MASK), window, outcnt ); - Zwpos += outcnt; - -#define DISPLAY_BITS 13 - if ((Zwpos & ((1 << DISPLAY_BITS)-1)) == 0) { - printf( "." ); - fflush( stdout ); - } - - in = window; - for (n = 0; n < outcnt; n++) { - ch = *in++; - c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); - } - crc = c; - bytes_out += (ulg)outcnt; - outcnt = 0; -} - -static void error( char *x ) -{ - fprintf( stderr, "\n%s", x); - exit_code = 1; -} - -static int load_zkernel( int fd ) -{ - int i, err; - - for( i = 0; i < ZFILE_N_CHUNKS; ++i ) - ZFile[i] = NULL; - Zinfd = fd; - ll_lseek( fd, 0, SEEK_SET ); - - if (!(inbuf = (uch *)Malloc( INBUFSIZ ))) { - fprintf( stderr, "Couldn't allocate gunzip buffer\n" ); - boot_exit( EXIT_FAILURE ); - } - if (!(window = (uch *)Malloc( WSIZE ))) { - fprintf( stderr, "Couldn't allocate gunzip window\n" ); - boot_exit( EXIT_FAILURE ); - } - - printf( "Uncompressing kernel image " ); - fflush( stdout ); - makecrc(); - if (!(err = gunzip())) - printf( "done\n" ); - ZFileSize = Zwpos; - ll_close( Zinfd ); /* input file not needed anymore */ - - Mfree( inbuf ); - Mfree( window ); - return( err ); -} - -/* Note about the read/lseek wrapper and its memory management: It assumes - * that all seeks are only forward, and thus data already read or skipped can - * be freed. This is true for current organization of bootstrap and kernels. - * Little exception: The struct kexec at the start of the file. After reading - * it, there may be a seek back to the end of the file. But this currently - * doesn't hurt. Same considerations apply to the TFTP file buffers. (Roman) - */ - -static int kread( int fd, void *buf, unsigned cnt ) -{ - unsigned done = 0; - - if (!ZFileSize) - return( ll_read( fd, buf, cnt ) ); - - if (ZFpos + cnt > ZFileSize) - cnt = ZFileSize - ZFpos; - - while( cnt > 0 ) { - unsigned chunk = ZFpos >> ZFILE_CHUNK_BITS; - unsigned endchunk = (chunk+1) << ZFILE_CHUNK_BITS; - unsigned n = cnt; - - if (ZFpos + n > endchunk) - n = endchunk - ZFpos; - memcpy( buf, ZFile[chunk] + (ZFpos & ZFILE_CHUNK_MASK), n ); - cnt -= n; - buf += n; - done += n; - ZFpos += n; - - if (ZFpos == endchunk) { - Mfree( ZFile[chunk] ); - ZFile[chunk] = NULL; - } - } - - return( done ); -} - - -static int klseek( int fd, int where, int whence ) -{ - unsigned oldpos, oldchunk, newchunk; - - if (!ZFileSize) - return( ll_lseek( fd, where, whence ) ); - - oldpos = ZFpos; - switch( whence ) { - case SEEK_SET: - ZFpos = where; - break; - case SEEK_CUR: - ZFpos += where; - break; - case SEEK_END: - ZFpos = ZFileSize + where; - break; - default: - return( -1 ); - } - if (ZFpos < 0) { - ZFpos = 0; - return( -1 ); - } - else if (ZFpos > ZFileSize) { - ZFpos = ZFileSize; - return( -1 ); - } - - /* free memory of skipped-over data */ - oldchunk = oldpos >> ZFILE_CHUNK_BITS; - newchunk = ZFpos >> ZFILE_CHUNK_BITS; - while( oldchunk < newchunk ) { - if (ZFile[oldchunk]) { - Mfree( ZFile[oldchunk] ); - ZFile[oldchunk] = NULL; - } - ++oldchunk; - } - - return( ZFpos ); -} - - -static void free_zfile( void ) -{ - int i; - - for( i = 0; i < ZFILE_N_CHUNKS; ++i ) - if (ZFile[i]) Mfree( ZFile[i] ); -} - -static int kclose( int fd ) -{ - if (ZFileSize) { - free_zfile(); - return( 0 ); - } - else - return( ll_close( fd ) ); -} - - - -#endif /* ZKERNEL */ diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/atari/bootstrap.h linux/arch/m68k/boot/atari/bootstrap.h --- v2.1.47/linux/arch/m68k/boot/atari/bootstrap.h Fri Dec 20 01:19:58 1996 +++ linux/arch/m68k/boot/atari/bootstrap.h Wed Dec 31 16:00:00 1969 @@ -1,171 +0,0 @@ -/* -** bootstrap.h -- This file is a part of the Atari bootloader. -** -** Copyright 1993 by Arjan Knor -** -** Modified by Andreas Schwab -** - clear transparent translation registers -** Modified 18-Aug-96 by Geert Uytterhoeven -** - Updated for the new boot information structure (untested!) -** Modified 1996-11-12 by Andreas Schwab -** - Fixed and tested previous change -** -** 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. -** -*/ - -#ifndef BOOTSTRAP_H -#define BOOTSTRAP_H - - /* - * Atari Bootinfo Definitions - * - * All limits herein are `soft' limits, i.e. they don't put constraints - * on the actual parameters in the kernel. - */ - -struct atari_bootinfo { - unsigned long machtype; /* machine type */ - unsigned long cputype; /* system CPU */ - unsigned long fputype; /* system FPU */ - unsigned long mmutype; /* system MMU */ - int num_memory; /* # of memory blocks found */ - struct mem_info memory[NUM_MEMINFO]; /* memory description */ - struct mem_info ramdisk; /* ramdisk description */ - char command_line[CL_SIZE]; /* kernel command line parameters */ - unsigned long mch_cookie; /* _MCH cookie from TOS */ -}; - - -/* _MCH cookie values */ -#define MACH_ST 0 -#define MACH_STE 1 -#define MACH_TT 2 -#define MACH_FALCON 3 - -/* some constants for memory handling */ -#define ST_RAM 0 -#define TT_RAM 1 -#define TT_RAM_BASE (u_long)(0x01000000) -#define MB (1024 * 1024) -#define START_MEM (bi.memory[0].addr) -#define MEM_SIZE (bi.memory[0].size) - -/* the various CPU- and FPU-types */ -#define AFF_68000 (1) -#define AFF_68020 (2) -#define AFF_68030 (4) -#define AFF_68040 (8) -#define AFF_68881 (16) -#define AFF_68882 (32) - -/* the possible OS-languages */ -#define USA 0 -#define FRG 1 -#define FRA 2 -#define UK 3 -#define SPA 4 -#define ITA 5 -#define SWE 6 -#define SWF 7 -#define SWG 8 -#define TUR 9 -#define FIN 10 -#define NOR 11 -#define DEN 12 -#define SAU 13 -#define HOL 14 - -/* some inline functions */ - -static __inline int fpu_idle_frame_size (void) -{ - char fpu_frame[216]; - __asm__ __volatile__ ("fnop"::); - __asm__ __volatile__ ("fsave %0@" : : "a" (fpu_frame)); - return fpu_frame[1]; -} - -static __inline void change_stack (u_long *stackp) -{ - __asm__ volatile ("movel %0,sp\n\t" :: "g" (stackp) : "sp"); -} - -static __inline void disable_interrupts (void) -{ - __asm__ volatile ("orw #0x700,sr":); -} - -extern struct atari_bootinfo bi; -static __inline void disable_cache (void) -{ - __asm__ volatile ("movec %0,cacr" :: "d" (0)); - if (bi.cputype & CPU_68060) { - /* '060: clear branch cache after disabling it; - * disable superscalar operation (and enable FPU) */ - __asm__ volatile ("movec %0,cacr" :: "d" (0x00400000)); - __asm__ volatile ("moveq #0,d0;" - ".long 0x4e7b0808" /* movec d0,pcr */ - : /* no outputs */ - : /* no inputs */ - : "d0"); - } -} - -static __inline void disable_mmu (void) -{ - if (bi.cputype & (CPU_68040|CPU_68060)) { - __asm__ volatile ("moveq #0,d0;" - ".long 0x4e7b0003;" /* movec d0,tc */ - ".long 0x4e7b0004;" /* movec d0,itt0 */ - ".long 0x4e7b0005;" /* movec d0,itt1 */ - ".long 0x4e7b0006;" /* movec d0,dtt0 */ - ".long 0x4e7b0007" /* movec d0,dtt1 */ - : /* no outputs */ - : /* no inputs */ - : "d0"); - } - else { - __asm__ volatile ("subl #4,sp\n\t" - "pmove tc,sp@\n\t" - "bclr #7,sp@\n\t" - "pmove sp@,tc\n\t" - "addl #4,sp"); - if (bi.cputype & CPU_68030) { - __asm__ volatile ("clrl sp@-\n\t" - ".long 0xf0170800\n\t" /* pmove sp@,tt0 */ - ".long 0xf0170c00\n\t" /* pmove sp@,tt1 */ - "addl #4,sp\n"); - } - } -} - -static __inline void jump_to_mover (void *, void *, void *, void *, int, int, - void *) __attribute__ ((noreturn)); -static __inline void jump_to_mover (void *kernel_start, void *mem_start, - void *ramdisk_end, void *mem_end, - int kernel_size, int ramdisk_size, - void *mover_addr) -{ - asm volatile ("movel %0,a0\n\t" - "movel %1,a1\n\t" - "movel %2,a2\n\t" - "movel %3,a3\n\t" - "movel %4,d0\n\t" - "movel %5,d1\n\t" - "jmp %6@\n" - : /* no outputs */ - : "g" (kernel_start), "g" (mem_start), - "g" (ramdisk_end), "g" (mem_end), - "g" (kernel_size), "g" (ramdisk_size), - "a" (mover_addr) - : "a0", "a1", "a2", "a3", "d0", "d1"); - - /* Avoid warning that function may return */ - for (;;) ; -} - -#endif /* BOOTSTRAP_H */ - diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/atari/ethlance.c linux/arch/m68k/boot/atari/ethlance.c --- v2.1.47/linux/arch/m68k/boot/atari/ethlance.c Wed Sep 25 00:47:39 1996 +++ linux/arch/m68k/boot/atari/ethlance.c Wed Dec 31 16:00:00 1969 @@ -1,435 +0,0 @@ - -#include -#include - -#include "bootp.h" -#include "ethlance.h" - - -struct { - volatile unsigned short *memaddr; - volatile unsigned short *ioaddr; -} lance_addr_list[] = { - { (void *)0xfe010000, (void *)0xfe00fff0 }, /* RieblCard VME in TT */ - { (void *)0xfec10000, (void *)0xfec0fff0 }, /* RieblCard VME in MegaSTE - (highest byte stripped) */ - { (void *)0xfee00000, (void *)0xfeff7000 }, /* RieblCard in ST - (highest byte stripped) */ - { (void *)0xfecf0000, (void *)0xfecffff0 }, /* PAMCard VME in TT and MSTE - (highest byte stripped) */ -}; - -#define N_LANCE_ADDR (sizeof(lance_addr_list)/sizeof(*lance_addr_list)) - -#define TX_RING_SIZE 1 -#define TX_RING_LEN_BITS 0 - -#define RX_RING_SIZE 16 -#define RX_RING_LEN_BITS (4 << 5) - -#define offsetof(type,elt) ((unsigned long)(&(((type *)0)->elt))) - -/* The LANCE Rx and Tx ring descriptors. */ -struct lance_rx_head { - unsigned short base; /* Low word of base addr */ - volatile unsigned char flag; - unsigned char base_hi; /* High word of base addr (unused) */ - short buf_length; /* This length is 2s complement! */ - short msg_length; /* This length is "normal". */ -}; - -struct lance_tx_head { - unsigned short base; /* Low word of base addr */ - volatile unsigned char flag; - unsigned char base_hi; /* High word of base addr (unused) */ - short length; /* Length is 2s complement! */ - volatile short misc; -}; - -struct ringdesc { - unsigned short adr_lo; /* Low 16 bits of address */ - unsigned char len; /* Length bits */ - unsigned char adr_hi; /* High 8 bits of address (unused) */ -}; - -struct lance_packet { - volatile unsigned char data[PKTLEN]; -}; - -/* The LANCE initialization block, described in databook. */ -struct lance_init_block { - unsigned short mode; /* Pre-set mode */ - unsigned char hwaddr[6]; /* Physical ethernet address */ - unsigned filter[2]; /* Multicast filter (unused). */ - /* Receive and transmit ring base, along with length bits. */ - struct ringdesc rx_ring; - struct ringdesc tx_ring; -}; - -/* The whole layout of the Lance shared memory */ -struct lance_memory { - struct lance_init_block init; - struct lance_tx_head tx_head[TX_RING_SIZE]; - struct lance_rx_head rx_head[RX_RING_SIZE]; - struct lance_packet tx_packet[TX_RING_SIZE]; - struct lance_packet rx_packet[TX_RING_SIZE]; -}; - -#define RIEBL_MAGIC 0x09051990 -#define RIEBL_MAGIC_ADDR ((unsigned long *)(((char *)MEM) + 0xee8a)) -#define RIEBL_HWADDR_ADDR ((unsigned char *)(((char *)MEM) + 0xee8e)) -#define RIEBL_IVEC_ADDR ((unsigned short *)(((char *)MEM) + 0xfffe)) - -struct lance_ioreg { -/* base+0x0 */ volatile unsigned short data; -/* base+0x2 */ volatile unsigned short addr; - unsigned char _dummy1[3]; -/* base+0x7 */ volatile unsigned char ivec; - unsigned char _dummy2[5]; -/* base+0xd */ volatile unsigned char eeprom; - unsigned char _dummy3; -/* base+0xf */ volatile unsigned char mem; -}; - -enum lance_type { - OLD_RIEBL, /* old Riebl card without battery */ - NEW_RIEBL, /* new Riebl card with battery */ - PAM_CARD /* PAM card with EEPROM */ -} CardType; - -HWADDR dev_addr; - -/* This is a default address for the old RieblCards without a battery - * that have no ethernet address at boot time. 00:00:36:04 is the - * prefix for Riebl cards, the 00:00 at the end is arbitrary. - */ - -HWADDR OldRieblDefHwaddr = { - 0x00, 0x00, 0x36, 0x04, 0x00, 0x00 -}; - -struct lance_ioreg *IO; -struct lance_memory *MEM; - -#define DREG IO->data -#define AREG IO->addr -#define REGA(a) ( AREG = (a), DREG ) - -int CurRx; - - -/* Definitions for the Lance */ - -/* tx_head flags */ -#define TMD1_ENP 0x01 -#define TMD1_STP 0x02 -#define TMD1_DEF 0x04 -#define TMD1_ONE 0x08 -#define TMD1_MORE 0x10 -#define TMD1_ERR 0x40 -#define TMD1_OWN 0x80 - -#define TMD1_OWN_CHIP TMD1_OWN -#define TMD1_OWN_HOST 0 - -/* tx_head misc field */ -#define TMD3_TDR 0x03FF -#define TMD3_RTRY 0x0400 -#define TMD3_LCAR 0x0800 -#define TMD3_LCOL 0x1000 -#define TMD3_UFLO 0x4000 -#define TMD3_BUFF3 0x8000 - -/* rx_head flags */ -#define RMD1_ENP 0x01 -#define RMD1_STP 0x02 -#define RMD1_BUFF 0x04 -#define RMD1_CRC 0x08 -#define RMD1_OFLO 0x10 -#define RMD1_FRAM 0x20 -#define RMD1_ERR 0x40 -#define RMD1_OWN 0x80 - -#define RMD1_OWN_CHIP RMD1_OWN -#define RMD1_OWN_HOST 0 - -/* register names */ -#define CSR0 0 -#define CSR1 1 -#define CSR2 2 -#define CSR3 3 - -/* CSR0 */ -#define CSR0_INIT 0x0001 /* initialize */ -#define CSR0_STRT 0x0002 /* start */ -#define CSR0_STOP 0x0004 /* stop */ -#define CSR0_TDMD 0x0008 /* transmit demand */ -#define CSR0_TXON 0x0010 /* transmitter on */ -#define CSR0_RXON 0x0020 /* receiver on */ -#define CSR0_INEA 0x0040 /* interrupt enable */ -#define CSR0_INTR 0x0080 /* interrupt active */ -#define CSR0_IDON 0x0100 /* initialization done */ -#define CSR0_TINT 0x0200 /* transmitter interrupt */ -#define CSR0_RINT 0x0400 /* receiver interrupt */ -#define CSR0_MERR 0x0800 /* memory error */ -#define CSR0_MISS 0x1000 /* missed frame */ -#define CSR0_CERR 0x2000 /* carrier error (no heartbeat :-) */ -#define CSR0_BABL 0x4000 /* babble: tx-ed too many bits */ -#define CSR0_ERR 0x8000 /* error */ - -/* CSR3 */ -#define CSR3_BCON 0x0001 -#define CSR3_ACON 0x0002 -#define CSR3_BSWP 0x0004 - - -#define HZ 200 -#define _hz_200 (*(volatile unsigned long *)0x4ba) - - - - -/***************************** Prototypes *****************************/ - -static int lance_probe( void ); -static int addr_readable( volatile void *regp, int wordflag ); -static int lance_init( void ); -static void lance_get_hwaddr( HWADDR *addr ); -static int lance_snd( Packet *pkt, int len ); -static int lance_rcv( Packet *pkt, int *len ); - -/************************* End of Prototypes **************************/ - - - -ETHIF_SWITCH LanceSwitch = { - lance_probe, lance_init, lance_get_hwaddr, - lance_snd, lance_rcv -}; - - -static int lance_probe( void ) - -{ int i; - - for( i = 0; i < N_LANCE_ADDR; ++i ) { - if (addr_readable( lance_addr_list[i].memaddr, 1 ) && - (lance_addr_list[i].memaddr[0] = 1, - lance_addr_list[i].memaddr[0] == 1) && - (lance_addr_list[i].memaddr[0] = 0, - lance_addr_list[i].memaddr[0] == 0) && - addr_readable( lance_addr_list[i].ioaddr, 1 )) { - break; - } - } - if (i == N_LANCE_ADDR) return( -1 ); - - IO = (struct lance_ioreg *)lance_addr_list[i].ioaddr; - MEM = (struct lance_memory *)lance_addr_list[i].memaddr; - REGA( CSR0 ) = CSR0_STOP; - - return( 0 ); -} - - -static int addr_readable( volatile void *regp, int wordflag ) - -{ int ret; - long *vbr, save_berr; - - __asm__ __volatile__ ( "movec %/vbr,%0" : "=r" (vbr) : ); - save_berr = vbr[2]; - - __asm__ __volatile__ - ( "movel %/sp,%/d1\n\t" - "movel #Lberr,%2@\n\t" - "moveq #0,%0\n\t" - "tstl %3\n\t" - "bne 1f\n\t" - "tstb %1@\n\t" - "bra 2f\n" -"1: tstw %1@\n" -"2: moveq #1,%0\n" -"Lberr: movel %/d1,%/sp" - : "=&d" (ret) - : "a" (regp), "a" (&vbr[2]), "rm" (wordflag) - : "d1", "memory" - ); - - vbr[2] = save_berr; - - return( ret ); -} - - -static int lance_init( void ) - -{ int i; - - /* Now test for type: If the eeprom I/O port is readable, it is a - * PAM card */ - if (addr_readable( &(IO->eeprom), 0 )) { - /* Switch back to Ram */ - i = IO->mem; - CardType = PAM_CARD; - } - else if (*RIEBL_MAGIC_ADDR == RIEBL_MAGIC) { - CardType = NEW_RIEBL; - } - else - CardType = OLD_RIEBL; - - /* Get the ethernet address */ - switch( CardType ) { - case OLD_RIEBL: - /* No ethernet address! (Set some default address) */ - memcpy( dev_addr, OldRieblDefHwaddr, ETHADDRLEN ); - break; - case NEW_RIEBL: - memcpy( dev_addr, RIEBL_HWADDR_ADDR, ETHADDRLEN ); - break; - case PAM_CARD: - i = IO->eeprom; - for( i = 0; i < ETHADDRLEN; ++i ) - dev_addr[i] = - ((((unsigned short *)MEM)[i*2] & 0x0f) << 4) | - ((((unsigned short *)MEM)[i*2+1] & 0x0f)); - i = IO->mem; - break; - } - - MEM->init.mode = 0x0000; /* Disable Rx and Tx. */ - for( i = 0; i < ETHADDRLEN; i++ ) - MEM->init.hwaddr[i] = dev_addr[i^1]; /* <- 16 bit swap! */ - MEM->init.filter[0] = 0x00000000; - MEM->init.filter[1] = 0x00000000; - MEM->init.rx_ring.adr_lo = offsetof( struct lance_memory, rx_head ); - MEM->init.rx_ring.adr_hi = 0; - MEM->init.rx_ring.len = RX_RING_LEN_BITS; - MEM->init.tx_ring.adr_lo = offsetof( struct lance_memory, tx_head ); - MEM->init.tx_ring.adr_hi = 0; - MEM->init.tx_ring.len = TX_RING_LEN_BITS; - - REGA( CSR3 ) = CSR3_BSWP | (CardType == PAM_CARD ? CSR3_ACON : 0); - REGA( CSR2 ) = 0; - REGA( CSR1 ) = 0; - REGA( CSR0 ) = CSR0_INIT | CSR0_STRT; - - i = 1000000; - while( i-- > 0 ) - if (DREG & CSR0_IDON) - break; - if (i < 0 || (DREG & CSR0_ERR)) { - DREG = CSR0_STOP; - return( -1 ); - } - DREG = CSR0_IDON; - - for (i = 0; i < TX_RING_SIZE; i++) { - MEM->tx_head[i].base = offsetof( struct lance_memory, tx_packet[i] ); - MEM->tx_head[i].flag = TMD1_OWN_HOST; - MEM->tx_head[i].base_hi = 0; - MEM->tx_head[i].length = 0; - MEM->tx_head[i].misc = 0; - } - - for (i = 0; i < RX_RING_SIZE; i++) { - MEM->rx_head[i].base = offsetof( struct lance_memory, rx_packet[i] ); - MEM->rx_head[i].flag = TMD1_OWN_CHIP; - MEM->rx_head[i].base_hi = 0; - MEM->rx_head[i].buf_length = -PKTLEN; - MEM->rx_head[i].msg_length = 0; - } - CurRx = 0; - - return( 0 ); -} - - -static void lance_get_hwaddr( HWADDR *addr ) - -{ - memcpy( addr, dev_addr, ETHADDRLEN ); -} - - -static int lance_snd( Packet *pkt, int len ) - -{ unsigned long timeout; - - /* The old LANCE chips doesn't automatically pad buffers to min. size. */ - len = (len < 60) ? 60 : len; - /* PAM-Card has a bug: Can only send packets with even number of bytes! */ - if (CardType == PAM_CARD && (len & 1)) - ++len; - - MEM->tx_head[0].length = -len; - MEM->tx_head[0].misc = 0; - memcpy( (void *)&MEM->tx_packet[0].data, pkt, len ); - MEM->tx_head[0].base = offsetof(struct lance_memory, tx_packet[0]); - MEM->tx_head[0].base_hi = 0; - MEM->tx_head[0].flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP; - - /* Trigger an immediate send poll. */ - REGA( CSR0 ) = CSR0_TDMD; - - /* Wait for packet being sent */ - timeout = _hz_200 + 3*HZ; - while( (MEM->tx_head[0].flag & TMD1_OWN_CHIP) && - !MEM->tx_head[0].misc && - _hz_200 < timeout ) - ; - - if ((MEM->tx_head[0].flag & TMD1_OWN) == TMD1_OWN_HOST && - !(MEM->tx_head[0].misc & TMD1_ERR)) - /* sent ok */ - return( 0 ); - - /* failure */ - if (_hz_200 >= timeout) - return( ETIMEO ); - if (MEM->tx_head[0].misc & TMD3_UFLO) { - /* On FIFO errors, must re-turn on TX! */ - DREG = CSR0_STRT; - } - - return( ESEND ); -} - - -static int lance_rcv( Packet *pkt, int *len ) - -{ unsigned long timeout; - int stat; - - /* Wait for a packet */ - timeout = _hz_200 + 4*HZ; - while( (MEM->rx_head[CurRx].flag & TMD1_OWN_CHIP) && - _hz_200 < timeout ) - ; - /* Not ours -> was a timeout */ - if (((stat = MEM->rx_head[CurRx].flag) & TMD1_OWN) == TMD1_OWN_CHIP) - return( ETIMEO ); - - /* Check for errors */ - if (stat != (RMD1_ENP|RMD1_STP)) { - MEM->rx_head[CurRx].flag &= (RMD1_ENP|RMD1_STP); - if (stat & RMD1_FRAM) return( EFRAM ); - if (stat & RMD1_OFLO) return( EOVERFL ); - if (stat & RMD1_CRC) return( ECRC ); - return( ERCV ); - } - - /* Get the packet */ - *len = MEM->rx_head[CurRx].msg_length & 0xfff; - memcpy( pkt, (void *)&MEM->rx_packet[CurRx].data, *len ); - - /* Give the buffer back to the chip */ - MEM->rx_head[CurRx].buf_length = -PKTLEN; - MEM->rx_head[CurRx].flag |= RMD1_OWN_CHIP; - CurRx = (CurRx + 1) % RX_RING_SIZE; - - return( 0 ); -} - - diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/atari/ethlance.h linux/arch/m68k/boot/atari/ethlance.h --- v2.1.47/linux/arch/m68k/boot/atari/ethlance.h Wed Sep 25 00:47:39 1996 +++ linux/arch/m68k/boot/atari/ethlance.h Wed Dec 31 16:00:00 1969 @@ -1,7 +0,0 @@ - -#ifndef _ethlance_h -#define _ethlance_h - -extern ETHIF_SWITCH LanceSwitch; - -#endif /* _ethlance_h */ diff -u --recursive --new-file v2.1.47/linux/arch/m68k/boot/atari/sysvars.h linux/arch/m68k/boot/atari/sysvars.h --- v2.1.47/linux/arch/m68k/boot/atari/sysvars.h Wed Dec 27 12:44:47 1995 +++ linux/arch/m68k/boot/atari/sysvars.h Wed Dec 31 16:00:00 1969 @@ -1,22 +0,0 @@ -typedef struct _osheader -{ - unsigned short os_entry; - unsigned short os_version; - void *reseth; - struct _osheader *os_beg; - void *os_end; - long os_rsv1; - void *os_magic; - long os_date; - unsigned short os_conf; - unsigned short os_dosdate; - char **p_root; - unsigned char **pkbshift; - void **p_run; - char *p_rsv2; -} OSHEADER; - -#define phystop ((unsigned long *)0x42e) -#define _sysbase ((OSHEADER **)0x4f2) -#define _p_cookies ((unsigned long **)0x5a0) -#define ramtop ((unsigned long *)0x5a4) diff -u --recursive --new-file v2.1.47/linux/arch/m68k/config.in linux/arch/m68k/config.in --- v2.1.47/linux/arch/m68k/config.in Mon Jul 7 08:18:53 1997 +++ linux/arch/m68k/config.in Thu Jul 31 13:09:16 1997 @@ -218,6 +218,10 @@ define_bool CONFIG_VT_CONSOLE y define_bool CONFIG_FB_CONSOLE y +if [ "$CONFIG_ATARI" = "y" ]; then + define_bool CONFIG_NVRAM y +fi + tristate 'Parallel printer support' CONFIG_PRINTER if [ "$CONFIG_AMIGA" = "y" ]; then dep_tristate 'Multiface Card III parallel support' CONFIG_MULTIFACE_III_LP $CONFIG_PRINTER @@ -243,6 +247,7 @@ dep_tristate 'GVP IO-Extender parallel printer support' CONFIG_GVPIOEXT_LP $CONFIG_GVPIOEXT dep_tristate 'GVP IO-Extender PLIP support' CONFIG_GVPIOEXT_PLIP $CONFIG_GVPIOEXT tristate 'Multiface Card III serial support' CONFIG_MULTIFACE_III_TTY + bool 'Hisoft Whippet PCMCIA serial support' CONFIG_WHIPPET fi if [ "$CONFIG_ATARI_MFPSER" = "y" -o "$CONFIG_ATARI_SCC" = "y" -o \ "$CONFIG_ATARI_MIDI" = "y" -o "$CONFIG_AMIGA_BUILTIN_SERIAL" = "y" -o \ diff -u --recursive --new-file v2.1.47/linux/arch/m68k/defconfig linux/arch/m68k/defconfig --- v2.1.47/linux/arch/m68k/defconfig Mon Jul 7 08:18:53 1997 +++ linux/arch/m68k/defconfig Thu Jul 31 13:09:16 1997 @@ -152,9 +152,6 @@ # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_DCACHE_PRELOAD is not set -# CONFIG_OMIRR is not set -# CONFIG_TRANS_NAMES is not set CONFIG_MINIX_FS=y CONFIG_EXT2_FS=y CONFIG_FAT_FS=y diff -u --recursive --new-file v2.1.47/linux/arch/m68k/ifpsp060/iskeleton.S linux/arch/m68k/ifpsp060/iskeleton.S --- v2.1.47/linux/arch/m68k/ifpsp060/iskeleton.S Thu Jun 26 12:33:36 1997 +++ linux/arch/m68k/ifpsp060/iskeleton.S Thu Jul 31 13:09:16 1997 @@ -37,6 +37,7 @@ #include #include + |################################ | (1) EXAMPLE CALL-OUTS # | # diff -u --recursive --new-file v2.1.47/linux/arch/m68k/kernel/console.c linux/arch/m68k/kernel/console.c --- v2.1.47/linux/arch/m68k/kernel/console.c Mon Jul 7 08:18:53 1997 +++ linux/arch/m68k/kernel/console.c Thu Jul 31 13:09:16 1997 @@ -420,7 +420,10 @@ set_scrmem(fg_console, 0); set_origin(fg_console); #endif /* XXX */ - update_screen(fg_console); + /* don't update in graphics mode */ + if (currcons == fg_console && vt_cons[fg_console]->vc_mode == KD_TEXT) + update_screen(fg_console); + set_cursor(fg_console); return 0; @@ -512,8 +515,9 @@ console_table[currcons]->winsize = ws; } - if (currcons == fg_console) - update_screen(fg_console); + /* don't update in graphics mode */ + if (currcons == fg_console && vt_cons[fg_console]->vc_mode == KD_TEXT) + update_screen(fg_console); } void vc_disallocate(unsigned int currcons) diff -u --recursive --new-file v2.1.47/linux/arch/m68k/kernel/entry.S linux/arch/m68k/kernel/entry.S --- v2.1.47/linux/arch/m68k/kernel/entry.S Mon Jun 16 16:35:53 1997 +++ linux/arch/m68k/kernel/entry.S Thu Jul 31 13:09:16 1997 @@ -190,7 +190,7 @@ andw #ALLOWINT,%sr /* check if we need to do software interrupts */ - + movel SYMBOL_NAME(bh_active),%d0 andl SYMBOL_NAME(bh_mask),%d0 jeq SYMBOL_NAME(ret_from_exception) diff -u --recursive --new-file v2.1.47/linux/arch/m68k/kernel/head.S linux/arch/m68k/kernel/head.S --- v2.1.47/linux/arch/m68k/kernel/head.S Sat May 24 09:10:22 1997 +++ linux/arch/m68k/kernel/head.S Thu Jul 31 13:09:16 1997 @@ -273,6 +273,9 @@ movel %d2,%a0@ lea %pc@(SYMBOL_NAME(is_medusa)),%a0 movel %d3,%a0@ + lea %pc@(Liobase),%a0 + movel %d2,%a0@ /* On a Hades the iobase must be set + before opening the serial port. */ Lnotypetest: #endif diff -u --recursive --new-file v2.1.47/linux/arch/m68k/kernel/process.c linux/arch/m68k/kernel/process.c --- v2.1.47/linux/arch/m68k/kernel/process.c Sat May 24 09:10:22 1997 +++ linux/arch/m68k/kernel/process.c Thu Jul 31 13:09:16 1997 @@ -283,8 +283,9 @@ struct pt_regs *regs = (struct pt_regs *) &name; lock_kernel(); - error = getname(name, &filename); - if (error) + filename = getname(name); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; error = do_execve(filename, argv, envp, regs); putname(filename); diff -u --recursive --new-file v2.1.47/linux/arch/m68k/kernel/sys_m68k.c linux/arch/m68k/kernel/sys_m68k.c --- v2.1.47/linux/arch/m68k/kernel/sys_m68k.c Sat May 24 09:10:22 1997 +++ linux/arch/m68k/kernel/sys_m68k.c Thu Jul 31 13:09:16 1997 @@ -205,11 +205,10 @@ return -ENOSYS; } -/* Convert virtual address VADDR to physical address PADDR, recording - in VALID whether the virtual address is actually mapped. */ -#define virt_to_phys_040(vaddr, paddr, valid) \ -{ \ - unsigned long _mmusr; \ +/* Convert virtual address VADDR to physical address PADDR */ +#define virt_to_phys_040(vaddr) \ +({ \ + unsigned long _mmusr, _paddr; \ \ __asm__ __volatile__ (".chip 68040\n\t" \ "ptestr (%1)\n\t" \ @@ -217,20 +216,14 @@ ".chip 68k" \ : "=r" (_mmusr) \ : "a" (vaddr)); \ - if (!(_mmusr & MMU_R_040)) \ - (valid) = 0; \ - else \ - { \ - (valid) = 1; \ - (paddr) = _mmusr & PAGE_MASK; \ - } \ -} + _paddr = (_mmusr & MMU_R_040) ? (_mmusr & PAGE_MASK) : 0; \ + _paddr; \ +}) static inline int cache_flush_040 (unsigned long addr, int scope, int cache, unsigned long len) { - unsigned long paddr; - int valid; + unsigned long paddr, i; switch (scope) { @@ -261,19 +254,31 @@ break; case FLUSH_SCOPE_LINE: - len >>= 4; /* Find the physical address of the first mapped page in the address range. */ - for (;;) - { - virt_to_phys_040 (addr, paddr, valid); - if (valid) - break; - if (len <= PAGE_SIZE / 16) - return 0; - len -= (PAGE_SIZE - (addr & PAGE_MASK)) / 16; - addr = (addr + PAGE_SIZE) & PAGE_MASK; - } + if ((paddr = virt_to_phys_040(addr))) { + paddr += addr & ~(PAGE_MASK | 15); + len = (len + (addr & 15) + 15) >> 4; + } else { + unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK); + + if (len <= tmp) + return 0; + addr += tmp; + len -= tmp; + tmp = PAGE_SIZE; + for (;;) + { + if ((paddr = virt_to_phys_040(addr))) + break; + if (len <= tmp) + return 0; + addr += tmp; + len -= tmp; + } + len = (len + 15) >> 4; + } + i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4; while (len--) { switch (cache) @@ -301,36 +306,33 @@ : : "a" (paddr)); break; } - addr += 16; - if (len) + if (!--i && len) { - if ((addr & (PAGE_SIZE-1)) < 16) + addr += PAGE_SIZE; + i = PAGE_SIZE / 16; + /* Recompute physical address when crossing a page + boundary. */ + for (;;) { - /* Recompute physical address when crossing a page - boundary. */ - for (;;) - { - virt_to_phys_040 (addr, paddr, valid); - if (valid) - break; - if (len <= PAGE_SIZE / 16) - return 0; - len -= (PAGE_SIZE - (addr & PAGE_MASK)) / 16; - addr = (addr + PAGE_SIZE) & PAGE_MASK; - } + if ((paddr = virt_to_phys_040(addr))) + break; + if (len <= i) + return 0; + len -= i; + addr += PAGE_SIZE; } - else - paddr += 16; } + else + paddr += 16; } break; default: case FLUSH_SCOPE_PAGE: + len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1); for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE) { - virt_to_phys_040 (addr, paddr, valid); - if (!valid) + if (!(paddr = virt_to_phys_040(addr))) continue; switch (cache) { @@ -363,21 +365,21 @@ return 0; } -#define virt_to_phys_060(vaddr, paddr, valid) \ -{ \ +#define virt_to_phys_060(vaddr) \ +({ \ + unsigned long paddr; \ __asm__ __volatile__ (".chip 68060\n\t" \ "plpar (%0)\n\t" \ ".chip 68k" \ : "=a" (paddr) \ : "0" (vaddr)); \ - (valid) = 1; /* XXX */ \ -} + (paddr); /* XXX */ \ +}) static inline int cache_flush_060 (unsigned long addr, int scope, int cache, unsigned long len) { - unsigned long paddr; - int valid; + unsigned long paddr, i; switch (scope) { @@ -407,19 +409,30 @@ break; case FLUSH_SCOPE_LINE: - len >>= 4; /* Find the physical address of the first mapped page in the address range. */ - for (;;) - { - virt_to_phys_060 (addr, paddr, valid); - if (valid) - break; - if (len <= PAGE_SIZE / 16) - return 0; - len -= (PAGE_SIZE - (addr & PAGE_MASK)) / 16; - addr = (addr + PAGE_SIZE) & PAGE_MASK; - } + len += addr & 15; + addr &= -16; + if (!(paddr = virt_to_phys_060(addr))) { + unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK); + + if (len <= tmp) + return 0; + addr += tmp; + len -= tmp; + tmp = PAGE_SIZE; + for (;;) + { + if ((paddr = virt_to_phys_060(addr))) + break; + if (len <= tmp) + return 0; + addr += tmp; + len -= tmp; + } + } + len = (len + 15) >> 4; + i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4; while (len--) { switch (cache) @@ -447,36 +460,35 @@ : : "a" (paddr)); break; } - addr += 16; - if (len) + if (!--i && len) { - if ((addr & (PAGE_SIZE-1)) < 16) - { - /* Recompute the physical address when crossing a - page boundary. */ - for (;;) - { - virt_to_phys_060 (addr, paddr, valid); - if (valid) - break; - if (len <= PAGE_SIZE / 16) - return 0; - len -= (PAGE_SIZE - (addr & PAGE_MASK)) / 16; - addr = (addr + PAGE_SIZE) & PAGE_MASK; - } - } - else - paddr += 16; + addr += PAGE_SIZE; + i = PAGE_SIZE / 16; + /* Recompute physical address when crossing a page + boundary. */ + for (;;) + { + if ((paddr = virt_to_phys_060(addr))) + break; + if (len <= i) + return 0; + len -= i; + addr += PAGE_SIZE; + } } + else + paddr += 16; } break; default: case FLUSH_SCOPE_PAGE: + len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1); + addr &= PAGE_MASK; /* Workaround for bug in some + revisions of the 68060 */ for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE) { - virt_to_phys_060 (addr, paddr, valid); - if (!valid) + if (!(paddr = virt_to_phys_060(addr))) continue; switch (cache) { diff -u --recursive --new-file v2.1.47/linux/arch/mips/config.in linux/arch/mips/config.in --- v2.1.47/linux/arch/mips/config.in Sun Jul 27 12:11:00 1997 +++ linux/arch/mips/config.in Thu Jul 31 13:09:16 1997 @@ -80,7 +80,7 @@ bool 'Sysctl support' CONFIG_SYSCTL if [ "$CONFIG_SGI" != "y" ]; then - tristate 'Parallel port support' CONFIG_PARPORT + tristate 'Parallel port support' CONFIG_PNP_PARPORT fi endmenu diff -u --recursive --new-file v2.1.47/linux/arch/mips/defconfig linux/arch/mips/defconfig --- v2.1.47/linux/arch/mips/defconfig Mon Jul 7 08:18:53 1997 +++ linux/arch/mips/defconfig Thu Jul 31 13:09:16 1997 @@ -86,6 +86,7 @@ # CONFIG_IP_ACCT is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) diff -u --recursive --new-file v2.1.47/linux/arch/mips/jazz/int-handler.S linux/arch/mips/jazz/int-handler.S --- v2.1.47/linux/arch/mips/jazz/int-handler.S Thu Jun 26 12:33:37 1997 +++ linux/arch/mips/jazz/int-handler.S Thu Jul 31 13:09:16 1997 @@ -278,7 +278,10 @@ b loc_call li t3,PTRSIZE*JAZZ_ETHERNET_IRQ # delay slot -loc_scsi: PANIC("Unimplemented loc_scsi handler") +loc_scsi: li s1,~JAZZ_IE_SCSI + li a0,12 # JAZZ_SCSI_IRQ */ + b loc_call + li t3,PTRSIZE*12 # JAZZ_ETHERNET_IRQ # delay slot /* * Keyboard interrupt handler diff -u --recursive --new-file v2.1.47/linux/arch/mips/jazz/jazzdma.c linux/arch/mips/jazz/jazzdma.c --- v2.1.47/linux/arch/mips/jazz/jazzdma.c Thu Jun 26 12:33:37 1997 +++ linux/arch/mips/jazz/jazzdma.c Thu Jul 31 13:09:16 1997 @@ -375,6 +375,7 @@ * Clear all interrupt flags */ r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) | R4030_TC_INTR | R4030_MEM_INTR | R4030_ADDR_INTR); /* diff -u --recursive --new-file v2.1.47/linux/arch/mips/jazz/setup.c linux/arch/mips/jazz/setup.c --- v2.1.47/linux/arch/mips/jazz/setup.c Thu Jun 26 12:33:37 1997 +++ linux/arch/mips/jazz/setup.c Thu Jul 31 13:09:16 1997 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -36,11 +37,21 @@ extern void jazz_machine_halt(void); extern void jazz_machine_power_off(void); +void (*board_time_init)(struct irqaction *irq); + +__initfunc(static void jazz_time_init(struct irqaction *irq)) +{ + /* set the clock to 100 Hz */ + r4030_write_reg32(JAZZ_TIMER_INTERVAL, 9); + setup_x86_irq(0, irq); +} + __initfunc(static void jazz_irq_setup(void)) { set_except_vector(0, jazz_handle_int); r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, JAZZ_IE_ETHERNET | + JAZZ_IE_SCSI | JAZZ_IE_SERIAL1 | JAZZ_IE_SERIAL2 | JAZZ_IE_PARALLEL | @@ -57,14 +68,42 @@ __initfunc(void jazz_setup(void)) { + tag *atag; + + /* + * we just check if a tag_screen_info can be gathered + * in setup_arch(), if yes we don't proceed futher... + */ + atag = bi_TagFind(tag_screen_info); + if (!atag) { + /* + * If no, we try to find the tag_arc_displayinfo which is + * always created by Milo for an ARC box (for now Milo only + * works on ARC boxes :) -Stoned. + */ + atag = bi_TagFind(tag_arcdisplayinfo); + if (atag) { + screen_info.orig_x = + ((mips_arc_DisplayInfo*)TAGVALPTR(atag))->cursor_x; + screen_info.orig_y = + ((mips_arc_DisplayInfo*)TAGVALPTR(atag))->cursor_y; + screen_info.orig_video_cols = + ((mips_arc_DisplayInfo*)TAGVALPTR(atag))->columns; + screen_info.orig_video_lines = + ((mips_arc_DisplayInfo*)TAGVALPTR(atag))->lines; + } + } + irq_setup = jazz_irq_setup; fd_cacheflush = jazz_fd_cacheflush; feature = &jazz_feature; // Will go away + port_base = JAZZ_PORT_BASE; isa_slot_offset = 0xe3000000; request_region(0x00,0x20,"dma1"); request_region(0x40,0x20,"timer"); request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); + board_time_init = jazz_time_init; /* The RTC is outside the port address space */ _machine_restart = jazz_machine_restart; diff -u --recursive --new-file v2.1.47/linux/arch/mips/kernel/irixelf.c linux/arch/mips/kernel/irixelf.c --- v2.1.47/linux/arch/mips/kernel/irixelf.c Mon Jul 7 08:18:53 1997 +++ linux/arch/mips/kernel/irixelf.c Thu Jul 31 13:09:17 1997 @@ -228,7 +228,7 @@ * an ELF header. */ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex, - struct inode * interpreter_inode, + struct dentry * interpreter_dentry, unsigned int *interp_load_addr) { struct file * file; @@ -256,8 +256,8 @@ if((interp_elf_ex->e_type != ET_EXEC && interp_elf_ex->e_type != ET_DYN) || !elf_check_arch(interp_elf_ex->e_machine) || - (!interpreter_inode->i_op || - !interpreter_inode->i_op->default_file_ops->mmap)){ + (!interpreter_dentry->d_inode->i_op || + !interpreter_dentry->d_inode->i_op->default_file_ops->mmap)) { printk("IRIX interp has bad e_type %d\n", interp_elf_ex->e_type); return 0xffffffff; } @@ -288,7 +288,7 @@ return 0xffffffff; } - retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, + retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1); @@ -296,7 +296,7 @@ dump_phdrs(elf_phdata, interp_elf_ex->e_phnum); #endif - elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY); + elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY); if (elf_exec_fileno < 0) { printk("Could not open IRIX interp inode.\n"); kfree(elf_phdata); @@ -408,8 +408,9 @@ /* First of all, some simple consistency checks */ if((ehp->e_type != ET_EXEC && ehp->e_type != ET_DYN) || !elf_check_arch(ehp->e_machine) || - (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)) { + (!bprm->dentry->d_inode->i_op || + !bprm->dentry->d_inode->i_op->default_file_ops || + !bprm->dentry->d_inode->i_op->default_file_ops->mmap)) { return -ENOEXEC; } @@ -435,13 +436,14 @@ /* Look for an IRIX ELF interpreter. */ static inline int look_for_irix_interpreter(char **name, - struct inode **interpreter_inode, + struct dentry **interpreter_dentry, struct elfhdr *interp_elf_ex, struct elf_phdr *epp, struct linux_binprm *bprm, int pnum) { int i, old_fs; int retval = -EINVAL; + struct dentry *dentry = NULL; *name = NULL; for(i = 0; i < pnum; i++, epp++) { @@ -450,7 +452,7 @@ /* It is illegal to have two interpreters for one executable. */ if(*name != NULL) - goto losing; + goto out; *name = (char *) kmalloc((epp->p_filesz + strlen(IRIX_INTERP_PREFIX)), @@ -459,26 +461,31 @@ return -ENOMEM; strcpy(*name, IRIX_INTERP_PREFIX); - retval = read_exec(bprm->inode, epp->p_offset, (*name + 16), + retval = read_exec(bprm->dentry, epp->p_offset, (*name + 16), epp->p_filesz, 1); if(retval < 0) - goto losing; + goto out; old_fs = get_fs(); set_fs(get_ds()); - retval = namei(NAM_FOLLOW_LINK, *name, interpreter_inode); + dentry = namei(*name); set_fs(old_fs); - if(retval < 0) - goto losing; + if(IS_ERR(dentry)) { + retval = PTR_ERR(dentry); + goto out; + } - retval = read_exec(*interpreter_inode, 0, bprm->buf, 128, 1); - if(retval < 0) - goto losing; + retval = read_exec(dentry, 0, bprm->buf, 128, 1); + if(retval) + goto dput_and_out; *interp_elf_ex = *((struct elfhdr *) bprm->buf); } + *interpreter_dentry = dentry; return 0; -losing: +dput_and_out: + dput(dentry); +out: kfree(*name); return retval; } @@ -538,7 +545,7 @@ } static inline int map_interpreter(struct elf_phdr *epp, struct elfhdr *ihp, - struct inode *iino, unsigned int *iladdr, + struct dentry *identry, unsigned int *iladdr, int pnum, int old_fs, unsigned int *eentry) { @@ -554,11 +561,11 @@ return -1; set_fs(old_fs); - *eentry = load_irix_interp(ihp, iino, iladdr); + *eentry = load_irix_interp(ihp, identry, iladdr); old_fs = get_fs(); set_fs(get_ds()); - iput(iino); + dput(identry); if(*eentry == 0xffffffff) return -1; @@ -573,7 +580,7 @@ struct pt_regs * regs) { struct elfhdr elf_ex, interp_elf_ex; - struct inode *interpreter_inode; + struct dentry *interpreter_dentry; struct elf_phdr *elf_phdata, *elf_ihdr, *elf_ephdr; unsigned int load_addr, elf_bss, elf_brk; unsigned int elf_entry, interp_load_addr = 0; @@ -599,7 +606,7 @@ if (elf_phdata == NULL) return -ENOMEM; - retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata, + retval = read_exec(bprm->dentry, elf_ex.e_phoff, (char *) elf_phdata, elf_ex.e_phentsize * elf_ex.e_phnum, 1); if (retval < 0) { kfree (elf_phdata); @@ -629,7 +636,7 @@ elf_bss = 0; elf_brk = 0; - elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); + elf_exec_fileno = open_dentry(bprm->dentry, O_RDONLY); if (elf_exec_fileno < 0) { kfree (elf_phdata); @@ -642,7 +649,8 @@ end_code = 0; end_data = 0; - retval = look_for_irix_interpreter(&elf_interpreter, &interpreter_inode, + retval = look_for_irix_interpreter(&elf_interpreter, + &interpreter_dentry, &interp_elf_ex, elf_phdata, bprm, elf_ex.e_phnum); if(retval) { @@ -703,7 +711,7 @@ if(elf_interpreter) { retval = map_interpreter(elf_phdata, &interp_elf_ex, - interpreter_inode, &interp_load_addr, + interpreter_dentry, &interp_load_addr, elf_ex.e_phnum, old_fs, &elf_entry); kfree(elf_interpreter); if(retval) { @@ -795,7 +803,8 @@ struct file * file; struct elfhdr elf_ex; struct elf_phdr *elf_phdata = NULL; - struct inode * inode; + struct dentry *dentry; + struct inode *inode; unsigned int len; int elf_bss; int retval; @@ -805,7 +814,8 @@ len = 0; file = current->files->fd[fd]; - inode = file->f_inode; + dentry = file->f_dentry; + inode = dentry->d_inode; elf_bss = 0; if (!file || !file->f_op) @@ -831,7 +841,8 @@ /* First of all, some simple consistency checks. */ if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || !elf_check_arch(elf_ex.e_machine) || - (!inode->i_op || !inode->i_op->default_file_ops->mmap)) + (!dentry->d_inode->i_op || + !dentry->d_inode->i_op->default_file_ops->mmap)) return -ENOEXEC; /* Now read in all of the header information. */ @@ -843,7 +854,7 @@ if (elf_phdata == NULL) return -ENOMEM; - retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata, + retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * elf_ex.e_phnum, 1); j = 0; @@ -973,14 +984,13 @@ */ static int dump_write(struct file *file, const void *addr, int nr) { - file->f_inode->i_status |= ST_MODIFIED; - return file->f_op->write(file->f_inode, file, addr, nr) == nr; + return file->f_op->write(file->f_dentry->d_inode, file, addr, nr) == nr; } static int dump_seek(struct file *file, off_t off) { if (file->f_op->llseek) { - if (file->f_op->llseek(file->f_inode, file, off, 0) != off) + if (file->f_op->llseek(file->f_dentry->d_inode, file, off, 0) != off) return 0; } else file->f_pos = off; @@ -1071,6 +1081,7 @@ { int has_dumped = 0; struct file file; + struct dentry *dentry; struct inode *inode; unsigned short fs; char corefile[6+sizeof(current->comm)]; @@ -1138,30 +1149,24 @@ fs = get_fs(); set_fs(KERNEL_DS); - memcpy(corefile,"core.",5); + memcpy(corefile,"core.", 5); #if 0 memcpy(corefile+5,current->comm,sizeof(current->comm)); #else corefile[4] = '\0'; #endif - if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) { + dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC, 0600); + if (IS_ERR(dentry)) { inode = NULL; goto end_coredump; } + inode = dentry->d_inode; if (!S_ISREG(inode->i_mode)) goto end_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) goto end_coredump; - file.f_mode = 3; - file.f_flags = 0; - file.f_count = 1; - file.f_inode = inode; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto end_coredump; + if (init_private_file(&file, dentry, 3)) + goto end_coredump; if (!file.f_op->write) goto close_coredump; has_dumped = 1; @@ -1330,11 +1335,11 @@ close_coredump: if (file.f_op->release) - file.f_op->release(inode,&file); + file.f_op->release(inode, &file); end_coredump: set_fs(fs); - iput(inode); + dput(dentry); #ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; #endif diff -u --recursive --new-file v2.1.47/linux/arch/mips/kernel/syscall.c linux/arch/mips/kernel/syscall.c --- v2.1.47/linux/arch/mips/kernel/syscall.c Thu Jun 26 12:33:37 1997 +++ linux/arch/mips/kernel/syscall.c Thu Jul 31 13:09:17 1997 @@ -131,20 +131,21 @@ */ asmlinkage int sys_execve(struct pt_regs *regs) { - int res; + int error; char * filename; lock_kernel(); - res = getname((char *) (long)regs->regs[4], &filename); - if (res) + filename = getname((char *) (long)regs->regs[4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; - res = do_execve(filename, (char **) (long)regs->regs[5], - (char **) (long)regs->regs[6], regs); + error = do_execve(filename, (char **) (long)regs->regs[5], + (char **) (long)regs->regs[6], regs); putname(filename); out: unlock_kernel(); - return res; + return error; } /* diff -u --recursive --new-file v2.1.47/linux/arch/mips/kernel/syscalls.h linux/arch/mips/kernel/syscalls.h --- v2.1.47/linux/arch/mips/kernel/syscalls.h Mon Jul 7 08:18:53 1997 +++ linux/arch/mips/kernel/syscalls.h Thu Jul 31 13:09:17 1997 @@ -7,7 +7,7 @@ * * Copyright (C) 1995, 1996 by Ralf Baechle * - * $Id: syscalls.h,v 1.5 1997/06/25 17:08:35 ralf Exp $ + * $Id: syscalls.h,v 1.6 1997/07/20 15:32:25 ralf Exp $ */ /* @@ -208,3 +208,5 @@ SYS(sys_query_module, 5) SYS(sys_poll, 3) SYS(sys_nfsservctl, 3) +SYS(sys_setresgid, 3) /* 4190 */ +SYS(sys_getresgid, 3) diff -u --recursive --new-file v2.1.47/linux/arch/mips/kernel/sysirix.c linux/arch/mips/kernel/sysirix.c --- v2.1.47/linux/arch/mips/kernel/sysirix.c Mon Jul 7 08:18:54 1997 +++ linux/arch/mips/kernel/sysirix.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: sysirix.c,v 1.2 1997/06/17 15:24:26 ralf Exp $ +/* $Id: sysirix.c,v 1.3 1997/07/20 15:32:25 ralf Exp $ * sysirix.c: IRIX system call emulation. * * Copyright (C) 1996 David S. Miller @@ -652,6 +652,7 @@ asmlinkage int irix_statfs(const char *path, struct irix_statfs *buf, int len, int fs_type) { + struct dentry *dentry; struct inode *inode; struct statfs kbuf; int error, old_fs, i; @@ -664,20 +665,19 @@ error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statfs)); if (error) goto out; - error = namei(NAM_FOLLOW_LINK, path, &inode); - if (error) - goto out; - if (!inode->i_sb->s_op->statfs) { - iput(inode); - error = -ENOSYS; + dentry = namei(path); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; - } + inode = dentry->d_inode; old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto dput_and_out; - iput(inode); __put_user(kbuf.f_type, &buf->f_type); __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); @@ -691,6 +691,8 @@ } error = 0; +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -698,7 +700,8 @@ asmlinkage int irix_fstatfs(unsigned int fd, struct irix_statfs *buf) { - struct inode * inode; + struct dentry *dentry; + struct inode *inode; struct statfs kbuf; struct file *file; int error, old_fs, i; @@ -711,18 +714,29 @@ error = -EBADF; goto out; } - if (!(inode = file->f_inode)) { + if (!(dentry = file->f_dentry)) { + error = -ENOENT; + goto out; + } + if (!(inode = dentry->d_inode)) { error = -ENOENT; goto out; } + if (!inode->i_sb) { + error = -ENODEV; + goto out; + } if (!inode->i_sb->s_op->statfs) { error = -ENOSYS; goto out; } old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto out; __put_user(kbuf.f_type, &buf->f_type); __put_user(kbuf.f_bsize, &buf->f_bsize); @@ -789,13 +803,14 @@ asmlinkage int irix_exec(struct pt_regs *regs) { int error, base = 0; - char * filename; + char *filename; lock_kernel(); if(regs->regs[2] == 1000) base = 1; - error = getname((char *) (long)regs->regs[base + 4], &filename); - if (error) + filename = getname((char *) (long)regs->regs[base + 4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; error = do_execve(filename, (char **) (long)regs->regs[base + 5], (char **) 0, regs); @@ -809,13 +824,14 @@ asmlinkage int irix_exece(struct pt_regs *regs) { int error, base = 0; - char * filename; + char *filename; lock_kernel(); if(regs->regs[2] == 1000) base = 1; - error = getname((char *) (long)regs->regs[base + 4], &filename); - if (error) + filename = getname((char *) (long)regs->regs[base + 4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; error = do_execve(filename, (char **) (long)regs->regs[base + 5], (char **) (long)regs->regs[base + 6], regs); @@ -1380,6 +1396,7 @@ asmlinkage int irix_statvfs(char *fname, struct irix_statvfs *buf) { + struct dentry *dentry; struct inode *inode; struct statfs kbuf; int error, old_fs, i; @@ -1390,20 +1407,23 @@ error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs)); if(error) goto out; - error = namei(NAM_FOLLOW_LINK, fname, &inode); - if(error) - goto out; - if(!inode->i_sb->s_op->statfs) { - iput(inode); - error = -ENOSYS; - goto out; - } + dentry = namei(fname); + error = PTR_ERR(dentry); + if(!IS_ERR(dentry)) + goto out; + inode = dentry->d_inode; + + error = -ENOSYS; + if(!inode->i_sb->s_op->statfs) + goto dput_and_out; old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto dput_and_out; - iput(inode); __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); __put_user(kbuf.f_blocks, &buf->f_blocks); @@ -1426,6 +1446,8 @@ error = 0; +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -1433,7 +1455,8 @@ asmlinkage int irix_fstatvfs(int fd, struct irix_statvfs *buf) { - struct inode * inode; + struct dentry *dentry; + struct inode *inode; struct statfs kbuf; struct file *file; int error, old_fs, i; @@ -1449,7 +1472,11 @@ error = -EBADF; goto out; } - if (!(inode = file->f_inode)) { + if (!(dentry = file->f_dentry)) { + error = -ENOENT; + goto out; + } + if (!(inode = dentry->d_inode)) { error = -ENOENT; goto out; } @@ -1459,8 +1486,11 @@ } old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto out; __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); @@ -1489,26 +1519,28 @@ return error; } -#define NOFOLLOW_LINKS NAM_FOLLOW_TRAILSLASH -#define FOLLOW_LINKS NAM_FOLLOW_LINK +#define NOFOLLOW_LINKS 0 +#define FOLLOW_LINKS 1 -static inline int chown_common(char *filename, uid_t user, gid_t group, int follow) +static inline int chown_common(uid_t user, gid_t group, struct dentry *dentry) { struct inode * inode; int error; struct iattr newattrs; - error = namei(follow, filename,&inode); - if (error) - return error; - if (IS_RDONLY(inode)) { - iput(inode); - return -EROFS; - } - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { - iput(inode); - return -EPERM; - } + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto out; + inode = dentry->d_inode; + + error = -EROFS; + if (IS_RDONLY(inode)) + goto dput_and_out; + + error = -EPERM; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + goto dput_and_out; + if (user == (uid_t) -1) user = inode->i_uid; if (group == (gid_t) -1) @@ -1534,38 +1566,45 @@ newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } - inode->i_dirt = 1; if (inode->i_sb->dq_op) { inode->i_sb->dq_op->initialize(inode, -1); + error = -EDQUOT; if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) - return -EDQUOT; + goto dput_and_out; error = notify_change(inode, &newattrs); if (error) inode->i_sb->dq_op->transfer(inode, &newattrs, 1); } else error = notify_change(inode, &newattrs); - iput(inode); - return(error); + +dput_and_out: + dput(dentry); +out: + return error; } -asmlinkage int irix_chown(char *fname, int uid, int gid) +asmlinkage int irix_chown(const char *filename, int uid, int gid) { int retval; + struct dentry *dentry; lock_kernel(); /* Do follow any and all links... */ - retval = chown_common(fname, uid, gid, FOLLOW_LINKS); + dentry = namei(filename); + retval = chown_common(uid, gid, dentry); unlock_kernel(); return retval; } -asmlinkage int irix_lchown(char *fname, int uid, int gid) +asmlinkage int irix_lchown(const char *filename, int uid, int gid) { int retval; + struct dentry *dentry; lock_kernel(); /* Do _not_ follow any links... */ - retval = chown_common(fname, uid, gid, NOFOLLOW_LINKS); + dentry = lnamei(filename); + retval = chown_common(uid, gid, dentry); unlock_kernel(); return retval; } @@ -1722,6 +1761,7 @@ asmlinkage int irix_statvfs64(char *fname, struct irix_statvfs64 *buf) { + struct dentry *dentry; struct inode *inode; struct statfs kbuf; int error, old_fs, i; @@ -1731,20 +1771,22 @@ error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs)); if(error) goto out; - error = namei(NAM_FOLLOW_LINK, fname, &inode); - if(error) - goto out; - if(!inode->i_sb->s_op->statfs) { - iput(inode); - error = -ENOSYS; - goto out; - } + dentry = namei(fname); + error = PTR_ERR(dentry); + if(IS_ERR(dentry)) + goto out; + error = -ENOSYS; + inode = dentry->d_inode; + if(!inode->i_sb->s_op->statfs) + goto dput_and_out; old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto dput_and_out; - iput(inode); __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); __put_user(kbuf.f_blocks, &buf->f_blocks); @@ -1767,6 +1809,8 @@ error = 0; +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -1774,7 +1818,8 @@ asmlinkage int irix_fstatvfs64(int fd, struct irix_statvfs *buf) { - struct inode * inode; + struct dentry *dentry; + struct inode *inode; struct statfs kbuf; struct file *file; int error, old_fs, i; @@ -1790,7 +1835,11 @@ error = -EBADF; goto out; } - if (!(inode = file->f_inode)) { + if (!(dentry = file->f_dentry)) { + error = -ENOENT; + goto out; + } + if (!(inode = dentry->d_inode)) { error = -ENOENT; goto out; } @@ -1800,8 +1849,11 @@ } old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto out; __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); @@ -1927,6 +1979,8 @@ asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count, int *eob) { struct file *file; + struct dentry *dentry; + struct inode *inode; struct irix_dirent32 *lastdirent; struct irix_dirent32_callback buf; int error; @@ -1936,25 +1990,34 @@ printk("[%s:%d] ngetdents(%d, %p, %d, %p) ", current->comm, current->pid, fd, dirent, count, eob); #endif - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) { - error = -EBADF; + error = -EBADF; + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; - } - if (!file->f_op || !file->f_op->readdir) { - error = -ENOTDIR; + + dentry = file->f_dentry; + if (!dentry) goto out; - } - if(verify_area(VERIFY_WRITE, dirent, count) || - verify_area(VERIFY_WRITE, eob, sizeof(*eob))) { - error = -EFAULT; + + inode = dentry->d_inode; + if (!inode) goto out; - } + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = -EFAULT; + if(!access_ok(VERIFY_WRITE, dirent, count) || + !access_ok(VERIFY_WRITE, eob, sizeof(*eob))) + goto out; + __put_user(0, eob); buf.current_dir = (struct irix_dirent32 *) dirent; buf.previous = NULL; buf.count = count; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, irix_filldir32); + + error = file->f_op->readdir(inode, file, &buf, irix_filldir32); if (error < 0) goto out; lastdirent = buf.previous; @@ -2027,6 +2090,8 @@ asmlinkage int irix_getdents64(int fd, void *dirent, int cnt) { struct file *file; + struct dentry *dentry; + struct inode *inode; struct irix_dirent64 *lastdirent; struct irix_dirent64_callback buf; int error; @@ -2036,28 +2101,35 @@ printk("[%s:%d] getdents64(%d, %p, %d) ", current->comm, current->pid, fd, dirent, cnt); #endif - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) { - error = -EBADF; + error = -EBADF; + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; - } - if (!file->f_op || !file->f_op->readdir) { - error = -ENOTDIR; + + dentry = file->f_dentry; + if (!dentry) goto out; - } - if(verify_area(VERIFY_WRITE, dirent, cnt)) { - error = -EFAULT; + + inode = dentry->d_inode; + if (!inode) goto out; - } - if(cnt < (sizeof(struct irix_dirent64) + 255)) { - error = -EINVAL; + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = -EFAULT; + if(!access_ok(VERIFY_WRITE, dirent, cnt)) + goto out; + + error = -EINVAL; + if(cnt < (sizeof(struct irix_dirent64) + 255)) goto out; - } buf.curr = (struct irix_dirent64 *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, irix_filldir64); + error = file->f_op->readdir(inode, file, &buf, irix_filldir64); if (error < 0) goto out; lastdirent = buf.previous; @@ -2079,6 +2151,8 @@ asmlinkage int irix_ngetdents64(int fd, void *dirent, int cnt, int *eob) { struct file *file; + struct dentry *dentry; + struct inode *inode; struct irix_dirent64 *lastdirent; struct irix_dirent64_callback buf; int error; @@ -2088,30 +2162,37 @@ printk("[%s:%d] ngetdents64(%d, %p, %d) ", current->comm, current->pid, fd, dirent, cnt); #endif - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) { - error = -EBADF; + error = -EBADF; + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; - } - if (!file->f_op || !file->f_op->readdir) { - error = -ENOTDIR; + + dentry = file->f_dentry; + if (!dentry) goto out; - } - if(verify_area(VERIFY_WRITE, dirent, cnt) || - verify_area(VERIFY_WRITE, eob, sizeof(*eob))) { - error = -EFAULT; + + inode = dentry->d_inode; + if (!inode) goto out; - } - if(cnt < (sizeof(struct irix_dirent64) + 255)) { - error = -EINVAL; + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = -EFAULT; + if(!access_ok(VERIFY_WRITE, dirent, cnt) || + !access_ok(VERIFY_WRITE, eob, sizeof(*eob))) + goto out; + + error = -EINVAL; + if(cnt < (sizeof(struct irix_dirent64) + 255)) goto out; - } *eob = 0; buf.curr = (struct irix_dirent64 *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, irix_filldir64); + error = file->f_op->readdir(inode, file, &buf, irix_filldir64); if (error < 0) goto out; lastdirent = buf.previous; diff -u --recursive --new-file v2.1.47/linux/arch/mips/kernel/sysmips.c linux/arch/mips/kernel/sysmips.c --- v2.1.47/linux/arch/mips/kernel/sysmips.c Mon Jul 7 08:18:54 1997 +++ linux/arch/mips/kernel/sysmips.c Thu Jul 31 13:09:17 1997 @@ -7,7 +7,7 @@ * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle * - * $Id: sysmips.c,v 1.4 1997/06/30 15:52:37 ralf Exp $ + * $Id: sysmips.c,v 1.5 1997/07/20 15:32:27 ralf Exp $ */ #include #include @@ -57,18 +57,21 @@ switch(cmd) { case SETNAME: - if (!suser()) { - retval = -EPERM; + retval = -EPERM; + if (!suser()) goto out; - } + name = (char *) arg1; len = strlen_user(name); + + retval = len; if (len < 0) - retval = len; goto out; + + retval = -EINVAL; if (len == 0 || len > __NEW_UTS_LEN) - retval = -EINVAL; goto out; + copy_from_user(system_utsname.nodename, name, len); system_utsname.nodename[len] = '\0'; retval = 0; diff -u --recursive --new-file v2.1.47/linux/arch/mips/mm/fault.c linux/arch/mips/mm/fault.c --- v2.1.47/linux/arch/mips/mm/fault.c Thu Jun 26 12:33:38 1997 +++ linux/arch/mips/mm/fault.c Thu Jul 31 13:09:17 1997 @@ -81,17 +81,6 @@ */ bad_area: up(&mm->mmap_sem); - /* Did we have an exception handler installed? */ - - fixup = search_exception_table(regs->cp0_epc); - if (fixup) { - long new_epc; - new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc); - printk(KERN_DEBUG "Exception at [<%lx>] (%lx)\n", - regs->cp0_epc, new_epc); - regs->cp0_epc = new_epc; - goto out; - } if (user_mode(regs)) { tsk->tss.cp0_badvaddr = address; @@ -111,6 +100,18 @@ force_sig(SIGSEGV, tsk); goto out; } + + /* Did we have an exception handler installed? */ + fixup = search_exception_table(regs->cp0_epc); + if (fixup) { + long new_epc; + new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc); + printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n", + tsk->comm, regs->cp0_epc, new_epc); + regs->cp0_epc = new_epc; + goto out; + } + /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. diff -u --recursive --new-file v2.1.47/linux/arch/mips/mm/init.c linux/arch/mips/mm/init.c --- v2.1.47/linux/arch/mips/mm/init.c Thu Jun 26 12:33:38 1997 +++ linux/arch/mips/mm/init.c Thu Jul 31 13:09:17 1997 @@ -156,7 +156,6 @@ zeropage(unsigned long page) { flush_page_to_ram(page); - sync_mem(); __zeropage(page); } diff -u --recursive --new-file v2.1.47/linux/arch/ppc/Makefile linux/arch/ppc/Makefile --- v2.1.47/linux/arch/ppc/Makefile Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/Makefile Thu Jul 31 13:09:17 1997 @@ -25,61 +25,62 @@ CC = gcc$(SUFFIX) CFLAGSINC = -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__ CFLAGS = $(CFLAGSINC) \ - -Wstrict-prototypes \ - -fomit-frame-pointer \ + -Wstrict-prototypes -fomit-frame-pointer \ -fno-builtin \ - -finhibit-size-directive -fno-strength-reduce\ - -O2 -fsigned-char -pipe -mstring -mmultiple + -finhibit-size-directive \ + -O2 -fsigned-char -pipe -ffixed-r2 -mstring -mmultiple -msoft-float +# -fverbose-asm CPP = $(CC) -E $(CFLAGS) AR = ar$(SUFFIX) RANLIB = ranlib$(SUFFIX) STRIP = strip$(SUFFIX) NM = nm$(SUFFIX) -ifdef CONFIG_603 -CFLAGS := $(CFLAGS) -mcpu=603 -DCPU=603 +ifdef CONFIG_601 +CFLAGS := $(CFLAGS) -mcpu=601 -DCPU=601 endif -ifdef CONFIG_603e -CFLAGS := $(CFLAGS) -mcpu=603e -DCPU=603e +ifdef CONFIG_603 +CFLAGS := $(CFLAGS) -mcpu=603 -DCPU=603 endif ifdef CONFIG_604 CFLAGS := $(CFLAGS) -mcpu=604 -DCPU=604 endif -# -# NFS_ROOT_NAME specifies the default name of the directory to mount -# as root via NFS, if the kernel does not get the "root=" option from -# the boot loader. The "%s" will be replaced by the IP-number of the -# local system. -# -NFS_ROOT = -DNFS_ROOT="\"/joplin/ppc/root\"" - HEAD := arch/ppc/kernel/head.o ARCH_SUBDIRS = arch/ppc/kernel arch/ppc/mm arch/ppc/lib SUBDIRS := $(SUBDIRS) $(ARCH_SUBDIRS) ARCHIVES := arch/ppc/kernel/kernel.o arch/ppc/mm/mm.o arch/ppc/lib/lib.o $(ARCHIVES) - +CORE_FILES := arch/ppc/kernel/kernel.o arch/ppc/mm/mm.o arch/ppc/lib/lib.o $(CORE_FILES) MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot -netboot: vmlinux +checks: + @$(MAKE) -C arch/$(ARCH)/kernel checks + +netboot: checks vmlinux @$(MAKEBOOT) netboot -znetboot: vmlinux +znetboot: checks vmlinux @$(MAKEBOOT) znetboot -zImage: vmlinux +#rcpboot: checks vmlinux +# @$(MAKEBOOT) rcpboot + +zImage: checks vmlinux @$(MAKEBOOT) zImage -floppy: vmlinux +floppy: checks vmlinux @$(MAKEBOOT) floppy -install: vmlinux +install: checks vmlinux @$(MAKEBOOT) install +vmlinux.coff : checks vmlinux + $(MAKE) -C arch/ppc/coffboot/ vmlinux.coff + arch/ppc/kernel: dummy $(MAKE) linuxsubdirs SUBDIRS=arch/ppc/kernel @@ -89,12 +90,24 @@ arch/ppc/lib: dummy $(MAKE) linuxsubdirs SUBDIRS=arch/ppc/lib +diffs: + arch/ppc/mkdiff + +tar: + arch/ppc/mktar archclean: - rm -f arch/ppc/kernel/mk_defs arch/ppc/kernel/ppc_defs.h TAGS + rm -f arch/ppc/kernel/mk_defs arch/ppc/kernel/ppc_defs.h arch/ppc/kernel/checks TAGS + rm -f `find arch/ppc/ \( -name '*.[oas]' -o -name '*~' -o -name '#*#' \) -print` + rm -f `find include/asm-ppc/ \( -name '*.[oas]' -o -name '*~' -o -name '#*#' \) -print` @$(MAKEBOOT) clean archdep: + $(MAKE) -C arch/ppc/boot fastdep + $(MAKE) -C arch/ppc/kernel fastdep + $(MAKE) -C arch/ppc/mm fastdep + $(MAKE) -C arch/ppc/lib fastdep + +tags : + etags arch/ppc/*/*.c arch/ppc/*/*.S include/asm/* */*.c -corttags : - etags arch/ppc/*/*.c include/asm/* */*.c drivers/*/*.c net/*.c diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile --- v2.1.47/linux/arch/ppc/boot/Makefile Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/Makefile Thu Jul 31 13:09:17 1997 @@ -7,6 +7,7 @@ # # Copyright (C) 1994 by Linus Torvalds # Adapted for PowerPC by Gary Thomas +# modified by Cort (cort@cs.nmt.edu) # .c.s: @@ -22,7 +23,7 @@ ZLINKFLAGS = -T ../ld.script -Ttext 0x00800000 -GZIP_FLAGS = -9 +GZIP_FLAGS = -v9 SYSTEM = $(TOPDIR)/vmlinux @@ -32,49 +33,51 @@ all: $(TOPDIR)/zImage -mkboot : cortstrip.c - $(HOSTCC) $(CFLAGSINC) -Wl,-static -o mkboot cortstrip.c +mkprep : mkprep.c + $(HOSTCC) $(CFLAGSINC) -o mkprep mkprep.c + +find_name : find_name.c + $(HOSTCC) $(CFLAGSINC) -o find_name find_name.c mk_type41: mk_type41.c - $(HOSTCC) $(CFLAGSINC) -Wl,-static -o mk_type41 mk_type41.c + $(HOSTCC) $(CFLAGSINC) -o mk_type41 mk_type41.c + +piggyback: piggyback.c + $(HOSTCC) $(CFLAGS) -o piggyback piggyback.c -floppy: zImage $(TOPDIR)/vmlinux +floppy: $(TOPDIR)/vmlinux zImage dd if=$(TOPDIR)/zImage of=/dev/fd0H1440 bs=64b -netboot : $(TOPDIR)/vmlinux mkboot - mkboot $(TOPDIR)/vmlinux $(TOPDIR)/netboot -# rcp $(TOPDIR)/netboot charon:/usr/tftpboot/vmlinux +netboot : $(TOPDIR)/vmlinux mkprep + mkprep $(TOPDIR)/vmlinux $(TOPDIR)/netboot + +znetboot : zvmlinux mkprep + mkprep zvmlinux $(TOPDIR)/znetboot + cp $(TOPDIR)/znetboot /usr/local/tftpboot/vmlinux -znetboot : mkboot zvmlinux - mkboot zvmlinux $(TOPDIR)/znetboot +rcpboot : znetboot rcp $(TOPDIR)/znetboot charon:/usr/tftpboot/vmlinux -zImage: mk_type41 zvmlinux -# make znetboot ourselves since using the normal dep -# will rcp it -- Cort - mkboot zvmlinux $(TOPDIR)/znetboot - mk_type41 $(TOPDIR)/znetboot $(TOPDIR)/zImage +zImage: zvmlinux mkprep + mkprep -pbp zvmlinux $(TOPDIR)/zImage install: zImage dd if=$(TOPDIR)/zImage of=/dev/sda4 ln -s /dev/sda4 $(INSTALL_PATH)/vmlinuz cp $(TOPDIR)/System.map $(INSTALL_PATH)/ -zvmlinux: $(OBJECTS) $(SYSTEM) piggyback netboot $(TOPDIR)/vmlinux - gzip ${GZIP_FLAGS} <$(TOPDIR)/netboot | ./piggyback | $(AS) -o piggy.o +zvmlinux: $(OBJECTS) $(SYSTEM) mkprep find_name + mkprep $(TOPDIR)/vmlinux -|gzip ${GZIP_FLAGS}|mkprep -asm - -|$(AS) -o piggy.o $(LD) $(ZLINKFLAGS) -o zvmlinux $(OBJECTS) piggy.o - rm -f piggy.o xx_boot - -head.o: head.s - -head.s: head.S $(TOPDIR)/include/linux/tasks.h - $(CPP) -traditional head.S -o head.s - -piggyback: piggyback.c - $(HOSTCC) $(CFLAGS) -o piggyback piggyback.c + rm -f piggy.o clean: - rm -f piggyback zvmlinux mk_type41 mkprep mkboot + rm -f piggyback zvmlinux mk_type41 mkprep mkboot find_name rm -f $(TOPDIR)/{zImage,znetboot,netboot} +fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend + dep: + $(CPP) -M *.S *.c > .depend + diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/cortstrip.c linux/arch/ppc/boot/cortstrip.c --- v2.1.47/linux/arch/ppc/boot/cortstrip.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/cortstrip.c Wed Dec 31 16:00:00 1969 @@ -1,73 +0,0 @@ -#include -#include -#include -#include - -/* amount to skip */ -#define PLACE 65536 - -/* size of read buffer */ -#define SIZE 0x100000 - -/* crude program to strip the elf header to make a bootable - image via tftp - */ - - -int main(int argc, char **argv ) -{ - int fd, fdo; - unsigned char data[SIZE]; - int i, n, skip; - -#if 0 - if ( argc != 3 ) - { - fprintf(stderr,"%s infile outfile\n", argv[0]); - exit(-1); - } -#endif - - - fd = open(argv[1], O_RDONLY); - if ( fd == -1 ) - { - fprintf(stderr,"Couldn't open %s\n", argv[1]); - perror("open()"); - exit(-1); - } - - fdo = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC,0755); - if ( fdo == -1 ) - { - fprintf(stderr,"Couldn't open %s\n", argv[2]); - perror("open()"); - exit(-1); - } - -#if 0 - skip = atoi(argv[3]); -#else - skip = PLACE; -#endif - i = lseek(fd, skip, SEEK_SET); - /*printf("lseek'd %d bytes\n", i);*/ - if ( i == -1 ) - { - perror("lseek()"); - } - - while ( (n = read(fd, data, SIZE)) > 0 ) - { - /*printf("Read %d bytes\n", n);*/ - i = write(fdo, data, n); - /*printf("Wrote %d bytes\n", i); */ - } - - - close(fdo); - close(fd); - return(0); -} - - diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/find_name.c linux/arch/ppc/boot/find_name.c --- v2.1.47/linux/arch/ppc/boot/find_name.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/boot/find_name.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,47 @@ +#include +#include +#include +/* + * Finds a given address in the System.map and prints it out + * with its neighbors. -- Cort + */ + +void main(int argc, char **argv) +{ + unsigned long addr, cmp, i; + FILE *f; + char *ptr; + char s[256], last[256]; + + if ( argc < 2 ) + { + fprintf(stderr, "Usage: %s
\n", argv[0]); + exit(-1); + } + + for ( i = 1 ; argv[i] ; i++ ) + { + sscanf( argv[i], "%0x", &addr ); + /* adjust if addr is relative to kernelbase */ + if ( addr < PAGE_OFFSET ) + addr += PAGE_OFFSET; + + if ( (f = fopen( "System.map", "r" )) == NULL ) + { + perror("fopen()\n"); + exit(-1); + } + + while ( !feof(f) ) + { + fgets(s, 255 , f); + sscanf( s, "%0x", &cmp ); + if ( addr < cmp ) + break; + strcpy( last, s); + } + + printf( "%s", last); + } + fclose(f); +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/gzip.h linux/arch/ppc/boot/gzip.h --- v2.1.47/linux/arch/ppc/boot/gzip.h Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/gzip.h Thu Jul 31 13:09:17 1997 @@ -271,7 +271,7 @@ extern void flush_outbuf OF((void)); extern void flush_window OF((void)); extern char *strlwr OF((char *s)); -extern char *basename OF((char *fname)); +/*extern char *basename OF((char *fname));*/ extern char *add_envopt OF((int *argcp, char ***argvp, char *env)); extern void error OF((char *m)); extern void warn OF((char *a, char *b)); diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/head.S linux/arch/ppc/boot/head.S --- v2.1.47/linux/arch/ppc/boot/head.S Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/head.S Thu Jul 31 13:09:17 1997 @@ -1,5 +1,6 @@ #include "../kernel/ppc_defs.h" #include "../kernel/ppc_asm.tmpl" +#include .text diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/inflate.c linux/arch/ppc/boot/inflate.c --- v2.1.47/linux/arch/ppc/boot/inflate.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/inflate.c Thu Jul 31 13:09:17 1997 @@ -9,7 +9,7 @@ */ #ifndef lint -static char rcsid[] = "$Id: inflate.c,v 0.10 1993/02/04 13:21:06 jloup Exp $"; +static char rcsid[] = "$Id: inflate.c,v 1.1.1.1 1997/02/25 05:18:11 cort Exp $"; #endif #include "gzip.h" diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/misc.c linux/arch/ppc/boot/misc.c --- v2.1.47/linux/arch/ppc/boot/misc.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/misc.c Thu Jul 31 13:09:17 1997 @@ -327,7 +327,7 @@ _put_MSR(_get_MSR() & ~0x0030); vga_init(0xC0000000); - clear_screen(); + /*clear_screen();*/ output_ptr = 0; @@ -373,93 +373,6 @@ #endif } -#if 0 -verify_ram() -{ - unsigned long loc; - puts("Clearing memory:"); - for (loc = 0; loc <= 0x400000; loc += 4); - { - *(unsigned long *)loc = 0x0; - } - for (loc = 0; loc <= 0x400000; loc += 4); - { - if (*(unsigned long *)loc != 0x0) - { - puts(" - failed at "); - puthex(loc); - puts(": "); - puthex(*(unsigned long *)loc); - while (1); - } - } - puts("0"); - for (loc = 0; loc <= 0x400000; loc += 4); - { - *(unsigned long *)loc = 0xFFFFFFFF; - } - for (loc = 0; loc <= 0x400000; loc += 4); - { - if (*(unsigned long *)loc != 0xFFFFFFFF) - { - puts(" - failed at "); - puthex(loc); - puts(": "); - puthex(*(unsigned long *)loc); - while (1); - } - } - puts("1"); - for (loc = 0; loc <= 0x400000; loc += 4); - { - *(unsigned long *)loc = loc; - } - for (loc = 0; loc <= 0x400000; loc += 4); - { - if (*(unsigned long *)loc != loc) - { - puts(" - failed at "); - puthex(loc); - puts(": "); - puthex(*(unsigned long *)loc); - while (1); - } - } - puts("?"); - for (loc = 0; loc <= 0x400000; loc += 4); - { - *(unsigned long *)loc = 0xDEADB00B; - } - for (loc = 0; loc <= 0x400000; loc += 4); - { - if (*(unsigned long *)loc != 0xDEADB00B) - { - puts(" - failed at "); - puthex(loc); - puts(": "); - puthex(*(unsigned long *)loc); - while (1); - } - } - puts(">"); - for (loc = 0; loc <= 0x400000; loc += 4); - { - *(unsigned long *)loc = 0x0; - } - for (loc = 0; loc <= 0x400000; loc += 4); - { - if (*(unsigned long *)loc != 0x0) - { - puts(" - failed at "); - puthex(loc); - puts(": "); - puthex(*(unsigned long *)loc); - while (1); - } - } - puts("\n"); -} - do_cksum(unsigned long loc) { unsigned int ptr, cksum; @@ -582,7 +495,6 @@ } return (errors == 0); } -#endif void puthex(unsigned long val) { diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/mk_type41.c linux/arch/ppc/boot/mk_type41.c --- v2.1.47/linux/arch/ppc/boot/mk_type41.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/mk_type41.c Wed Dec 31 16:00:00 1969 @@ -1,208 +0,0 @@ -/* - * This program will make a type 0x41 load image from an - * executable file. Note: assumes that the executable has - * already been "flattened" by 'mkboot'. - * - * usage: mk_type41 flat-file image - */ - -#include -#include -#ifdef linux -#include -#else -#include -#endif - -_LE(long val, unsigned char *le) -{ - le[0] = val; - le[1] = val >> 8; - le[2] = val >> 16; - le[3] = val >> 24; -} - -main(int argc, char *argv[]) -{ - int in_fd, out_fd, len, size; - struct stat info; - char buf[8192]; - struct hdr - { - unsigned long entry_point; - unsigned long image_length; - } hdr; - if (argc != 3) - { - fprintf(stderr, "usage: mk_type41 \n"); - exit(1); - } - if ((in_fd = open(argv[1], 0)) < 0) - { - fprintf(stderr, "Can't open input file: '%s': %s\n", argv[1], strerror(errno)); - exit(2); - } - if ((out_fd = creat(argv[2], 0666)) < 0) - { - fprintf(stderr, "Can't create output file: '%s': %s\n", argv[2], strerror(errno)); - exit(2); - } - if (fstat(in_fd, &info) < 0) - { - fprintf(stderr, "Can't get info on input file: %s\n", strerror(errno)); - exit(4); - } - write_prep_boot_partition(out_fd); - _LE(0x400, &hdr.entry_point); - _LE(info.st_size+0x400, &hdr.image_length); - lseek(out_fd, 0x200, 0); - if (write(out_fd, &hdr, sizeof(hdr)) != sizeof(hdr)) - { - fprintf(stderr, "Can't write output file: %s\n", strerror(errno)); - exit(5); - } - lseek(out_fd, 0x400, 0); - while ((len = read(in_fd, buf, sizeof(buf))) > 0) - { - if (write(out_fd, buf, len) != len) - { - fprintf(stderr, "Can't write output file: %s\n", strerror(errno)); - exit(5); - } - } - if (len < 0) - { - fprintf(stderr, "Can't read input file: %s\n", strerror(errno)); - exit(6); - } - close(in_fd); - close(out_fd); -} - -/* Adapted from IBM Naked Application Package (NAP) */ - -#define Align(value,boundary) \ - (((value) + (boundary) - 1) & ~((boundary) - 1)) - -#define HiByte(word) ((word_t)(word) >> 8) -#define LoByte(word) ((word_t)(word) & 0xFF) - -#define HiWord(dword) ((dword_t)(dword) >> 16) -#define LoWord(dword) ((dword_t)(dword) & 0xFFFF) - -/* - * Little-endian stuff - */ -#define LeWord(word) \ - (((word_t)(word) >> 8) | ((word_t)(word) << 8)) - -#define LeDword(dword) \ - (LeWord(LoWord(dword)) << 16) | LeWord(HiWord(dword)) - -#define PcDword(dword) \ - (LeWord(LoWord(dword)) << 16) | LeWord(HiWord(dword)) - - -typedef unsigned long dword_t; -typedef unsigned short word_t; -typedef unsigned char byte_t; -typedef byte_t block_t[512]; -typedef byte_t page_t[4096]; - -/* - * Partition table entry - * - from the PReP spec - */ -typedef struct partition_entry { - byte_t boot_indicator; - byte_t starting_head; - byte_t starting_sector; - byte_t starting_cylinder; - - byte_t system_indicator; - byte_t ending_head; - byte_t ending_sector; - byte_t ending_cylinder; - - dword_t beginning_sector; - dword_t number_of_sectors; -} partition_entry_t; - -#define BootActive 0x80 -#define SystemPrep 0x41 - - -/* - * Writes the "boot record", which contains the partition table, to the - * diskette, followed by the dummy PC boot block and load image descriptor - * block. It returns the number of bytes it has written to the load - * image. - * - * The boot record is the first block of the diskette and identifies the - * "PReP" partition. The "PReP" partition contains the "load image" starting - * at offset zero within the partition. The first block of the load image is - * a dummy PC boot block. The second block is the "load image descriptor" - * which contains the size of the load image and the entry point into the - * image. The actual boot image starts at offset 1024 bytes (third sector) - * in the partition. - */ -void -write_prep_boot_partition(int out_fd) -{ - block_t block; - partition_entry_t *pe = (partition_entry_t *)&block[0x1BE]; - dword_t *entry = (dword_t *)&block[0]; - dword_t *length = (dword_t *)&block[4]; - - bzero( &block, sizeof block ); - - /* - * Magic marker - */ - block[510] = 0x55; - block[511] = 0xAA; - - /* - * Build a "PReP" partition table entry in the boot record - * - "PReP" may only look at the system_indicator - */ - pe->boot_indicator = BootActive; - pe->system_indicator = SystemPrep; - - /* - * The first block of the diskette is used by this "boot record" which - * actually contains the partition table. (The first block of the - * partition contains the boot image, but I digress...) We'll set up - * one partition on the diskette and it shall contain the rest of the - * diskette. - */ - pe->starting_head = 0; /* zero-based */ - pe->starting_sector = 2; /* one-based */ - pe->starting_cylinder = 0; /* zero-based */ - - pe->ending_head = 1; /* assumes two heads */ - pe->ending_sector = 18; /* assumes 18 sectors/track */ - pe->ending_cylinder = 79; /* assumes 80 cylinders/diskette */ - - /* - * The "PReP" software ignores the above fields and just looks at - * the next two. - * - size of the diskette is (assumed to be) - * (2 tracks/cylinder)(18 sectors/tracks)(80 cylinders/diskette) - * - unlike the above sector numbers, the beginning sector is zero-based! - */ -#if 0 - pe->beginning_sector = LeDword(1); -#else - /* This has to be 0 on the PowerStack? */ - pe->beginning_sector = LeDword(0); -#endif - pe->number_of_sectors = LeDword(2*18*80-1); - - /* - * Write the partition table - */ - lseek( out_fd, 0, 0 ); - write( out_fd, block, sizeof block ); -} - diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/mkprep.c linux/arch/ppc/boot/mkprep.c --- v2.1.47/linux/arch/ppc/boot/mkprep.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/mkprep.c Thu Jul 31 13:09:17 1997 @@ -1,19 +1,54 @@ /* - * This program will make a type 0x41 load image from an - * executable file. Note: assumes that the executable has - * already been "flattened" by 'mkboot'. - * - * usage: mk_type41 flat-file image + * Makes a prep bootable image which can be dd'd onto + * a disk device to make a bootdisk. Will take + * as input a elf executable, strip off the header + * and write out a boot image as: + * 1) default - strips elf header + * suitable as a network boot image + * 2) -pbp - strips elf header and writes out prep boot partition image + * cat or dd onto disk for booting + * 3) -asm - strips elf header and writes out as asm data + * useful for generating data for a compressed image + * -- Cort */ -#include -#include #ifdef linux +#include #include +/*#include */ /* the byte swap funcs don't work here -- Cort */ #else +#include #include #endif -#include /* use same as kernel -- Cort */ + +#include +#include + +#define cpu_to_le32(x) le32_to_cpu((x)) +unsigned long le32_to_cpu(unsigned long x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + + +#define cpu_to_le16(x) le16_to_cpu((x)) +unsigned short le16_to_cpu(unsigned short x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} + +#define cpu_to_be32(x) (x) +#define be32_to_cpu(x) (x) +#define cpu_to_be16(x) (x) +#define be16_to_cpu(x) (x) + +/* size of read buffer */ +#define SIZE 0x1000 + typedef unsigned long dword_t; typedef unsigned short word_t; @@ -21,119 +56,239 @@ typedef byte_t block_t[512]; typedef byte_t page_t[4096]; -_LE(long val, unsigned char *le) -{ - le[0] = val; - le[1] = val >> 8; - le[2] = val >> 16; - le[3] = val >> 24; -} /* * Partition table entry * - from the PReP spec */ typedef struct partition_entry { - byte_t boot_indicator; - byte_t starting_head; - byte_t starting_sector; - byte_t starting_cylinder; - - byte_t system_indicator; - byte_t ending_head; - byte_t ending_sector; - byte_t ending_cylinder; + byte_t boot_indicator; + byte_t starting_head; + byte_t starting_sector; + byte_t starting_cylinder; + + byte_t system_indicator; + byte_t ending_head; + byte_t ending_sector; + byte_t ending_cylinder; - dword_t beginning_sector; - dword_t number_of_sectors; + dword_t beginning_sector; + dword_t number_of_sectors; } partition_entry_t; #define BootActive 0x80 #define SystemPrep 0x41 +void copy_image(int , int); +void write_prep_partition(int , int ); +void write_asm_data( int in, int out ); + +unsigned int elfhdr_size = 65536; int main(int argc, char *argv[]) { - struct stat info; - char buf[8192]; - int in_fd, out_fd,len; + int in_fd, out_fd; + int argptr = 1; + unsigned int prep = 0; + unsigned int asmoutput = 0; + + if ( (argc < 3) || (argc > 4) ) + { + fprintf(stderr, "usage: %s [-pbp] [-asm] \n",argv[0]); + exit(-1); + } + + /* needs to handle args more elegantly -- but this is a small/simple program */ + + /* check for -pbp */ + if ( !strcmp( argv[argptr], "-pbp" ) ) + { + prep = 1; + argptr++; + } + + /* check for -asm */ + if ( !strcmp( argv[argptr], "-asm" ) ) + { + asmoutput = 1; + argptr++; + } + + /* input file */ + if ( !strcmp( argv[argptr], "-" ) ) + in_fd = 0; /* stdin */ + else + if ((in_fd = open( argv[argptr] , 0)) < 0) + exit(-1); + argptr++; + + /* output file */ + if ( !strcmp( argv[argptr], "-" ) ) + out_fd = 1; /* stdout */ + else + if ((out_fd = creat( argv[argptr] , 0755)) < 0) + exit(-1); + argptr++; + + /* skip elf header in input file */ + lseek(in_fd, elfhdr_size, SEEK_SET); + + /* write prep partition if necessary */ + if ( prep ) + write_prep_partition( in_fd, out_fd ); + + /* write input image to bootimage */ + if ( asmoutput ) + write_asm_data( in_fd, out_fd ); + else + copy_image(in_fd, out_fd); + + return 0; +} + +void write_prep_partition(int in, int out) +{ unsigned char block[512]; partition_entry_t *pe = (partition_entry_t *)&block[0x1BE]; - dword_t *entry = (dword_t *)&block[0x50]; - dword_t *length = (dword_t *)&block[0x51]; + dword_t *entry = (dword_t *)&block[0]; + dword_t *length = (dword_t *)&block[sizeof(long)]; + struct stat info; - if (argc != 3) - { - fprintf(stderr, "usage: mk_type41 \n"); - exit(1); - } - if ((in_fd = open(argv[1], 0)) < 0) - { - exit(2); - } - if ((out_fd = creat(argv[2], 0755)) < 0) - { - exit(2); - } + if (fstat(in, &info) < 0) + { + fprintf(stderr,"info failed\n"); + exit(-1); + } + + bzero( block, sizeof block ); - bzero( &block, sizeof block ); - + + /* set entry point and boot image size */ + *entry = cpu_to_le32(0x400); + /* need use size - elfheader? */ + *length = cpu_to_le32(info.st_size+0x400); /* - * Magic marker + * Writes the "boot record", which contains the partition table, to the + * diskette, followed by the dummy PC boot block and load image descriptor + * block. It returns the number of bytes it has written to the load + * image. + * + * The boot record is the first block of the diskette and identifies the + * "PReP" partition. The "PReP" partition contains the "load image" starting + * at offset zero within the partition. The first block of the load image is + * a dummy PC boot block. The second block is the "load image descriptor" + * which contains the size of the load image and the entry point into the + * image. The actual boot image starts at offset 1024 bytes (third sector) + * in the partition. */ + + /* sets magic number for msdos partition (used by linux) */ block[510] = 0x55; block[511] = 0xAA; + /* + * Build a "PReP" partition table entry in the boot record + * - "PReP" may only look at the system_indicator + */ pe->boot_indicator = BootActive; pe->system_indicator = SystemPrep; - pe->starting_head = 0; /* zero-based */ - pe->starting_sector = 2; /* one-based */ - pe->starting_cylinder = 0; /* zero-based */ - - pe->ending_head = 1; /* assumes two heads */ - pe->ending_sector = 18; /* assumes 18 sectors/track */ - pe->ending_cylinder = 79; /* assumes 80 cylinders/diskette */ + /* + * The first block of the diskette is used by this "boot record" which + * actually contains the partition table. (The first block of the + * partition contains the boot image, but I digress...) We'll set up + * one partition on the diskette and it shall contain the rest of the + * diskette. + */ + pe->starting_head = 0; /* zero-based */ + pe->starting_sector = 2; /* one-based */ + pe->starting_cylinder = 0; /* zero-based */ + pe->ending_head = 1; /* assumes two heads */ + pe->ending_sector = 18; /* assumes 18 sectors/track */ + pe->ending_cylinder = 79; /* assumes 80 cylinders/diskette */ + /* + * The "PReP" software ignores the above fields and just looks at + * the next two. + * - size of the diskette is (assumed to be) + * (2 tracks/cylinder)(18 sectors/tracks)(80 cylinders/diskette) + * - unlike the above sector numbers, the beginning sector is zero-based! + */ #if 0 -#if 0 - pe->beginning_sector = LeDword(1); + pe->beginning_sector = cpu_to_le32(1); #else - /* This has to be 0 on the PowerStack? */ - pe->beginning_sector = LeDword(0); -#endif - pe->number_of_sectors = LeDword(2*18*80-1); -#endif + /* This has to be 0 on the PowerStack? */ + pe->beginning_sector = cpu_to_le32(0); +#endif +/*pe->number_of_sectors = cpu_to_le32(2*18*80-1);*/ + + write( out, block, sizeof(block) ); + write( out, entry, sizeof(*entry) ); + write( out, length, sizeof(*length) ); + /* set file position to 2nd sector where image will be written */ + lseek( out, 0x400, SEEK_SET ); +} + + + +void +copy_image(int in, int out) +{ + char buf[SIZE]; + int n; + + while ( (n = read(in, buf, SIZE)) > 0 ) + write(out, buf, n); +} + + +void +write_asm_data( int in, int out ) +{ + int i, cnt, pos, len; + unsigned int cksum, val; + unsigned char *lp; + unsigned char buf[SIZE]; + unsigned char str[256]; - if (fstat(in_fd, &info) < 0) + write( out, "\t.data\n\t.globl input_data\ninput_data:\n", + strlen( "\t.data\n\t.globl input_data\ninput_data:\n" ) ); + pos = 0; + cksum = 0; + while ((len = read(in, buf, sizeof(buf))) > 0) + { + cnt = 0; + lp = (unsigned char *)buf; + len = (len + 3) & ~3; /* Round up to longwords */ + for (i = 0; i < len; i += 4) { - exit(4); + if (cnt == 0) + { + write( out, "\t.long\t", strlen( "\t.long\t" ) ); + } + sprintf( str, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); + write( out, str, strlen(str) ); + val = *(unsigned long *)lp; + cksum ^= val; + lp += 4; + if (++cnt == 4) + { + cnt = 0; + sprintf( str, " # %x \n", pos+i-12); + write( out, str, strlen(str) ); + } else + { + write( out, ",", 1 ); + } } - /* begin execution at 0x400 */ - _LE(0x400,(unsigned char *)entry); - _LE(info.st_size+0x400,length); - - lseek( out_fd, 0, 0 ); - /* write out 1st block */ - write( out_fd, block, sizeof block ); - - /* copy image */ -#if 1 - lseek(out_fd, 0x400, 0); - while ((len = read(in_fd, buf, sizeof(buf))) > 0) - { - if (write(out_fd, buf, len) != len) - { - exit(5); - } - } - if (len < 0) - { - exit(6); - } - close(in_fd); - close(out_fd); -#endif - return 0; -} + if (cnt) + { + write( out, "0\n", 2 ); + } + pos += len; + } + sprintf(str, "\t.globl input_len\ninput_len:\t.long\t0x%x\n", pos); + write( out, str, strlen(str) ); + fprintf(stderr, "cksum = %x\n", cksum); +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/piggyback.c linux/arch/ppc/boot/piggyback.c --- v2.1.47/linux/arch/ppc/boot/piggyback.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/piggyback.c Wed Dec 31 16:00:00 1969 @@ -1,64 +0,0 @@ -#include - -extern long ce_exec_config[]; - -main(int argc, char *argv[]) -{ - int i, cnt, pos, len; - unsigned int cksum, val; - unsigned char *lp; - unsigned char buf[8192]; - if (argc != 1) - { - fprintf(stderr, "usage: %s out-file\n", argv[0]); - exit(1); - } - fprintf(stdout, "#\n"); - fprintf(stdout, "# Miscellaneous data structures:\n"); - fprintf(stdout, "# WARNING - this file is automatically generated!\n"); - fprintf(stdout, "#\n"); - fprintf(stdout, "\n"); - fprintf(stdout, "\t.data\n"); - fprintf(stdout, "\t.globl input_data\n"); - fprintf(stdout, "input_data:\n"); - pos = 0; - cksum = 0; - while ((len = read(0, buf, sizeof(buf))) > 0) - { - cnt = 0; - lp = (unsigned char *)buf; - len = (len + 3) & ~3; /* Round up to longwords */ - for (i = 0; i < len; i += 4) - { - if (cnt == 0) - { - fprintf(stdout, "\t.long\t"); - } - fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); - val = *(unsigned long *)lp; - cksum ^= val; - lp += 4; - if (++cnt == 4) - { - cnt = 0; - fprintf(stdout, " # %x \n", pos+i-12); - fflush(stdout); - } else - { - fprintf(stdout, ","); - } - } - if (cnt) - { - fprintf(stdout, "0\n"); - } - pos += len; - } - fprintf(stdout, "\t.globl input_len\n"); - fprintf(stdout, "input_len:\t.long\t0x%x\n", pos); - fflush(stdout); - fclose(stdout); - fprintf(stderr, "cksum = %x\n", cksum); - exit(0); -} - diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/unzip.c linux/arch/ppc/boot/unzip.c --- v2.1.47/linux/arch/ppc/boot/unzip.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/unzip.c Thu Jul 31 13:09:17 1997 @@ -17,7 +17,7 @@ */ #ifndef lint -static char rcsid[] = "$Id: unzip.c,v 0.9 1993/02/10 16:07:22 jloup Exp $"; +static char rcsid[] = "$Id: unzip.c,v 1.1.1.1 1997/02/25 05:18:11 cort Exp $"; #endif #include "gzip.h" diff -u --recursive --new-file v2.1.47/linux/arch/ppc/boot/vreset.c linux/arch/ppc/boot/vreset.c --- v2.1.47/linux/arch/ppc/boot/vreset.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/boot/vreset.c Thu Jul 31 13:09:17 1997 @@ -7,6 +7,864 @@ * Steve Sellgren * San Francisco Indigo Company * sfindigo!sellgren@uunet.uu.net + * + * Original concept by: + * Gary Thomas + * Adapted for Moto boxes by: + * Pat Kane & Mark Scott, 1996 + * Adapted for IBM portables by: + * Takeshi Ishimoto + * Multi-console support: + * Terje Malmedal + */ + +#include "iso_font.h" +#include + +extern char *vidmem; +extern int lines, cols; +/* estimate for delay */ +unsigned long loops_per_sec = 50000000;; +/* + * VGA Register + */ +struct VgaRegs +{ + unsigned short io_port; + unsigned char io_index; + unsigned char io_value; +}; + +/* + * Default console text mode registers used to reset + * graphics adapter. + */ +#define NREGS 54 +#define ENDMK 0xFFFF /* End marker */ + +#define S3Vendor 0x5333 +#define CirrusVendor 0x1013 +#define DiamondVendor 0x100E +#define MatroxVendor 0x102B +#define ParadiseVendor 0x101C + +struct VgaRegs GenVgaTextRegs[NREGS+1] = { +/* port index value */ + /* SR Regs */ + 0x3c4, 0x1, 0x0, + 0x3c4, 0x2, 0x3, + 0x3c4, 0x3, 0x0, + 0x3c4, 0x4, 0x2, + /* CR Regs */ + 0x3d4, 0x0, 0x5f, + 0x3d4, 0x1, 0x4f, + 0x3d4, 0x2, 0x50, + 0x3d4, 0x3, 0x82, + 0x3d4, 0x4, 0x55, + 0x3d4, 0x5, 0x81, + 0x3d4, 0x6, 0xbf, + 0x3d4, 0x7, 0x1f, + 0x3d4, 0x8, 0x00, + 0x3d4, 0x9, 0x4f, + 0x3d4, 0xa, 0x0d, + 0x3d4, 0xb, 0x0e, + 0x3d4, 0xc, 0x00, + 0x3d4, 0xd, 0x00, + 0x3d4, 0xe, 0x00, + 0x3d4, 0xf, 0x00, + 0x3d4, 0x10, 0x9c, + 0x3d4, 0x11, 0x8e, + 0x3d4, 0x12, 0x8f, + 0x3d4, 0x13, 0x28, + 0x3d4, 0x14, 0x1f, + 0x3d4, 0x15, 0x96, + 0x3d4, 0x16, 0xb9, + 0x3d4, 0x17, 0xa3, + /* GR Regs */ + 0x3ce, 0x0, 0x0, + 0x3ce, 0x1, 0x0, + 0x3ce, 0x2, 0x0, + 0x3ce, 0x3, 0x0, + 0x3ce, 0x4, 0x0, + 0x3ce, 0x5, 0x10, + 0x3ce, 0x6, 0xe, + 0x3ce, 0x7, 0x0, + 0x3ce, 0x8, 0xff, + ENDMK +}; + +struct VgaRegs S3TextRegs[NREGS+1] = { +/* port index value */ + /* SR Regs */ + 0x3c4, 0x1, 0x0, + 0x3c4, 0x2, 0x3, + 0x3c4, 0x3, 0x0, + 0x3c4, 0x4, 0x2, + /* CR Regs */ + 0x3d4, 0x0, 0x5f, + 0x3d4, 0x1, 0x4f, + 0x3d4, 0x2, 0x50, + 0x3d4, 0x3, 0x82, + 0x3d4, 0x4, 0x55, + 0x3d4, 0x5, 0x81, + 0x3d4, 0x6, 0xbf, + 0x3d4, 0x7, 0x1f, + 0x3d4, 0x8, 0x00, + 0x3d4, 0x9, 0x4f, + 0x3d4, 0xa, 0x0d, + 0x3d4, 0xb, 0x0e, + 0x3d4, 0xc, 0x00, + 0x3d4, 0xd, 0x00, + 0x3d4, 0xe, 0x00, + 0x3d4, 0xf, 0x00, + 0x3d4, 0x10, 0x9c, + 0x3d4, 0x11, 0x8e, + 0x3d4, 0x12, 0x8f, + 0x3d4, 0x13, 0x28, + 0x3d4, 0x14, 0x1f, + 0x3d4, 0x15, 0x96, + 0x3d4, 0x16, 0xb9, + 0x3d4, 0x17, 0xa3, + /* GR Regs */ + 0x3ce, 0x0, 0x0, + 0x3ce, 0x1, 0x0, + 0x3ce, 0x2, 0x0, + 0x3ce, 0x3, 0x0, + 0x3ce, 0x4, 0x0, + 0x3ce, 0x5, 0x10, + 0x3ce, 0x6, 0xe, + 0x3ce, 0x7, 0x0, + 0x3ce, 0x8, 0xff, + ENDMK +}; + +struct RGBColors +{ + unsigned char r, g, b; +}; + +/* + * Default console text mode color table. + * These values were obtained by booting Linux with + * text mode firmware & then dumping the registers. + */ +struct RGBColors TextCLUT[256] = +{ + /* red green blue */ + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2a, + 0x0, 0x2a, 0x0, + 0x0, 0x2a, 0x2a, + 0x2a, 0x0, 0x0, + 0x2a, 0x0, 0x2a, + 0x2a, 0x2a, 0x0, + 0x2a, 0x2a, 0x2a, + 0x0, 0x0, 0x15, + 0x0, 0x0, 0x3f, + 0x0, 0x2a, 0x15, + 0x0, 0x2a, 0x3f, + 0x2a, 0x0, 0x15, + 0x2a, 0x0, 0x3f, + 0x2a, 0x2a, 0x15, + 0x2a, 0x2a, 0x3f, + 0x0, 0x15, 0x0, + 0x0, 0x15, 0x2a, + 0x0, 0x3f, 0x0, + 0x0, 0x3f, 0x2a, + 0x2a, 0x15, 0x0, + 0x2a, 0x15, 0x2a, + 0x2a, 0x3f, 0x0, + 0x2a, 0x3f, 0x2a, + 0x0, 0x15, 0x15, + 0x0, 0x15, 0x3f, + 0x0, 0x3f, 0x15, + 0x0, 0x3f, 0x3f, + 0x2a, 0x15, 0x15, + 0x2a, 0x15, 0x3f, + 0x2a, 0x3f, 0x15, + 0x2a, 0x3f, 0x3f, + 0x15, 0x0, 0x0, + 0x15, 0x0, 0x2a, + 0x15, 0x2a, 0x0, + 0x15, 0x2a, 0x2a, + 0x3f, 0x0, 0x0, + 0x3f, 0x0, 0x2a, + 0x3f, 0x2a, 0x0, + 0x3f, 0x2a, 0x2a, + 0x15, 0x0, 0x15, + 0x15, 0x0, 0x3f, + 0x15, 0x2a, 0x15, + 0x15, 0x2a, 0x3f, + 0x3f, 0x0, 0x15, + 0x3f, 0x0, 0x3f, + 0x3f, 0x2a, 0x15, + 0x3f, 0x2a, 0x3f, + 0x15, 0x15, 0x0, + 0x15, 0x15, 0x2a, + 0x15, 0x3f, 0x0, + 0x15, 0x3f, 0x2a, + 0x3f, 0x15, 0x0, + 0x3f, 0x15, 0x2a, + 0x3f, 0x3f, 0x0, + 0x3f, 0x3f, 0x2a, + 0x15, 0x15, 0x15, + 0x15, 0x15, 0x3f, + 0x15, 0x3f, 0x15, + 0x15, 0x3f, 0x3f, + 0x3f, 0x15, 0x15, + 0x3f, 0x15, 0x3f, + 0x3f, 0x3f, 0x15, + 0x3f, 0x3f, 0x3f, + 0x39, 0xc, 0x5, + 0x15, 0x2c, 0xf, + 0x26, 0x10, 0x3d, + 0x29, 0x29, 0x38, + 0x4, 0x1a, 0xe, + 0x2, 0x1e, 0x3a, + 0x3c, 0x25, 0x33, + 0x3c, 0xc, 0x2c, + 0x3f, 0x3, 0x2b, + 0x1c, 0x9, 0x13, + 0x25, 0x2a, 0x35, + 0x1e, 0xa, 0x38, + 0x24, 0x8, 0x3, + 0x3, 0xe, 0x36, + 0xc, 0x6, 0x2a, + 0x26, 0x3, 0x32, + 0x5, 0x2f, 0x33, + 0x3c, 0x35, 0x2f, + 0x2d, 0x26, 0x3e, + 0xd, 0xa, 0x10, + 0x25, 0x3c, 0x11, + 0xd, 0x4, 0x2e, + 0x5, 0x19, 0x3e, + 0xc, 0x13, 0x34, + 0x2b, 0x6, 0x24, + 0x4, 0x3, 0xd, + 0x2f, 0x3c, 0xc, + 0x2a, 0x37, 0x1f, + 0xf, 0x12, 0x38, + 0x38, 0xe, 0x2a, + 0x12, 0x2f, 0x19, + 0x29, 0x2e, 0x31, + 0x25, 0x13, 0x3e, + 0x33, 0x3e, 0x33, + 0x1d, 0x2c, 0x25, + 0x15, 0x15, 0x5, + 0x32, 0x25, 0x39, + 0x1a, 0x7, 0x1f, + 0x13, 0xe, 0x1d, + 0x36, 0x17, 0x34, + 0xf, 0x15, 0x23, + 0x2, 0x35, 0xd, + 0x15, 0x3f, 0xc, + 0x14, 0x2f, 0xf, + 0x19, 0x21, 0x3e, + 0x27, 0x11, 0x2f, + 0x38, 0x3f, 0x3c, + 0x36, 0x2d, 0x15, + 0x16, 0x17, 0x2, + 0x1, 0xa, 0x3d, + 0x1b, 0x11, 0x3f, + 0x21, 0x3c, 0xd, + 0x1a, 0x39, 0x3d, + 0x8, 0xe, 0xe, + 0x22, 0x21, 0x23, + 0x1e, 0x30, 0x5, + 0x1f, 0x22, 0x3d, + 0x1e, 0x2f, 0xa, + 0x0, 0x1c, 0xe, + 0x0, 0x1c, 0x15, + 0x0, 0x1c, 0x1c, + 0x0, 0x15, 0x1c, + 0x0, 0xe, 0x1c, + 0x0, 0x7, 0x1c, + 0xe, 0xe, 0x1c, + 0x11, 0xe, 0x1c, + 0x15, 0xe, 0x1c, + 0x18, 0xe, 0x1c, + 0x1c, 0xe, 0x1c, + 0x1c, 0xe, 0x18, + 0x1c, 0xe, 0x15, + 0x1c, 0xe, 0x11, + 0x1c, 0xe, 0xe, + 0x1c, 0x11, 0xe, + 0x1c, 0x15, 0xe, + 0x1c, 0x18, 0xe, + 0x1c, 0x1c, 0xe, + 0x18, 0x1c, 0xe, + 0x15, 0x1c, 0xe, + 0x11, 0x1c, 0xe, + 0xe, 0x1c, 0xe, + 0xe, 0x1c, 0x11, + 0xe, 0x1c, 0x15, + 0xe, 0x1c, 0x18, + 0xe, 0x1c, 0x1c, + 0xe, 0x18, 0x1c, + 0xe, 0x15, 0x1c, + 0xe, 0x11, 0x1c, + 0x14, 0x14, 0x1c, + 0x16, 0x14, 0x1c, + 0x18, 0x14, 0x1c, + 0x1a, 0x14, 0x1c, + 0x1c, 0x14, 0x1c, + 0x1c, 0x14, 0x1a, + 0x1c, 0x14, 0x18, + 0x1c, 0x14, 0x16, + 0x1c, 0x14, 0x14, + 0x1c, 0x16, 0x14, + 0x1c, 0x18, 0x14, + 0x1c, 0x1a, 0x14, + 0x1c, 0x1c, 0x14, + 0x1a, 0x1c, 0x14, + 0x18, 0x1c, 0x14, + 0x16, 0x1c, 0x14, + 0x14, 0x1c, 0x14, + 0x14, 0x1c, 0x16, + 0x14, 0x1c, 0x18, + 0x14, 0x1c, 0x1a, + 0x14, 0x1c, 0x1c, + 0x14, 0x1a, 0x1c, + 0x14, 0x18, 0x1c, + 0x14, 0x16, 0x1c, + 0x0, 0x0, 0x10, + 0x4, 0x0, 0x10, + 0x8, 0x0, 0x10, + 0xc, 0x0, 0x10, + 0x10, 0x0, 0x10, + 0x10, 0x0, 0xc, + 0x10, 0x0, 0x8, + 0x10, 0x0, 0x4, + 0x10, 0x0, 0x0, + 0x10, 0x4, 0x0, + 0x10, 0x8, 0x0, + 0x10, 0xc, 0x0, + 0x10, 0x10, 0x0, + 0xc, 0x10, 0x0, + 0x8, 0x10, 0x0, + 0x4, 0x10, 0x0, + 0x0, 0x10, 0x0, + 0x0, 0x10, 0x4, + 0x0, 0x10, 0x8, + 0x0, 0x10, 0xc, + 0x0, 0x10, 0x10, + 0x0, 0xc, 0x10, + 0x0, 0x8, 0x10, + 0x0, 0x4, 0x10, + 0x8, 0x8, 0x10, + 0xa, 0x8, 0x10, + 0xc, 0x8, 0x10, + 0xe, 0x8, 0x10, + 0x10, 0x8, 0x10, + 0x10, 0x8, 0xe, + 0x10, 0x8, 0xc, + 0x10, 0x8, 0xa, + 0x10, 0x8, 0x8, + 0x10, 0xa, 0x8, + 0x10, 0xc, 0x8, + 0x10, 0xe, 0x8, + 0x10, 0x10, 0x8, + 0xe, 0x10, 0x8, + 0xc, 0x10, 0x8, + 0xa, 0x10, 0x8, + 0x8, 0x10, 0x8, + 0x8, 0x10, 0xa, + 0x8, 0x10, 0xc, + 0x8, 0x10, 0xe, + 0x8, 0x10, 0x10, + 0x8, 0xe, 0x10, + 0x8, 0xc, 0x10, + 0x8, 0xa, 0x10, + 0xb, 0xb, 0x10, + 0xc, 0xb, 0x10, + 0xd, 0xb, 0x10, + 0xf, 0xb, 0x10, + 0x10, 0xb, 0x10, + 0x10, 0xb, 0xf, + 0x10, 0xb, 0xd, + 0x10, 0xb, 0xc, + 0x10, 0xb, 0xb, + 0x10, 0xc, 0xb, + 0x10, 0xd, 0xb, + 0x10, 0xf, 0xb, + 0x10, 0x10, 0xb, + 0xf, 0x10, 0xb, + 0xd, 0x10, 0xb, + 0xc, 0x10, 0xb, + 0xb, 0x10, 0xb, + 0xb, 0x10, 0xc, + 0xb, 0x10, 0xd, + 0xb, 0x10, 0xf, + 0xb, 0x10, 0x10, + 0xb, 0xf, 0x10, + 0xb, 0xd, 0x10, + 0xb, 0xc, 0x10, + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0 +}; + +unsigned char AC[21] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x0C, 0x00, 0x0F, 0x08, 0x00}; + +static int scanPCI(int start_slt); +static int PCIVendor(int); +static void printslots(void); +int delayLoop(int); +extern void puthex(unsigned long); +extern void puts(const char *); +static void unlockS3(void); + +static inline +outw(int port, unsigned short val) +{ + outb(port, val >> 8); + outb(port+1, val); +} + +#define PPC_601 1 + +vga_init(unsigned char *ISA_mem) +{ + int slot; + struct VgaRegs *VgaTextRegs; + + if ((_get_PVR()>>16) == PPC_601) { + return(old_vga_init(ISA_mem)); + } + +#if 1 + /* See if VGA already in TEXT mode - exit if so! */ + outb(0x3CE, 0x06); + if ((inb(0x3CF) & 0x01) == 0){puts("VGA already in text mode\n"); return;} +#endif + + /* If no VGA responding in text mode, then we have some work to do... + */ + slot = -1; + while((slot = scanPCI(slot)) > -1) { /* find video card in use */ + unlockVideo(slot); /* enable I/O to card */ + VgaTextRegs = GenVgaTextRegs; + + switch (PCIVendor(slot)) { + default: + break; + case(S3Vendor): + unlockS3(); + VgaTextRegs = S3TextRegs; + break; + + case(CirrusVendor): + outw(0x3C4, 0x0612); /* unlock ext regs */ + outw(0x3C4, 0x0700); /* reset ext sequence mode */ + break; + + case(ParadiseVendor): /* IBM Portable 850 */ + outw(0x3ce, 0x0f05); /* unlock pardise registers */ + outw(0x3c4, 0x0648); + outw(0x3d4, 0x2985); + outw(0x3d4, 0x34a6); + outb(0x3ce, 0x0b); /* disable linear addressing */ + outb(0x3cf, inb(0x3cf) & ~0x30); + outw(0x3c4, 0x1400); + outb(0x3ce, 0x0e); /* disable 256 color mode */ + outb(0x3cf, inb(0x3cf) & ~0x01); + outb(0xd00, 0xff); /* enable auto-centering */ + if (!(inb(0xd01) & 0x03)) { + outb(0x3d4, 0x33); + outb(0x3d5, inb(0x3d5) & ~0x90); + outb(0x3d4, 0x32); + outb(0x3d5, inb(0x3d5) | 0x04); + outw(0x3d4, 0x0250); + outw(0x3d4, 0x07ba); + outw(0x3d4, 0x0900); + outw(0x3d4, 0x15e7); + outw(0x3d4, 0x2a95); + } + outw(0x3d4, 0x34a0); + break; + + #if 0 /* Untested - probably doesn't work */ + case(MatroxVendor): + case(DiamondVendor): + puts("VGA Chip Vendor ID: "); + puthex(PCIVendor(slot)); + puts("\n"); + delayLoop(1); + #endif + }; + + outw(0x3C4, 0x0120); /* disable video */ + setTextRegs(VgaTextRegs); /* initial register setup */ + setTextCLUT(0); /* load color lookup table */ + loadFont(ISA_mem); /* load font */ + setTextRegs(VgaTextRegs); /* reload registers */ + outw(0x3C4, 0x0100); /* re-enable video */ + clearVideoMemory(); + + if (PCIVendor(slot) == S3Vendor) { + outb(0x3c2, 0x63); /* MISC */ + } /* endif */ + + #ifdef DEBUG + printslots(); + delayLoop(5); + #endif + + delayLoop(1); /* give time for the video monitor to come up */ + } + return (1); /* 'CRT' I/O supported */ +} + +static int +NOP(int x) +{ +} + +/* + * Write to VGA Attribute registers. + */ +writeAttr(index, data, videoOn) + unsigned char index; + unsigned char data; + unsigned char videoOn; /* video on flag */ +{ + unsigned char v; + v = inb(0x3da); /* reset attr. address toggle */ + if (videoOn) + outb(0x3c0, (index & 0x1F) | 0x20); + else + outb(0x3c0, (index & 0x1F)); + outb(0x3c0, data); +} + +setTextRegs(struct VgaRegs *svp) +{ + int i; + + /* + * saved settings + */ + while( svp->io_port != ENDMK ) { + outb(svp->io_port, svp->io_index); + outb(svp->io_port+1, svp->io_value); + svp++; + } + + outb(0x3c2, 0x67); /* MISC */ + outb(0x3c6, 0xff); /* MASK */ + + for ( i = 0; i < 0x10; i++) + writeAttr(i, AC[i], 0); /* pallete */ + writeAttr(0x10, 0x0c, 0); /* text mode */ + writeAttr(0x11, 0x00, 0); /* overscan color (border) */ + writeAttr(0x12, 0x0f, 0); /* plane enable */ + writeAttr(0x13, 0x08, 0); /* pixel panning */ + writeAttr(0x14, 0x00, 1); /* color select; video on */ +} + +setTextCLUT(int shift) +{ + int i; + + outb(0x3C6, 0xFF); + i = inb(0x3C7); + outb(0x3C8, 0); + i = inb(0x3C7); + + for ( i = 0; i < 256; i++) { + outb(0x3C9, TextCLUT[i].r << shift); + outb(0x3C9, TextCLUT[i].g << shift); + outb(0x3C9, TextCLUT[i].b << shift); + } +} + + +loadFont(unsigned char *ISA_mem) +{ + int i, j; + unsigned char *font_page = (unsigned char *) &ISA_mem[0xA0000]; + + outb(0x3C2, 0x67); + /* + * Load font + */ + i = inb(0x3DA); /* Reset Attr toggle */ + + outb(0x3C0,0x30); + outb(0x3C0, 0x01); /* graphics mode */ + + outw(0x3C4, 0x0001); /* reset sequencer */ + outw(0x3C4, 0x0204); /* write to plane 2 */ + outw(0x3C4, 0x0406); /* enable plane graphics */ + outw(0x3C4, 0x0003); /* reset sequencer */ + outw(0x3CE, 0x0402); /* read plane 2 */ + outw(0x3CE, 0x0500); /* write mode 0, read mode 0 */ + outw(0x3CE, 0x0605); /* set graphics mode */ + + for (i = 0; i < sizeof(font); i += 16) { + for (j = 0; j < 16; j++) { + __asm__ volatile("eieio"); + font_page[(2*i)+j] = font[i+j]; + } + } +} + +static void +unlockS3(void) +{ + int s3_device_id; + outw(0x3d4, 0x3848); + outw(0x3d4, 0x39a5); + outb(0x3d4, 0x2d); + s3_device_id = inb(0x3d5) << 8; + outb(0x3d4, 0x2e); + s3_device_id |= inb(0x3d5); + + if (s3_device_id != 0x8812) { + /* From the S3 manual */ + outb(0x46E8, 0x10); /* Put into setup mode */ + outb(0x3C3, 0x10); + outb(0x102, 0x01); /* Enable registers */ + outb(0x46E8, 0x08); /* Enable video */ + outb(0x3C3, 0x08); + outb(0x4AE8, 0x00); + +#if 0 + outb(0x42E8, 0x80); /* Reset graphics engine? */ +#endif + + outb(0x3D4, 0x38); /* Unlock all registers */ + outb(0x3D5, 0x48); + outb(0x3D4, 0x39); + outb(0x3D5, 0xA5); + outb(0x3D4, 0x40); + outb(0x3D5, inb(0x3D5)|0x01); + outb(0x3D4, 0x33); + outb(0x3D5, inb(0x3D5)&~0x52); + outb(0x3D4, 0x35); + outb(0x3D5, inb(0x3D5)&~0x30); + outb(0x3D4, 0x3A); + outb(0x3D5, 0x00); + outb(0x3D4, 0x53); + outb(0x3D5, 0x00); + outb(0x3D4, 0x31); + outb(0x3D5, inb(0x3D5)&~0x4B); + outb(0x3D4, 0x58); + + outb(0x3D5, 0); + + outb(0x3D4, 0x54); + outb(0x3D5, 0x38); + outb(0x3D4, 0x60); + outb(0x3D5, 0x07); + outb(0x3D4, 0x61); + outb(0x3D5, 0x80); + outb(0x3D4, 0x62); + outb(0x3D5, 0xA1); + outb(0x3D4, 0x69); /* High order bits for cursor address */ + outb(0x3D5, 0); + + outb(0x3D4, 0x32); + outb(0x3D5, inb(0x3D5)&~0x10); + } else { + outw(0x3c4, 0x0806); /* IBM Portable 860 */ + outw(0x3c4, 0x1041); + outw(0x3c4, 0x1128); + outw(0x3d4, 0x4000); + outw(0x3d4, 0x3100); + outw(0x3d4, 0x3a05); + outw(0x3d4, 0x6688); + outw(0x3d4, 0x5800); /* disable linear addressing */ + outw(0x3d4, 0x4500); /* disable H/W cursor */ + outw(0x3c4, 0x5410); /* enable auto-centering */ + outw(0x3c4, 0x561f); + outw(0x3c4, 0x1b80); /* lock DCLK selection */ + outw(0x3d4, 0x3900); /* lock S3 registers */ + outw(0x3d4, 0x3800); + } /* endif */ +} + +/* + * cursor() sets an offset (0-1999) into the 80x25 text area + */ +void +cursor(int x, int y) +{ + int pos = (y*cols)+x; + outb(0x3D4, 14); + outb(0x3D5, pos >> 8); + outb(0x3D4, 15); + outb(0x3D5, pos); +} + +clearVideoMemory() +{ + int i, j; + for (i = 0; i < lines; i++) { + for (j = 0; j < cols; j++) { + vidmem[((i*cols)+j)*2] = 0x20; /* fill with space character */ + vidmem[((i*cols)+j)*2+1] = 0x07; /* set bg & fg attributes */ + } + } +} + +/* ============ */ + + +#define NSLOTS 8 +#define NPCIREGS 5 + + +/* + should use devfunc number/indirect method to be totally safe on + all machines, this works for now on 3 slot Moto boxes +*/ + +struct PCI_ConfigInfo { + unsigned long * config_addr; + unsigned long regs[NPCIREGS]; +} PCI_slots [NSLOTS] = { + + { (unsigned long *)0x80808000, 0xDEADBEEF }, /* onboard */ + { (unsigned long *)0x80800800, 0xDEADBEEF }, /* onboard */ + { (unsigned long *)0x80801000, 0xDEADBEEF }, /* onboard */ + { (unsigned long *)0x80802000, 0xDEADBEEF }, /* onboard */ + { (unsigned long *)0x80804000, 0xDEADBEEF }, /* onboard */ + { (unsigned long *)0x80810000, 0xDEADBEEF }, /* slot A/1 */ + { (unsigned long *)0x80820000, 0xDEADBEEF }, /* slot B/2 */ + { (unsigned long *)0x80840000, 0xDEADBEEF } /* slot C/3 */ +}; + + + +/* + * The following code modifies the PCI Command register + * to enable memory and I/O accesses. + */ +unlockVideo(slot) +{ + volatile unsigned char * ppci; + + ppci = (unsigned char * )PCI_slots[slot].config_addr; + ppci[4] = 0x0003; /* enable memory and I/O accesses */ + ppci[0x10] = 0x00000; /* turn off memory mapping */ + ppci[0x11] = 0x00000; /* mem_base = 0 */ + ppci[0x12] = 0x00000; + ppci[0x13] = 0x00000; + __asm__ volatile("eieio"); + + outb(0x3d4, 0x11); + outb(0x3d5, 0x0e); /* unlock CR0-CR7 */ +} + +long +SwapBytes(long lv) /* turn little endian into big indian long */ +{ + long t; + t = (lv&0x000000FF) << 24; + t |= (lv&0x0000FF00) << 8; + t |= (lv&0x00FF0000) >> 8; + t |= (lv&0xFF000000) >> 24; + return(t); +} + + +#define DEVID 0 +#define CMD 1 +#define CLASS 2 +#define MEMBASE 4 + +int +scanPCI(int start_slt) +{ + int slt, r; + struct PCI_ConfigInfo *pslot; + int theSlot = -1; + int highVgaSlot = 0; + + for ( slt = start_slt + 1; slt < NSLOTS; slt++) { + pslot = &PCI_slots[slt]; + for ( r = 0; r < NPCIREGS; r++) { + pslot->regs[r] = SwapBytes ( pslot->config_addr[r] ); + } + /* card in slot ? */ + if ( pslot->regs[DEVID] != 0xFFFFFFFF ) { + /* VGA ? */ + if ( ((pslot->regs[CLASS] & 0xFFFFFF00) == 0x03000000) || + ((pslot->regs[CLASS] & 0xFFFFFF00) == 0x00010000)) { + highVgaSlot = slt; + /* did firmware enable it ? */ + if ( (pslot->regs[CMD] & 0x03) ) { + theSlot = slt; + break; + } + } + } + } + + return ( theSlot ); +} + +/* Delay for a certain number of seconds */ +/* Note: They loop is used since 'udelay' can't handle really long counts! */ + +int +delayLoop(int k) +{ + int i; + while (k-- > 0) { + for (i = 0; i < 1000; i++) { + udelay(1000); + } + } +} + + +/* return Vendor ID of card in the slot */ +static +int PCIVendor(int slotnum) { + struct PCI_ConfigInfo *pslot; + + pslot = &PCI_slots[slotnum]; + +return (pslot->regs[DEVID] & 0xFFFF); +} + +static +void printslots(void) +{ + int i; + struct PCI_ConfigInfo *pslot; + for(i=0; i < NSLOTS; i++) { +#if 0 + pslot = &PCI_slots[i]; + printf("Slot: %d, Addr: %x, Vendor: %08x, Class: %08x\n", + i, pslot->config_addr, pslot->regs[0], pslot->regs[2]); +#else + puts("PCI Slot number: "); puthex(i); + puts(" Vendor ID: "); + puthex(PCIVendor(i)); puts("\n"); +#endif + + } +} + +/* + * OLD vreset.c + * + * Initialize the VGA control registers to 80x25 text mode. + * + * Adapted from a program by: + * Steve Sellgren + * San Francisco Indigo Company + * sfindigo!sellgren@uunet.uu.net */ unsigned char CRTC[24] = { @@ -15,13 +873,8 @@ 0x9C, 0xAE, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3}; unsigned char SEQ[5] = {0x3, 0x0, 0x3, 0x0, 0x2}; unsigned char GC[9] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0xE, 0x0, 0xFF}; -unsigned char AC[21] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, - 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, - 0x0C, 0x00, 0x0F, 0x08, 0x00}; - -#include "iso_font.h" +#if 0 static const unsigned char color_LUT[] = { 0x00, 0x00, 0x00, /* 0 - black */ @@ -41,124 +894,124 @@ 0x2A, 0x2A, 0x15, /* 14 - yellow */ 0x2A, 0x2A, 0x3F, /* 15 - bright white */ }; - -static inline -outw(int port, unsigned short val) -{ - outb(port, val >> 8); - outb(port+1, val); -} +#endif -vga_init(unsigned char *ISA_mem) +old_vga_init(unsigned char *ISA_mem) { - int i, j; - int value; - unsigned char *font_page = (unsigned char *) &ISA_mem[0xA0000]; - - /* See if VGA already in TEXT mode - exit if so! */ - outb(0x3CE, 0x06); - if ((inb(0x3CF) & 0x01) == 0) return; + int i, j; + int value; + unsigned char *font_page = (unsigned char *) &ISA_mem[0xA0000]; + + /* See if VGA already in TEXT mode - exit if so! */ + outb(0x3CE, 0x06); + if ((inb(0x3CF) & 0x01) == 0) return; - /* From the S3 manual */ - outb(0x46E8, 0x10); /* Put into setup mode */ - outb(0x3C3, 0x10); - outb(0x102, 0x01); /* Enable registers */ - outb(0x46E8, 0x08); /* Enable video */ - outb(0x3C3, 0x08); - outb(0x4AE8, 0x00); - - outb(0x42E8, 0x80); /* Reset graphics engine? */ - - outb(0x3D4, 0x38); /* Unlock all registers */ - outb(0x3D5, 0x48); - outb(0x3D4, 0x39); - outb(0x3D5, 0xA5); - outb(0x3D4, 0x40); - outb(0x3D5, inb(0x3D5)|0x01); - outb(0x3D4, 0x33); - outb(0x3D5, inb(0x3D5)&~0x52); - outb(0x3D4, 0x35); - outb(0x3D5, inb(0x3D5)&~0x30); - outb(0x3D4, 0x3A); - outb(0x3D5, 0x00); - outb(0x3D4, 0x53); - outb(0x3D5, 0x00); - outb(0x3D4, 0x31); - outb(0x3D5, inb(0x3D5)&~0x4B); - outb(0x3D4, 0x58); - outb(0x3D5, 0); - - outb(0x3D4, 0x54); - outb(0x3D5, 0x38); - outb(0x3D4, 0x60); - outb(0x3D5, 0x07); - outb(0x3D4, 0x61); - outb(0x3D5, 0x80); - outb(0x3D4, 0x62); - outb(0x3D5, 0xA1); - outb(0x3D4, 0x69); /* High order bits for cursor address */ - outb(0x3D5, 0); + /* From the S3 manual */ + outb(0x46E8, 0x10); /* Put into setup mode */ + outb(0x3C3, 0x10); + outb(0x102, 0x01); /* Enable registers */ + outb(0x46E8, 0x08); /* Enable video */ + outb(0x3C3, 0x08); + outb(0x4AE8, 0x00); + +#if 0 + outb(0x42E8, 0x80); /* Reset graphics engine? */ +#endif + + outb(0x3D4, 0x38); /* Unlock all registers */ + outb(0x3D5, 0x48); + outb(0x3D4, 0x39); + outb(0x3D5, 0xA5); + outb(0x3D4, 0x40); + outb(0x3D5, inb(0x3D5)|0x01); + outb(0x3D4, 0x33); + outb(0x3D5, inb(0x3D5)&~0x52); + outb(0x3D4, 0x35); + outb(0x3D5, inb(0x3D5)&~0x30); + outb(0x3D4, 0x3A); + outb(0x3D5, 0x00); + outb(0x3D4, 0x53); + outb(0x3D5, 0x00); + outb(0x3D4, 0x31); + outb(0x3D5, inb(0x3D5)&~0x4B); + outb(0x3D4, 0x58); + outb(0x3D5, 0); + + outb(0x3D4, 0x54); + outb(0x3D5, 0x38); + outb(0x3D4, 0x60); + outb(0x3D5, 0x07); + outb(0x3D4, 0x61); + outb(0x3D5, 0x80); + outb(0x3D4, 0x62); + outb(0x3D5, 0xA1); + outb(0x3D4, 0x69); /* High order bits for cursor address */ + outb(0x3D5, 0); - outb(0x3D4, 0x32); - outb(0x3D5, inb(0x3D5)&~0x10); + outb(0x3D4, 0x32); + outb(0x3D5, inb(0x3D5)&~0x10); - outb(0x3C2, 0x67); - - /* Initialize DAC */ - outb(0x3C6,0xFF); - inb(0x3C7); - outb(0x3C8,0x00); - inb(0x3C7); - for (i=0; i>ppc_defs.h + rm mk_defs.s + +checks: checks.c + $(HOSTCC) ${CFLAGS} -o checks checks.c + checks kernel.o: $(OBJS) $(LD) -r -o kernel.o $(OBJS) - sync - -dep: - $(CPP) -M *.c > .depend fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend + +dep: + $(CPP) -M *.S *.c > .depend -modules: +modules: dummy: diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/align.c linux/arch/ppc/kernel/align.c --- v2.1.47/linux/arch/ppc/kernel/align.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/align.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,290 @@ +/* + * align.c - handle alignment exceptions for the Power PC. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au). + */ +#include +#include +#include +#include +#include +#include + +struct aligninfo { + unsigned char len; + unsigned char flags; +}; + +#define INVALID { 0, 0 } + +#define LD 1 /* load */ +#define ST 2 /* store */ +#define SE 4 /* sign-extend value */ +#define F 8 /* to/from fp regs */ +#define U 0x10 /* update index register */ +#define M 0x20 /* multiple load/store */ +#define S 0x40 /* single-precision fp, or byte-swap value */ +#define HARD 0x80 /* string, stwcx. */ + +/* + * The PowerPC stores certain bits of the instruction that caused the + * alignment exception in the DSISR register. This array maps those + * bits to information about the operand length and what the + * instruction would do. + */ +static struct aligninfo aligninfo[128] = { + { 4, LD }, /* 00 0 0000: lwz / lwarx */ + INVALID, /* 00 0 0001 */ + { 4, ST }, /* 00 0 0010: stw */ + INVALID, /* 00 0 0011 */ + { 2, LD }, /* 00 0 0100: lhz */ + { 2, LD+SE }, /* 00 0 0101: lha */ + { 2, ST }, /* 00 0 0110: sth */ + { 4, LD+M }, /* 00 0 0111: lmw */ + { 4, LD+F+S }, /* 00 0 1000: lfs */ + { 8, LD+F }, /* 00 0 1001: lfd */ + { 4, ST+F+S }, /* 00 0 1010: stfs */ + { 8, ST+F }, /* 00 0 1011: stfd */ + INVALID, /* 00 0 1100 */ + INVALID, /* 00 0 1101 */ + INVALID, /* 00 0 1110 */ + INVALID, /* 00 0 1111 */ + { 4, LD+U }, /* 00 1 0000: lwzu */ + INVALID, /* 00 1 0001 */ + { 4, ST+U }, /* 00 1 0010: stwu */ + INVALID, /* 00 1 0011 */ + { 2, LD+U }, /* 00 1 0100: lhzu */ + { 2, LD+SE+U }, /* 00 1 0101: lhau */ + { 2, ST+U }, /* 00 1 0110: sthu */ + { 4, ST+M }, /* 00 1 0111: stmw */ + { 4, LD+F+S+U }, /* 00 1 1000: lfsu */ + { 8, LD+F+U }, /* 00 1 1001: lfdu */ + { 4, ST+F+S+U }, /* 00 1 1010: stfsu */ + { 8, ST+F+U }, /* 00 1 1011: stfdu */ + INVALID, /* 00 1 1100 */ + INVALID, /* 00 1 1101 */ + INVALID, /* 00 1 1110 */ + INVALID, /* 00 1 1111 */ + INVALID, /* 01 0 0000 */ + INVALID, /* 01 0 0001 */ + INVALID, /* 01 0 0010 */ + INVALID, /* 01 0 0011 */ + INVALID, /* 01 0 0100 */ + INVALID, /* 01 0 0101: lwax?? */ + INVALID, /* 01 0 0110 */ + INVALID, /* 01 0 0111 */ + { 0, LD+HARD }, /* 01 0 1000: lswx */ + { 0, LD+HARD }, /* 01 0 1001: lswi */ + { 0, ST+HARD }, /* 01 0 1010: stswx */ + { 0, ST+HARD }, /* 01 0 1011: stswi */ + INVALID, /* 01 0 1100 */ + INVALID, /* 01 0 1101 */ + INVALID, /* 01 0 1110 */ + INVALID, /* 01 0 1111 */ + INVALID, /* 01 1 0000 */ + INVALID, /* 01 1 0001 */ + INVALID, /* 01 1 0010 */ + INVALID, /* 01 1 0011 */ + INVALID, /* 01 1 0100 */ + INVALID, /* 01 1 0101: lwaux?? */ + INVALID, /* 01 1 0110 */ + INVALID, /* 01 1 0111 */ + INVALID, /* 01 1 1000 */ + INVALID, /* 01 1 1001 */ + INVALID, /* 01 1 1010 */ + INVALID, /* 01 1 1011 */ + INVALID, /* 01 1 1100 */ + INVALID, /* 01 1 1101 */ + INVALID, /* 01 1 1110 */ + INVALID, /* 01 1 1111 */ + INVALID, /* 10 0 0000 */ + INVALID, /* 10 0 0001 */ + { 0, ST+HARD }, /* 10 0 0010: stwcx. */ + INVALID, /* 10 0 0011 */ + INVALID, /* 10 0 0100 */ + INVALID, /* 10 0 0101 */ + INVALID, /* 10 0 0110 */ + INVALID, /* 10 0 0111 */ + { 4, LD+S }, /* 10 0 1000: lwbrx */ + INVALID, /* 10 0 1001 */ + { 4, ST+S }, /* 10 0 1010: stwbrx */ + INVALID, /* 10 0 1011 */ + { 2, LD+S }, /* 10 0 1100: lhbrx */ + INVALID, /* 10 0 1101 */ + { 2, ST+S }, /* 10 0 1110: sthbrx */ + INVALID, /* 10 0 1111 */ + INVALID, /* 10 1 0000 */ + INVALID, /* 10 1 0001 */ + INVALID, /* 10 1 0010 */ + INVALID, /* 10 1 0011 */ + INVALID, /* 10 1 0100 */ + INVALID, /* 10 1 0101 */ + INVALID, /* 10 1 0110 */ + INVALID, /* 10 1 0111 */ + INVALID, /* 10 1 1000 */ + INVALID, /* 10 1 1001 */ + INVALID, /* 10 1 1010 */ + INVALID, /* 10 1 1011 */ + INVALID, /* 10 1 1100 */ + INVALID, /* 10 1 1101 */ + INVALID, /* 10 1 1110 */ + { 0, ST+HARD }, /* 10 1 1111: dcbz */ + { 4, LD }, /* 11 0 0000: lwzx */ + INVALID, /* 11 0 0001 */ + { 4, ST }, /* 11 0 0010: stwx */ + INVALID, /* 11 0 0011 */ + { 2, LD }, /* 11 0 0100: lhzx */ + { 2, LD+SE }, /* 11 0 0101: lhax */ + { 2, ST }, /* 11 0 0110: sthx */ + INVALID, /* 11 0 0111 */ + { 4, LD+F+S }, /* 11 0 1000: lfsx */ + { 8, LD+F }, /* 11 0 1001: lfdx */ + { 4, ST+F+S }, /* 11 0 1010: stfsx */ + { 8, ST+F }, /* 11 0 1011: stfdx */ + INVALID, /* 11 0 1100 */ + INVALID, /* 11 0 1101 */ + INVALID, /* 11 0 1110 */ + INVALID, /* 11 0 1111 */ + { 4, LD+U }, /* 11 1 0000: lwzux */ + INVALID, /* 11 1 0001 */ + { 4, ST+U }, /* 11 1 0010: stwux */ + INVALID, /* 11 1 0011 */ + { 2, LD+U }, /* 11 1 0100: lhzux */ + { 2, LD+SE+U }, /* 11 1 0101: lhaux */ + { 2, ST+U }, /* 11 1 0110: sthux */ + INVALID, /* 11 1 0111 */ + { 4, LD+F+S+U }, /* 11 1 1000: lfsux */ + { 8, LD+F+U }, /* 11 1 1001: lfdux */ + { 4, ST+F+S+U }, /* 11 1 1010: stfsux */ + { 8, ST+F+U }, /* 11 1 1011: stfdux */ + INVALID, /* 11 1 1100 */ + INVALID, /* 11 1 1101 */ + INVALID, /* 11 1 1110 */ + INVALID, /* 11 1 1111 */ +}; + +#define SWAP(a, b) (t = (a), (a) = (b), (b) = t) + +int +fix_alignment(struct pt_regs *regs) +{ + int instr, nb, flags; + int i, t; + int reg, areg; + unsigned char *addr; + union { + long l; + float f; + double d; + unsigned char v[8]; + } data; + + instr = (regs->dsisr >> 10) & 0x7f; + nb = aligninfo[instr].len; + if (nb == 0) + return 0; /* too hard or invalid instruction bits */ + flags = aligninfo[instr].flags; + addr = (unsigned char *) regs->dar; + reg = (regs->dsisr >> 5) & 0x1f; /* source/dest register */ + + /* Verify the address of the operand */ + if (user_mode(regs)) { + if (verify_area((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb)) + return -EFAULT; /* bad address */ + } + + if ((flags & F) && last_task_used_math == current) + giveup_fpu(); + + if (flags & M) + return 0; /* too hard for now */ + + /* If we read the operand, copy it in */ + if (flags & LD) { + if (nb == 2) { + data.v[0] = data.v[1] = 0; + if (__get_user(data.v[2], addr) + || __get_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__get_user(data.v[i], addr+i)) + return -EFAULT; + } + } + + switch (flags & ~U) { + case LD+SE: + if (data.v[2] >= 0x80) + data.v[0] = data.v[1] = -1; + /* fall through */ + case LD: + regs->gpr[reg] = data.l; + break; + case LD+S: + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + regs->gpr[reg] = data.l; + break; + case ST: + data.l = regs->gpr[reg]; + break; + case ST+S: + data.l = regs->gpr[reg]; + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + break; + case LD+F: + current->tss.fpr[reg] = data.d; + break; + case ST+F: + data.d = current->tss.fpr[reg]; + break; + /* these require some floating point conversions... */ + /* note that giveup_fpu enables the FPU for the kernel */ + /* we'd like to use the assignment, but we have to compile + * the kernel with -msoft-float so it doesn't use the + * fp regs for copying 8-byte objects. */ + case LD+F+S: + giveup_fpu(); + cvt_fd(&data.f, ¤t->tss.fpr[reg]); + /* current->tss.fpr[reg] = data.f; */ + break; + case ST+F+S: + giveup_fpu(); + cvt_df(¤t->tss.fpr[reg], &data.f); + /* data.f = current->tss.fpr[reg]; */ + break; + default: + printk("align: can't handle flags=%x\n", flags); + return 0; + } + + if (flags & ST) { + if (nb == 2) { + if (__put_user(data.v[2], addr) + || __put_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__put_user(data.v[i], addr+i)) + return -EFAULT; + } + } + + if (flags & U) { + areg = regs->dsisr & 0x1f; /* register to update */ + regs->gpr[areg] = regs->dar; + } + + return 1; +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/bitops.c linux/arch/ppc/kernel/bitops.c --- v2.1.47/linux/arch/ppc/kernel/bitops.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/bitops.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,201 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ + +#include +#include + +/* + * I left these here since the problems with "cc" make it difficult to keep + * them in bitops.h -- Cort + */ +void set_bit(int nr, volatile void *addr) +{ + unsigned int t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "set_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 + or %0,%0,%1 + stwcx. %0,0,%2 + bne 1b" + : "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); +} + +void clear_bit(int nr, volatile void *addr) +{ + unsigned int t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "clear_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 + andc %0,%0,%1 + stwcx. %0,0,%2 + bne 1b" + : "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); +} + +void change_bit(int nr, volatile void *addr) +{ + unsigned int t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "change_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 + xor %0,%0,%1 + stwcx. %0,0,%2 + bne 1b" + : "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); +} + +int test_and_set_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "test_and_set_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%3 + or %1,%0,%2 + stwcx. %1,0,%3 + bne 1b" + : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); + + return (old & mask) != 0; +} + +int test_and_clear_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "test_and_clear_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%3 + andc %1,%0,%2 + stwcx. %1,0,%3 + bne 1b" + : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); + + return (old & mask) != 0; +} + +int test_and_change_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "test_and_change_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%3 + xor %1,%0,%2 + stwcx. %1,0,%3 + bne 1b" + : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); + + return (old & mask) != 0; +} + +/* I put it in bitops.h -- Cort */ +#if 0 +int ffz(unsigned int x) +{ + int n; + + x = ~x & (x+1); /* set LS zero to 1, other bits to 0 */ + __asm__ ("cntlzw %0,%1" : "=r" (n) : "r" (x)); + return 31 - n; +} + +/* + * This implementation of find_{first,next}_zero_bit was stolen from + * Linus' asm-alpha/bitops.h. + */ + +int find_first_zero_bit(void * addr, int size) +{ + unsigned int * p = ((unsigned int *) addr); + unsigned int result = 0; + unsigned int tmp; + + if (size == 0) + return 0; + while (size & ~31UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; + tmp |= ~0UL << size; +found_middle: + return result + ffz(tmp); +} + +/* + * Find next zero bit in a bitmap reasonably efficiently.. + */ +int find_next_zero_bit(void * addr, int size, int offset) +{ + unsigned int * p = ((unsigned int *) addr) + (offset >> 5); + unsigned int result = offset & ~31UL; + unsigned int tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (32-offset); + if (size < 32) + goto found_first; + if (~tmp) + goto found_middle; + size -= 32; + result += 32; + } + while (size & ~31UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; +found_first: + tmp |= ~0UL << size; +found_middle: + return result + ffz(tmp); +} +#endif diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/checks.c linux/arch/ppc/kernel/checks.c --- v2.1.47/linux/arch/ppc/kernel/checks.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/checks.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Do various before compile checks of data structures + * -- Cort + */ +int main(void) +{ + int ret = 0; + + if ( sizeof(struct thread_struct) % 16 ) + { + printf("Thread struct is not modulo 16 bytes: " + "%d bytes total, %d bytes off\n", + sizeof(struct thread_struct), + sizeof(struct thread_struct)%16); + ret = -1; + } + + if ( sizeof(struct pt_regs) % 16 ) + { + printf("pt_regs struct is not modulo 16 bytes: " + "%d bytes total, %d bytes off\n", + sizeof(struct pt_regs), + sizeof(struct pt_regs)%16); + ret = -1; + + } + + printf("Task size : %d bytes\n" + "Tss size : %d bytes\n" + "pt_regs size : %d bytes\n" + "Kernel stack size: %d bytes\n", + sizeof(struct task_struct), sizeof(struct thread_struct), + sizeof(struct pt_regs), + sizeof(union task_union) - sizeof(struct task_struct)); + return ret; +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S --- v2.1.47/linux/arch/ppc/kernel/head.S Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/kernel/head.S Thu Jul 31 13:09:17 1997 @@ -1,1040 +1,1144 @@ +/* + * arch/ppc/kernel/head.S + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * + * This file contains the low-level support and setup for the + * PowerPC platform, including trap and interrupt dispatch. + * Also included here is low-level thread/task switch support. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + #include "ppc_asm.tmpl" #include "ppc_defs.h" -#include +#include +#include +#include #include -#include - -#define NEWMM 1 -#define SYNC() \ - isync; \ - sync - -#define STATS -/* - * Increment a [64 bit] statistic counter - * Uses R2, R3 - */ -#define BUMP(ctr) \ - lis r2,ctr@h; \ - ori r2,r2,ctr@l; \ - lwz r3,4(r2); \ - addic r3,r3,1; \ - stw r3,4(r2); \ - lwz r3,0(r2); \ - addze r3,r3; \ - stw r3,0(r2) - -/* The same as 'BUMP' but running unmapped (TLB code) */ -#define BUMP_UNMAPPED(ctr) \ - mfspr r0,XER; \ - lis r2,ctr@h; \ - ori r2,r2,ctr@l; \ - lis r3,0xF000; \ - andc r2,r2,r3; \ - lwz r3,4(r2); \ - addic r3,r3,1; \ - stw r3,4(r2); \ - lwz r3,0(r2); \ - addze r3,r3; \ - mtspr XER,r0; \ - stw r3,0(r2) - -#define DO_RFI_TRACE_UNMAPPED(mark) -#define DO_RFI_TRACE_MAPPED(mark) - -#define DEFAULT_TRAP(offset) \ - li r13,0; \ - ori r13,r13,HID0_ICE; \ - mtspr HID0,r13; \ - lis r13,0xFFF00000>>16; \ - ori r13,r13,offset; \ - mtlr r13; \ - blr -#define TRACE_TRAP(offset) +#include +#include -#define DATA_CACHE_OFF() \ - mfspr r2,HID0; \ - li r3,0; \ - ori r3,r3,HID0_DCE; \ - andc r2,r2,r3; \ - mtspr HID0,r2; - -#define DATA_CACHE_ON() \ - mfspr r2,HID0; \ - ori r2,r2,HID0_DCE; \ - mtspr HID0,r2; +#define SYNC() \ + sync; \ + isync -/* This instruction is not implemented on the PPC 603 */ +/* This instruction is not implemented on the PPC 603 or 601 */ #define tlbia \ - li r4,64; \ - mtspr CTR,r4; \ - lis r4,0x9000; \ + li r4,128; \ + mtctr r4; \ + lis r4,0xC000; \ 0: tlbie r4; \ addi r4,r4,0x1000; \ bdnz 0b -/* Validate kernel stack - check for overflow */ -/* all regs are considered scratch since the C function will stomp them */ -#define CHECK_STACK() \ - /*lis r3,current_set@ha; \ - lwz r3,current_set@l(r3); \ - bl _EXTERN(check_stack)*/ -#if 0 -#define _CHECK_STACK() \ - mtspr SPR0,r3; \ - mtspr SPR1,r4; /* use r3,4 as scratch */ \ - lis r2,current_set@ha; \ - lwz r2,current_set@l(r2); \ - lwz r2,KERNEL_STACK_PAGE(r2); \ - /* if kernel stack is sys_stack skip check */ \ - /*lis r3,sys_stack@h; \ - ori r3,r3,sys_stack@l; \ - cmpl 0,r1,r3;*/ \ - /* check for STACK_MAGIC on kernel stack page */ \ - lis r3, 0xdead; /* STACK_MAGIC */ \ - ori r3,r3,0xbeef; \ - lwz r4,0(r2); /* get *kernel_stack_page */ \ - cmpl 0,r4,r3; \ - bne 01f; \ - /* check that ksp is > kernel page */ \ - /*li r3,0x0FFF; \ - andc r2,r2,r3; \ - andc r3,r1,r3; \ - cmp 0,r3,r2; \ - beq 02f;*/ \ - /* check that ksp and kernel stack page are on same page */ \ - cmp 0,r1,r2; \ - bge 02f; \ -01: mr r6,r1; /* setup info for call to bad_stack() */ \ - mr r5,r2; \ - bl _EXTERN(bad_stack); \ -02: mfspr r4,SPR1; \ - mfspr r3,SPR0 -#endif +#define TOPHYS(x) (x - KERNELBASE) + + +/* this is a very kludgey way of loading up the BATs on the + prep system. I'll kill this horrible macro and write + something clean when I have a chance -- Cort + */ +#define LOAD_BATS(RA,RB) \ + mfspr RA,PVR ; \ + srwi r5,r5,16 ; \ + cmpi 0,RA,1 ; \ + beq 199f ; \ + /* load bats for 60x */ ; \ + lis RA,BAT0@h ; \ + ori RA,RA,BAT0@l ; \ + addis RA,RA,-KERNELBASE@h;\ + lwz RB,0(RA) ; \ + mtspr IBAT0U,RB ; \ + mtspr DBAT0U,RB ; \ + lwz RB,4(RA) ; \ + mtspr IBAT0L,RB ; \ + mtspr DBAT0L,RB ; \ + lis RA,BAT1@h ; \ + ori RA,RA,BAT1@l ; \ + addis RA,RA,-KERNELBASE@h;\ + lwz RB,0(RA) ; \ + mtspr IBAT1U,RB ; \ + mtspr DBAT1U,RB ; \ + lwz RB,4(RA) ; \ + mtspr IBAT1L,RB ; \ + mtspr DBAT1L,RB ; \ + lis RA,BAT2@h ; \ + ori RA,RA,BAT2@l ; \ + addis RA,RA,-KERNELBASE@h;\ + lwz RB,0(RA) ; \ + mtspr IBAT2U,RB ; \ + mtspr DBAT2U,RB ; \ + lwz RB,4(RA) ; \ + mtspr IBAT2L,RB ; \ + mtspr DBAT2L,RB ; \ + lis RA,BAT3@h ; \ + ori RA,RA,BAT3@l ; \ + addis RA,RA,-KERNELBASE@h;\ + lwz RB,0(RA) ; \ + mtspr IBAT3U,RB ; \ + mtspr DBAT3U,RB ; \ + lwz RB,4(RA) ; \ + mtspr IBAT3L,RB ; \ + mtspr DBAT3L,RB ; \ + b 200f ; \ +199: /*load bats for 601 */ ; \ + lis RA,BAT0_601@h ; \ + ori RA,RA,BAT0_601@l; \ + addis RA,RA,-KERNELBASE@h;\ + lwz RB,0(RA) ; \ + mtspr IBAT0U,RB ; \ + mtspr DBAT0U,RB ; \ + lwz RB,4(RA) ; \ + mtspr IBAT0L,RB ; \ + mtspr DBAT0L,RB ; \ + lis RA,BAT1_601@h ; \ + ori RA,RA,BAT1_601@l; \ + addis RA,RA,-KERNELBASE@h;\ + lwz RB,0(RA) ; \ + mtspr IBAT1U,RB ; \ + mtspr DBAT1U,RB ; \ + lwz RB,4(RA) ; \ + mtspr IBAT1L,RB ; \ + mtspr DBAT1L,RB ; \ + lis RA,BAT2_601@h ; \ + ori RA,RA,BAT2_601@l; \ + addis RA,RA,-KERNELBASE@h;\ + lwz RB,0(RA) ; \ + mtspr IBAT2U,RB ; \ + mtspr DBAT2U,RB ; \ + lwz RB,4(RA) ; \ + mtspr IBAT2L,RB ; \ + mtspr DBAT2L,RB ; \ + lis RA,BAT3_601@h ; \ + ori RA,RA,BAT3_601@l; \ + addis RA,RA,-KERNELBASE@h;\ + lwz RB,0(RA) ; \ + mtspr IBAT3U,RB ; \ + mtspr DBAT3U,RB ; \ + lwz RB,4(RA) ; \ + mtspr IBAT3L,RB ; \ + mtspr DBAT3L,RB ; \ +200: -/* save fp regs if fp is used */ -/* assumes that r1 contains ptr to regs of task and r2 is scratch - -- Cort */ -#define SAVE_FP_REGS() \ - /* check if fp has been used by checking msr_fp bit */ \ - lwz r2,_MSR(r1); \ - andi. r2,r2,MSR_FP; \ - bne 00f; \ - /* floating point has been used -- save fp regs */ \ - lis r2,current_set@h; \ - ori r2,r2,current_set@l; \ - addi r2,r2,TSS; \ - /*mr r2,r1;*/ \ - stfd fr0,TSS_FPR0(r2); \ - stfd fr1,TSS_FPR1(r2); \ - stfd fr2,TSS_FPR2(r2); \ - stfd fr3,TSS_FPR3(r2); \ - stfd fr4,TSS_FPR4(r2); \ - stfd fr5,TSS_FPR5(r2); \ - stfd fr6,TSS_FPR6(r2); \ - stfd fr7,TSS_FPR7(r2); \ - stfd fr8,TSS_FPR8(r2); \ - stfd fr9,TSS_FPR9(r2); \ - stfd fr10,TSS_FPR10(r2); \ - stfd fr11,TSS_FPR11(r2); \ - stfd fr12,TSS_FPR12(r2); \ - stfd fr13,TSS_FPR13(r2); \ - stfd fr14,TSS_FPR14(r2); \ - stfd fr15,TSS_FPR15(r2); \ - stfd fr16,TSS_FPR16(r2); \ - stfd fr17,TSS_FPR17(r2); \ - stfd fr18,TSS_FPR18(r2); \ - stfd fr19,TSS_FPR19(r2); \ - stfd fr20,TSS_FPR20(r2); \ - stfd fr21,TSS_FPR21(r2); \ - stfd fr22,TSS_FPR22(r2); \ - stfd fr23,TSS_FPR23(r2); \ - stfd fr24,TSS_FPR24(r2); \ - stfd fr25,TSS_FPR25(r2); \ - stfd fr26,TSS_FPR26(r2); \ - stfd fr27,TSS_FPR27(r2); \ - stfd fr28,TSS_FPR28(r2); \ - stfd fr29,TSS_FPR29(r2); \ - stfd fr30,TSS_FPR30(r2); \ - stfd fr31,TSS_FPR31(r2); \ -00: - - -/* restores fp regs if fp has been used -- always restores fpscr */ -/* assumes that r1 contains ptr to regs, r2 is scratch and srr1 holds - what will become the msr when this process executes -- Cort*/ -#define RESTORE_FP_REGS(mark) \ - /* check if restoring from _switch() */ \ - li r2, mark; \ - cmpi 0,r2,0x0f0f; \ - bne 00f; /* only need to save if called from _switch() with 0x0f0f */\ - /* check if fp has been used by checking msr_fp bit */ \ - /* srr1 contains msr */ \ - mfspr r2,SRR1; \ - andi. r2,r2,MSR_FP; \ - bne 00f; \ - /* floating point has been used -- restore fp regs */ \ - /* Hey, Rocky! Watch me pull fp regs from my stack! */ \ - lis r2,current_set@h; \ - ori r2,r2,current_set@l; \ - addi r2,r2,TSS; \ - /*mr r2,r1;*/\ - lfd fr0,TSS_FPR0(r2); \ - lfd fr1,TSS_FPR1(r2); \ - lfd fr2,TSS_FPR2(r2); \ - lfd fr3,TSS_FPR3(r2); \ - lfd fr4,TSS_FPR4(r2); \ - lfd fr5,TSS_FPR5(r2); \ - lfd fr6,TSS_FPR6(r2); \ - lfd fr7,TSS_FPR7(r2); \ - lfd fr8,TSS_FPR8(r2); \ - lfd fr9,TSS_FPR9(r2); \ - lfd fr10,TSS_FPR10(r2); \ - lfd fr11,TSS_FPR11(r2); \ - lfd fr12,TSS_FPR12(r2); \ - lfd fr13,TSS_FPR13(r2); \ - lfd fr14,TSS_FPR14(r2); \ - lfd fr15,TSS_FPR15(r2); \ - lfd fr16,TSS_FPR16(r2); \ - lfd fr17,TSS_FPR17(r2); \ - lfd fr18,TSS_FPR18(r2); \ - lfd fr19,TSS_FPR19(r2); \ - lfd fr20,TSS_FPR20(r2); \ - lfd fr21,TSS_FPR21(r2); \ - lfd fr22,TSS_FPR22(r2); \ - lfd fr23,TSS_FPR23(r2); \ - lfd fr24,TSS_FPR24(r2); \ - lfd fr25,TSS_FPR25(r2); \ - lfd fr26,TSS_FPR26(r2); \ - lfd fr27,TSS_FPR27(r2); \ - lfd fr28,TSS_FPR28(r2); \ - lfd fr29,TSS_FPR29(r2); \ - lfd fr30,TSS_FPR30(r2); \ - lfd fr31,TSS_FPR31(r2); \ -00: - -/* save all registers */ -#define SAVE_ALL_REGS(mark) \ - subi r1,r1,INT_FRAME_SIZE; /* Make room for frame */ \ - stmw r3,GPR3(r1); /* Save R3..R31 */ \ - stw r3,ORIG_GPR3(r1); \ - stw r0,GPR0(r1); \ - mfspr r2,SPR0; \ - stw r2,GPR1(r1); \ - mfspr r2,SPR1; \ - stw r2,GPR2(r1); \ - mfspr r2,SPR2; \ - stw r2,_NIP(r1); \ - mfspr r2,SPR3; \ - stw r2,_MSR(r1); \ - mfctr r2; \ - stw r2,_CTR(r1); \ - mflr r2; \ - stw r2,_LINK(r1); \ - mfcr r2; \ - stw r2,_CCR(r1); \ - mfspr r2,XER; \ - stw r2,_XER(r1); \ - mffs fr0; \ - stfd fr0,FPCSR(r1); \ - lis r2,_break_lwarx@h; \ - ori r2,r2,_break_lwarx@l; \ - stwcx. r2,0,r2; \ - li r2,mark; \ - stw r2,TRAP(r1); \ - lis r2,0xDEAD; \ - ori r2,r2,0xDEAD; \ - stw r2,MARKER(r1); \ - li r2,0; \ - stw r2,RESULT(r1) - - -/* save registers clobbered by a page fault handler */ -#define SAVE_PAGE_FAULT_REGS(offset) \ - mfspr r2,DAR; \ - stw r2,_DAR(r1); \ - mfspr r2,DSISR; \ - stw r2,_DSISR(r1); \ - mfspr r2,PVR; /* Check for 603/603e */ \ - srwi r2,r2,16; \ - cmpi 0,r2,3; /* 603 */ \ - beq 22f; \ - cmpi 0,r2,6; /* 603e */ \ - bne 24f; \ -22: mfspr r2,HASH1; /* Note: these registers exist only on 603 */ \ - stw r2,_HASH1(r1); \ - mfspr r2,HASH2; \ - stw r2,_HASH2(r1); \ - mfspr r2,IMISS; \ - stw r2,_IMISS(r1); \ - mfspr r2,DMISS; \ - stw r2,_DMISS(r1); \ - mfspr r2,ICMP; \ - stw r2,_ICMP(r1); \ - mfspr r2,DCMP; \ - stw r2,_DCMP(r1); \ -24: - -#define SAVE_INT_REGS(mark) \ - mtspr SPR0,r1; /* Save current stack pointer */ \ - mtspr SPR1,r2; /* Scratch */ \ - mfcr r2; \ - mtspr SPR2,r2; \ - mfspr r2,SRR1; /* Interrupt from user/system mode */ \ - andi. r2,r2,MSR_PR; \ - beq+ 10f; /* Jump if system - already have stack */ \ - mfspr r2,SPR2; /* Restore CCR */ \ - mtcrf 0xFF,r2; \ - mfspr r2,SRR0; /* Preserve interrupt registers */ \ - mtspr SPR2,r2; \ - mfspr r2,SRR1; \ - mtspr SPR3,r2; \ - lis r2,05f@h; \ - ori r2,r2,05f@l; \ - mtspr SRR0,r2; \ - mfmsr r2; \ - ori r2,r2,MSR_|MSR_DR|MSR_IR; \ - mtspr SRR1,r2; \ - rfi; \ -05: lis r2,current_set@ha; \ - lwz r2,current_set@l(r2); \ - mfspr r1,SPR2; \ - stw r1,TSS+LAST_PC(r2); \ - mfspr r1,SPR0; \ - stw r1,TSS+USER_STACK(r2); \ - lwz r1,TSS+KSP(r2); \ - subi r1,r1,INT_FRAME_SIZE; /* Make room for frame */ \ - stw r1,TSS+PT_REGS(r2); /* Save regs pointer for 'ptrace' */ \ - lwz r1,TSS+KSP(r2); \ - b 20f; \ -10: mfspr r2,SPR2; /* Restore CCR */ \ - mtcrf 0xFF,r2; \ - mfspr r2,SRR0; /* Preserve interrupt registers */ \ - mtspr SPR2,r2; \ - mfspr r2,SRR1; \ - mtspr SPR3,r2; \ - lis r2,20f@h; \ - ori r2,r2,20f@l; \ - mtspr SRR0,r2; \ - mfmsr r2; \ - ori r2,r2,MSR_|MSR_DR|MSR_IR; \ - mtspr SRR1,r2; \ - SYNC(); \ - rfi; \ -20: SAVE_ALL_REGS(mark); \ - CHECK_STACK() - -#define RETURN_FROM_INT(mark) \ -90: mfmsr r0; /* Disable interrupts */ \ - li r4,0; \ - ori r4,r4,MSR_EE; \ - andc r0,r0,r4; \ - sync; /* Some chip revs need this... */ \ - mtmsr r0; \ - lis r2,intr_count@ha; /* Need to run 'bottom half' */ \ - lwz r3,intr_count@l(r2); \ - cmpi 0,r3,0; \ - bne 00f; \ - lis r4,bh_mask@ha; \ - lwz r4,bh_mask@l(r4); \ - lis r5,bh_active@ha; \ - lwz r5,bh_active@l(r5); \ - and. r4,r4,r5; \ - beq 00f; \ - addi r3,r3,1; \ - stw r3,intr_count@l(r2); \ - bl _EXTERN(_do_bottom_half); \ - lis r2,intr_count@ha; \ - lwz r3,intr_count@l(r2); \ - subi r3,r3,1; \ - stw r3,intr_count@l(r2); \ -00: lwz r2,_MSR(r1); /* Returning to user mode? */ \ - andi. r2,r2,MSR_PR; \ - beq+ 10f; /* no - no need to mess with stack */ \ -/* lis r2,kernel_pages_are_copyback@ha; \ - lwz r2,kernel_pages_are_copyback@l(r2); \ - cmpi 0,r2,0; \ - beq 05f; \ - bl _EXTERN(flush_instruction_cache); */ \ -05: lis r3,current_set@ha; /* need to save kernel stack pointer */ \ - lwz r3,current_set@l(r3); \ - /*addi r4,r1,INT_FRAME_SIZE*/; /* size of frame */ \ - lwz r4, KERNEL_STACK_PAGE(r3); \ - addi r4,r4,KERNEL_STACK_SIZE; /* reset stack pointer to top of stack page */ \ - /* stack isn't 0'd so show_task():sched.c shows highwater of stack */ \ - stw r4,TSS+KSP(r3); \ - lwz r4,STATE(r3); /* If state != 0, can't run */ \ - cmpi 0,r4,0; \ - beq 06f; \ - bl _EXTERN(schedule); \ - b 90b; \ -06: lwz r4,COUNTER(r3); /* Time quantum expired? */ \ - cmpi 0,r4,0; \ - bne 07f; \ - bl _EXTERN(schedule); \ - b 90b; \ -07: lwz r4,BLOCKED(r3); /* Check for pending unblocked signals */ \ - lwz r5,SIGNAL(r3); \ - andc. r0,r5,r4; /* Lets thru any unblocked */ \ - beq 10f; \ - mr r3,r4; \ - mr r4,r1; \ - bl _EXTERN(do_signal); \ -10: lwz r2,_NIP(r1); /* Restore environment */ \ - mtspr SRR0,r2; \ - lwz r2,_MSR(r1); \ - mtspr SRR1,r2; \ - lmw r3,GPR3(r1); \ - lwz r2,_CTR(r1); \ - mtctr r2; \ - lwz r2,_LINK(r1); \ - mtlr r2; \ - lwz r2,_XER(r1); \ - mtspr XER,r2; \ - lfd fr0,FPCSR(r1); \ - mtfsf 0xFF,fr0; \ - RESTORE_FP_REGS(mark) ; \ - lwz r2,_CCR(r1); \ - mtcrf 0xFF,r2; \ - lwz r0,GPR0(r1); \ - lwz r2,GPR2(r1); \ - lwz r1,GPR1(r1); \ - SYNC(); \ - rfi + -_TEXT() -/* - * This code may be executed by a bootstrap process. If so, the - * purpose is to relocate the loaded image to it's final location - * in memory. - * R3: End of image - * R4: Start of image - 0x400 - * R11: Start of command line string - * R12: End of command line string - * R30: 'BeBx' if this is a BeBox - * - */ - .globl _start + .text .globl _stext _stext: -_start: - addi r4,r4,0x400 /* Point at start of image */ - li r5,0 /* Load address */ - subi r4,r4,4 /* Adjust for auto-increment */ - subi r5,r5,4 - subi r3,r3,4 -00: lwzu r0,4(r4) /* Fast move */ - stwu r0,4(r5) - cmp 0,r3,r4 - bne 00b - li r5,0x100 /* Actual code starts here */ - mtlr r5 - blr -hang: - ori r0,r0,0 - b hang +#ifdef CONFIG_PREP + . = 0x100 +_GLOBAL(HardReset) + b _start -/* - * BeBox CPU #1 vector & code - */ -_ORG(0x0080) - .globl BeBox_CPU1_vector -BeBox_CPU1_vector: - .long 0 -BeBox_CPU1_reset: - li r1,BeBox_CPU1_vector@l - li r2,0 - stw r2,0(r1) -00: lwz r2,0(r1) - cmpi 0,r2,0 - bne 10f - li r2,10000 - mtctr r2 -02: nop - bdnz 02b - b 00b -10: mtlr r1 - blr - -_ORG(0x0100) +#endif /* CONFIG_PREP */ -/* Hard Reset */ - .globl HardReset -HardReset: - b Reset +#ifdef CONFIG_PMAC +/* + * _start is defined this way because the XCOFF loader in the OpenFirmware + * on the powermac expects the entry point to be a procedure descriptor. + */ + .text + .globl _start +_start: + .long TOPHYS(__start),0,0 -_ORG(0x0200) - b MachineCheck +/* + * Enter here with the kernel text, data and bss loaded starting at + * 0, running with virtual == physical mapping. + * r5 points to the prom entry point (the client interface handler + * address). Address translation is turned on, with the prom + * managing the hash table. Interrupts are disabled. The stack + * pointer (r1) points to just below the end of the half-meg region + * from 0x380000 - 0x400000, which is mapped in already. + */ + .globl __start +__start: + +/* + * Use the first pair of BAT registers to map the 1st 8MB + * of RAM to KERNELBASE. + */ + mfspr r9,PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpi 0,r9,1 + lis r7,KERNELBASE@h + bne 4f + ori r7,r7,4 /* set up BAT registers for 601 */ + li r8,0x7f + b 5f +4: ori r7,r7,0xff /* set up BAT registers for 604 */ + li r8,2 + mtspr DBAT0U,r7 + mtspr DBAT0L,r8 +5: mtspr IBAT0U,r7 + mtspr IBAT0L,r8 + isync + +/* + * Now we have the 1st 8M of RAM mapped at KERNELBASE, so we can + * refer to addresses of data items, procedures, etc. normally. + */ + lis r7,start_here@ha /* jump up to our copy at KERNELBASE */ + addi r7,r7,start_here@l + mtlr r7 + blr +#endif /* CONFIG_PMAC */ -_ORG(0x0300) - b DataAccess -_ORG(0x0400) - b InstructionAccess -_ORG(0x0500) - b HardwareInterrupt -_ORG(0x0600) - b Alignment +/* + * Macros for storing registers into and loading registers from + * exception frames. + */ +#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) +#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) +#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) +#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) +#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) +#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) +#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) +#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) +#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) +#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) + +#define SAVE_FPR(n, base) stfd n,TSS_FPR0+8*(n)(base) +#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) +#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) +#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) +#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) +#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) +#define REST_FPR(n, base) lfd n,TSS_FPR0+8*(n)(base) +#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) +#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) +#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) +#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) +#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) + +/* + * Exception entry code. This code runs with address translation + * turned off, i.e. using physical addresses. + * We assume sprg3 has the physical address of the current + * task's thread_struct. + */ +#define EXCEPTION_PROLOG \ +0: mtspr SPRG0,r20; \ + mtspr SPRG1,r21; \ + mfcr r20; \ + mfspr r21,SRR1; /* test whether from user or kernel */\ + andi. r21,r21,MSR_PR; \ + mr r21,r1; /* from kernel - use current sp */\ + beq 1f; \ + mfspr r21,SPRG3; /* from user - load kernel sp */\ + lwz r21,KSP(r21); \ +1: addis r21,r21,-KERNELBASE@h; /* convert sp to physical */ \ + subi r21,r21,INT_FRAME_SIZE+STACK_UNDERHEAD; /* alloc exc. frame */\ + stw r1,GPR1(r21); \ + stw r1,0(r21); \ + addis r1,r21,KERNELBASE@h; /* set new kernel sp */ \ + stw r20,_CCR(r21); /* save registers */ \ + stw r22,GPR22(r21); \ + stw r23,GPR23(r21); \ + mfspr r20,SPRG0; \ + stw r20,GPR20(r21); \ + mfspr r22,SPRG1; \ + stw r22,GPR21(r21); \ + mflr r20; \ + stw r20,_LINK(r21); \ + mfctr r22; \ + stw r22,_CTR(r21); \ + mfspr r20,XER; \ + stw r20,_XER(r21); \ + mfspr r22,SRR0; \ + mfspr r23,SRR1; /* we can now take exceptions */\ + stw r0,GPR0(r21); \ + stw r2,GPR2(r21); \ + SAVE_4GPRS(3, r21); +/* + * Note: code which follows this uses cr0.eq (set if from kernel), + * r21, r22 (SRR0), and r23 (SRR1). + */ + +/* + * Exception vectors. + */ +#define STD_EXCEPTION(n, label, hdlr) \ + . = n; \ +label: \ + EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + li r20,MSR_KERNEL; \ + bl transfer_to_handler; \ + .long hdlr; \ + .long int_return + +#ifndef CONFIG_PREP +/* System reset */ + STD_EXCEPTION(0x100, Reset, UnknownException) +#endif /* ndef CONFIG_PREP */ -_ORG(0x0700) - b ProgramCheck +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) -_ORG(0x0800) - b FloatingPointCheck +/* Data access exception */ + . = 0x300 +DataAccess: + EXCEPTION_PROLOG + mfspr r20,DSISR + andis. r0,r20,0x8470 /* weird error? */ + bne 1f /* if not, try to put a PTE */ + mfspr r3,DAR /* into the hash table */ + rlwinm r4,r23,32-13,30,30 /* MSR_PR -> _PAGE_USER */ + rlwimi r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ + mfspr r5,SPRG3 /* phys addr of TSS */ + bl hash_page +1: stw r20,_DSISR(r21) + mr r5,r20 + mfspr r4,DAR + stw r4,_DAR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long do_page_fault + .long int_return + +/* Instruction access exception */ + . = 0x400 +InstructionAccess: + EXCEPTION_PROLOG + andis. r0,r23,0x4000 /* no pte found? */ + beq 1f /* if so, try to put a PTE */ + mr r3,r22 /* into the hash table */ + rlwinm r4,r23,32-13,30,30 /* MSR_PR -> _PAGE_USER */ + mr r20,r23 /* SRR1 has reason bits */ + mfspr r5,SPRG3 /* phys addr of TSS */ + bl hash_page +1: addi r3,r1,STACK_FRAME_OVERHEAD + mr r4,r22 + mr r5,r23 + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long do_page_fault + .long int_return + +/* External interrupt */ + STD_EXCEPTION(0x500, HardwareInterrupt, handle_IRQ) + +/* Alignment exception */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long AlignmentException + .long int_return -/* Decrementer register - ignored for now... */ +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long ProgramCheckException + .long int_return + +/* Floating-point unavailable */ + . = 0x800 +FPUnavailable: + EXCEPTION_PROLOG + bne load_up_fpu /* if from user, just load it up */ + li r20,MSR_KERNEL + bl transfer_to_handler /* if from kernel, take a trap */ + .long KernelFP + .long int_return + +/* Decrementer */ +#ifdef CONFIG_PREP +/* - ignored for now... */ _ORG(0x0900) -/* TRACE_TRAP(0x900) */ - mtspr SPR0,r1 + mtspr SPRG0,r1 lis r1,0x7FFF ori r1,r1,0xFFFF mtspr DEC,r1 - mfspr r1,SPR0 -#if 0 - SYNC -#endif + mfspr r1,SPRG0 rfi - -_ORG(0x0A00) -DEFAULT_TRAP(0x0A00) -_ORG(0x0B00) -DEFAULT_TRAP(0x0B00) +#endif /* CONFIG_PREP */ +#ifdef CONFIG_PMAC + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) +#endif /* CONFIG_PMAC */ -/* - * System call - */ -_ORG(0x0C00) - b SystemCall + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) -_ORG(0x0D00) - b SingleStep +/* System call */ + . = 0xc00 +SystemCall: + EXCEPTION_PROLOG + stw r3,ORIG_GPR3(r21) + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long DoSyscall + .long int_return + +/* Single step - not used on 601 */ + STD_EXCEPTION(0xd00, SingleStep, SingleStepException) -_ORG(0x0E00) -DEFAULT_TRAP(0x0E00) -_ORG(0x0F00) -DEFAULT_TRAP(0x0F00) + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) /* - * Handle TLB Miss on an instruction load + * Handle TLB miss for instruction on 603/603e. + * Note: we get an alternate set of r0 - r3 to use automatically. */ -_ORG(0x1000) -/* Note: It is *unsafe* to use the TRACE TRAP macro here since there */ -/* could be a 'trace' in progress when the TLB miss occurs. */ -/* TRACE_TRAP(0x1000) */ - b InstructionTLBMiss + . = 0x1000 +InstructionTLBMiss: + mfctr r0 /* Need to save this - CTR can't be touched! */ + mfspr r2,HASH1 /* Get PTE pointer */ + mfspr r3,ICMP /* Partial item compare value */ +00: li r1,8 /* 8 items / bucket */ + mtctr r1 + subi r2,r2,8 /* Preset pointer */ +10: lwzu r1,8(r2) /* Get next PTE */ + cmp 0,r1,r3 /* Found entry yet? */ + bdnzf 2,10b /* Jump back if not, until CTR==0 */ + bne 30f /* Try secondary hash if CTR==0 */ + lwz r1,4(r2) /* Get second word of entry */ +20: mtctr r0 /* Restore CTR */ + mfspr r3,SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r3 + mfspr r0,IMISS /* Set to update TLB */ + mtspr RPA,r1 + tlbli r0 + rfi /* All done */ +/* Secondary hash */ +30: andi. r1,r3,0x40 /* Already doing secondary hash? */ + bne InstructionAddressInvalid /* Yes - item not in hash table */ + mfspr r2,HASH2 /* Get hash table pointer */ + ori r3,r3,0x40 /* Set secondary hash */ + b 00b /* Try lookup again */ +InstructionAddressInvalid: + mfspr r3,SRR1 + rlwinm r1,r3,9,6,6 /* Get load/store bit */ + addis r1,r1,0x4000 /* Set bit 1 -> PTE not found */ + mtspr DSISR,r1 + mtctr r0 /* Restore CTR */ + andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ + or r2,r2,r1 + mtspr SRR1,r2 + mfspr r1,IMISS /* Get failing address */ + rlwinm. r2,r2,0,31,31 /* Check for little endian access */ + beq 20f /* Jump if big endian */ + xori r1,r1,3 +20: mtspr DAR,r1 /* Set fault address */ + mfmsr r0 /* Restore "normal" registers */ + xoris r0,r0,MSR_TGPR>>16 + mtcrf 0x80,r3 /* Restore CR0 */ + sync /* Some chip revs have problems here... */ + mtmsr r0 + b InstructionAccess /* - * Handle TLB Miss on a data item load + * Handle TLB miss for DATA Load operation on 603/603e */ -_ORG(0x1100) -/* TRACE_TRAP(0x1100) */ - b DataLoadTLBMiss + . = 0x1100 +DataLoadTLBMiss: + mfctr r0 /* Need to save this - CTR can't be touched! */ + mfspr r2,HASH1 /* Get PTE pointer */ + mfspr r3,DCMP /* Partial item compare value */ +00: li r1,8 /* 8 items / bucket */ + mtctr r1 + subi r2,r2,8 /* Preset pointer */ +10: lwzu r1,8(r2) /* Get next PTE */ + cmp 0,r1,r3 /* Found entry yet? */ + bdnzf 2,10b /* Jump back if not, until CTR==0 */ + bne 30f /* Try secondary hash if CTR==0 */ + lwz r1,4(r2) /* Get second word of entry */ +20: mtctr r0 /* Restore CTR */ + mfspr r3,SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r3 + mfspr r0,DMISS /* Set to update TLB */ + mtspr RPA,r1 + tlbld r0 + rfi /* All done */ +/* Secondary hash */ +30: andi. r1,r3,0x40 /* Already doing secondary hash? */ + bne DataAddressInvalid /* Yes - item not in hash table */ + mfspr r2,HASH2 /* Get hash table pointer */ + ori r3,r3,0x40 /* Set secondary hash */ + b 00b /* Try lookup again */ +DataAddressInvalid: + mfspr r3,SRR1 + rlwinm r1,r3,9,6,6 /* Get load/store bit */ + addis r1,r1,0x4000 /* Set bit 1 -> PTE not found */ + mtspr DSISR,r1 + mtctr r0 /* Restore CTR */ + andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ + mtspr SRR1,r2 + mfspr r1,DMISS /* Get failing address */ + rlwinm. r2,r2,0,31,31 /* Check for little endian access */ + beq 20f /* Jump if big endian */ + xori r1,r1,3 +20: mtspr DAR,r1 /* Set fault address */ + mfmsr r0 /* Restore "normal" registers */ + xoris r0,r0,MSR_TGPR>>16 + mtcrf 0x80,r3 /* Restore CR0 */ + sync /* Some chip revs have problems here... */ + mtmsr r0 + b DataAccess /* - * Handle TLB Miss on a store operation + * Handle TLB miss for DATA Store on 603/603e */ -_ORG(0x1200) -/* TRACE_TRAP(0x1200) */ - b DataStoreTLBMiss + . = 0x1200 +DataStoreTLBMiss: + mfctr r0 /* Need to save this - CTR can't be touched! */ + mfspr r2,HASH1 /* Get PTE pointer */ + mfspr r3,DCMP /* Partial item compare value */ +00: li r1,8 /* 8 items / bucket */ + mtctr r1 + subi r2,r2,8 /* Preset pointer */ +10: lwzu r1,8(r2) /* Get next PTE */ + cmp 0,r1,r3 /* Found entry yet? */ + bdnzf 2,10b /* Jump back if not, until CTR==0 */ + bne 30f /* Try secondary hash if CTR==0 */ + lwz r1,4(r2) /* Get second word of entry */ +20: mtctr r0 /* Restore CTR */ + mfspr r3,SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r3 + mfspr r0,DMISS /* Set to update TLB */ + mtspr RPA,r1 + tlbld r0 + rfi /* All done */ +/* Secondary hash */ +30: andi. r1,r3,0x40 /* Already doing secondary hash? */ + bne DataAddressInvalid /* Yes - item not in hash table */ + mfspr r2,HASH2 /* Get hash table pointer */ + ori r3,r3,0x40 /* Set secondary hash */ + b 00b /* Try lookup again */ -_ORG(0x1300) -InstructionAddressBreakpoint: - DEFAULT_TRAP(0x1300) +/* Instruction address breakpoint exception (on 603/604) */ + STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint) -_ORG(0x1400) -SystemManagementInterrupt: - DEFAULT_TRAP(0x1400) +/* System management exception (603?) */ + STD_EXCEPTION(0x1400, Trap_14, UnknownException) -_ORG(0x1500) + STD_EXCEPTION(0x1500, Trap_15, UnknownException) + STD_EXCEPTION(0x1600, Trap_16, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, UnknownException) + STD_EXCEPTION(0x1800, Trap_18, UnknownException) + STD_EXCEPTION(0x1900, Trap_19, UnknownException) + STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) + STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) + STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) + STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) + STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) + STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) + +/* Run mode exception */ + STD_EXCEPTION(0x2000, RunMode, RunModeException) + + STD_EXCEPTION(0x2100, Trap_21, UnknownException) + STD_EXCEPTION(0x2200, Trap_22, UnknownException) + STD_EXCEPTION(0x2300, Trap_23, UnknownException) + STD_EXCEPTION(0x2400, Trap_24, UnknownException) + STD_EXCEPTION(0x2500, Trap_25, UnknownException) + STD_EXCEPTION(0x2600, Trap_26, UnknownException) + STD_EXCEPTION(0x2700, Trap_27, UnknownException) + STD_EXCEPTION(0x2800, Trap_28, UnknownException) + STD_EXCEPTION(0x2900, Trap_29, UnknownException) + STD_EXCEPTION(0x2a00, Trap_2a, UnknownException) + STD_EXCEPTION(0x2b00, Trap_2b, UnknownException) + STD_EXCEPTION(0x2c00, Trap_2c, UnknownException) + STD_EXCEPTION(0x2d00, Trap_2d, UnknownException) + STD_EXCEPTION(0x2e00, Trap_2e, UnknownException) + STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) + + . = 0x3000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception, turning + * on address translation. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + andi. r23,r23,MSR_PR + mfspr r23,SPRG3 /* if from user, fix up tss */ + beq 2f +#ifdef CONFIG_PMAC + lwz r24,GPR1(r21) + stw r22,LAST_PC(r23) + stw r24,USER_STACK(r23) +#endif /* CONFIG_PMAC */ + addi r24,r1,STACK_FRAME_OVERHEAD + stw r24,PT_REGS(r23) +2: addi r2,r23,-TSS /* set r2 to current */ + addis r2,r2,KERNELBASE@h + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ /* - * This space [buffer] is used to forceably flush the data cache when - * running in copyback mode. This is necessary IFF the data cache could - * contain instructions for which the instruction cache has stale data. - * Since the instruction cache NEVER snoops the data cache, memory must - * be made coherent with the data cache to insure that the instruction - * cache gets a valid instruction stream. Note that this flushing is - * only performed when switching from system to user mode since this is - * the only juncture [as far as the OS goes] where the data cache may - * contain instructions, e.g. after a disk read. + * Continuation of the floating-point unavailable handler. */ -#define NUM_CACHE_LINES 128*4 -#define CACHE_LINE_SIZE 32 -cache_flush_buffer: - .space NUM_CACHE_LINES*CACHE_LINE_SIZE /* CAUTION! these need to match hardware */ +load_up_fpu: + bl giveup_fpu_unmapped + ori r23,r23,MSR_FP /* enable use of FP after return */ + mfspr r5,SPRG3 /* current task's TSS (phys) */ + lfd fr0,TSS_FPSCR-4(r5) + mtfsf 0xff,fr0 + REST_32FPRS(0, r5) + +/* use last_task_used_math instead of fpu_tss */ + lis r3,last_task_used_math@h/*a*/ + addis r3,r3,-KERNELBASE@h + subi r4,r5,TSS + addis r4,r4,KERNELBASE@h + stw r4,last_task_used_math@l(r3) +#if 0 + lis r3,fpu_tss@ha + addis r4,r5,KERNELBASE@h + addis r3,r3,-KERNELBASE@h + stw r4,fpu_tss@l(r3) +#endif + /* restore registers and return */ + lwz r3,_CCR(r21) + lwz r4,_LINK(r21) + mtcrf 0xff,r3 + mtlr r4 + REST_GPR(1, r21) + REST_4GPRS(3, r21) + /* we haven't used ctr or xer */ + mtspr SRR1,r23 + mtspr SRR0,r22 + REST_GPR(20, r21) + REST_2GPRS(22, r21) + lwz r21,GPR21(r21) + SYNC + rfi -#if NUM_CACHE_LINES < 512 -_ORG(0x4000) -#endif +/* + * Load a PTE into the hash table, if possible. + * The address is in r3, and r4 contains access flags: + * _PAGE_USER (4) if a user-mode access, ored with + * _PAGE_RW (2) if a write. r20 contains DSISR or SRR1, + * so bit 1 (0x40000000) is set if the exception was due + * to no matching PTE being found in the hash table. + * r5 contains the physical address of the current task's tss. + * + * Returns to the caller if the access is illegal or there is no + * mapping for the address. Otherwise it places an appropriate PTE + * in the hash table and returns from the exception. + * Uses r0, r2 - r6, ctr, lr. + * + * For speed, 4 of the instructions get patched once the size and + * physical address of the hash table are known. These definitions + * of Hash_base and Hash_bits below are just an example. + */ +Hash_base = 0x180000 +Hash_bits = 12 /* e.g. 256kB hash table */ +Hash_msk = (((1 << Hash_bits) - 1) * 64) + + .globl hash_page +hash_page: + /* Get PTE (linux-style) and check access */ + lwz r5,PG_TABLES(r5) /* task's page tables */ + lis r2,-KERNELBASE@h + add r5,r5,r2 /* convert to phys addr */ + rlwimi r5,r3,12,20,29 /* insert top 10 bits of address */ + lwz r5,0(r5) /* get pmd entry */ + rlwinm. r5,r5,0,0,19 /* extract address of pte page */ + beqlr- /* return if no mapping */ + add r2,r5,r2 + rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ + lwz r6,0(r2) /* get linux-style pte */ + ori r4,r4,1 /* set _PAGE_PRESENT bit in access */ + andc. r0,r4,r6 /* check access & ~permission */ + bnelr- /* return if access not permitted */ + ori r6,r6,0x100 /* set _PAGE_ACCESSED in pte */ + rlwinm r5,r4,5,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ + rlwimi r5,r4,7,22,22 /* _PAGE_RW -> _PAGE_HWWRITE */ + or r6,r6,r5 + stw r6,0(r2) /* update PTE (accessed/dirty bits) */ + + /* Convert linux-style PTE to low word of PPC-style PTE */ + rlwinm r4,r6,32-9,31,31 /* _PAGE_HWWRITE -> PP lsb */ + rlwimi r6,r6,32-1,31,31 /* _PAGE_USER -> PP (both bits now) */ + ori r4,r4,0xe04 /* clear out reserved bits */ + andc r6,r6,r4 /* PP=2 or 0, when _PAGE_HWWRITE */ + + /* Construct the high word of the PPC-style PTE */ + mfsrin r5,r3 /* get segment reg for segment */ + rlwinm r5,r5,7,1,24 /* put VSID in 0x7fffff80 bits */ + oris r5,r5,0x8000 /* set V (valid) bit */ + rlwimi r5,r3,10,26,31 /* put in API (abbrev page index) */ + + /* Get the address of the primary PTE group in the hash table */ + .globl hash_page_patch_A +hash_page_patch_A: + lis r4,Hash_base@h /* base address of hash table */ + rlwimi r4,r5,32-1,26-Hash_bits,25 /* (VSID & hash_mask) << 6 */ + rlwinm r0,r3,32-6,26-Hash_bits,25 /* (PI & hash_mask) << 6 */ + xor r4,r4,r0 /* make primary hash */ + + /* See whether it was a PTE not found exception or a + protection violation. */ + andis. r0,r20,0x4000 + li r2,8 /* PTEs/group */ + bne 10f /* no PTE: go look for an empty slot */ + tlbie r3 /* invalidate TLB entry */ + + /* Search the primary PTEG for a PTE whose 1st word matches r5 */ + mtctr r2 + addi r3,r4,-8 +1: lwzu r0,8(r3) /* get next PTE */ + cmp 0,r0,r5 + bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ + beq+ found_slot + + /* Search the secondary PTEG for a matching PTE */ + ori r5,r5,0x40 /* set H (secondary hash) bit */ + .globl hash_page_patch_B +hash_page_patch_B: + xoris r3,r4,Hash_msk>>16 /* compute secondary hash */ + xori r3,r3,0xffc0 + addi r3,r3,-8 + mtctr r2 +2: lwzu r0,8(r3) + cmp 0,r0,r5 + bdnzf 2,2b + beq+ found_slot + xori r5,r5,0x40 /* clear H bit again */ + + /* Search the primary PTEG for an empty slot */ +10: mtctr r2 + addi r3,r4,-8 /* search primary PTEG */ +1: lwzu r0,8(r3) /* get next PTE */ + cmpi 0,r0,0 /* empty? */ + bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ + beq+ found_empty + + /* Search the secondary PTEG for an empty slot */ + ori r5,r5,0x40 /* set H (secondary hash) bit */ + .globl hash_page_patch_C +hash_page_patch_C: + xoris r3,r4,Hash_msk>>16 /* compute secondary hash */ + xori r3,r3,0xffc0 + addi r3,r3,-8 + mtctr r2 +2: lwzu r0,8(r3) + cmpi 0,r0,0 + bdnzf 2,2b + beq+ found_empty + + /* Choose an arbitrary slot in the primary PTEG to overwrite */ + xori r5,r5,0x40 /* clear H bit again */ + lwz r2,next_slot@l(0) + addi r2,r2,8 + andi. r2,r2,0x38 + stw r2,next_slot@l(0) + add r3,r4,r2 + + /* Store PTE in PTEG */ +found_empty: + stw r5,0(r3) +found_slot: + stw r6,4(r3) + SYNC + /* Return from the exception */ + lwz r3,_CCR(r21) + lwz r4,_LINK(r21) + lwz r5,_CTR(r21) + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + REST_GPR(0, r21) + REST_2GPRS(1, r21) + REST_4GPRS(3, r21) + /* we haven't used xer */ + mtspr SRR1,r23 + mtspr SRR0,r22 + REST_GPR(20, r21) + REST_2GPRS(22, r21) + lwz r21,GPR21(r21) + SYNC + rfi +next_slot: + .long 0 -/* changed to use r3 as residual pointer (as firmware does), that's all -- Cort */ /* - * Hardware reset [actually from bootstrap] - * Initialize memory management & call secondary init - * Registers initialized by bootstrap: - * R11: Start of command line string - * R12: End of command line string - * R28: Residual data - * R29: Total Memory Size - * R30: 'BeBx' if this is a BeBox - */ -Reset: - lis r7,0xF000 /* To mask upper 4 bits */ -/* set pointer to residual data */ - lis r1,resptr@h - ori r1,r1,resptr@l - andc r1,r1,r7 -/* changed to use r3 as residual pointer (as firmware does) -- Cort */ -/* this is only a ptr, the actual data is copied in mmu_init */ - stw r3,0(r1) - -/* Copy argument string */ - li r0,0 /* Null terminate string */ - stb r0,0(r12) - lis r1,cmd_line@h - ori r1,r1,cmd_line@l - andc r1,r1,r7 /* No MMU yet - need unmapped address */ - subi r1,r1,1 - subi r11,r11,1 -00: lbzu r0,1(r11) - cmpi 0,r0,0 - stbu r0,1(r1) - bne 00b + * This is where the main kernel code starts. + */ -#define IS_BE_BOX 0x42654278 /* 'BeBx' */ - lis r1,isBeBox@h - ori r1,r1,isBeBox@l - andc r1,r1,r7 -/* See if this is a CPU other than CPU#1 */ -/* This [currently] happens on the BeBox */ - lwz r2,0(r1) - cmpi 0,r2,0 - bne Reset_BeBox_CPU1 -/* Save machine type indicator */ - li r2,0 - lis r3,IS_BE_BOX>>16 - ori r3,r3,IS_BE_BOX&0xFFFF - cmp 0,r30,r3 - bne 00f - li r2,1 - mr r11,r28 - mr r12,r29 - lis r5,BeBox_CPU1_vector@h - ori r5,r5,BeBox_CPU1_vector@l - andc r5,r5,r7 /* Tell CPU #1 where to go */ -00: stw r2,0(r1) - stw r30,4(r1) - +start_here: + /* + * Enable caches and 604-specific features if necessary. + */ + mfspr r9,PVR + rlwinm r9,r9,16,16,31 + cmpi 0,r9,1 + beq 4f /* not needed for 601 */ + mfspr r7,HID0 + andi. r0,r7,HID0_DCE + ori r7,r7,HID0_ICE|HID0_DCE + ori r8,r7,HID0_ICFI + bne 3f /* don't invalidate the D-cache */ + ori r8,r8,HID0_DCI /* unless it wasn't enabled */ +3: sync + mtspr HID0,r8 /* enable and invalidate caches */ + sync + mtspr HID0,r7 /* enable caches */ + sync + isync + cmpi 0,r9,4 /* check for 604 */ + cmpi 1,r9,9 /* or 604e */ + cror 2,2,6 + bne 4f + ori r7,r7,HID0_SIED|HID0_BHTE /* for 604[e], enable */ + mtspr HID0,r7 /* superscalar exec & br history tbl */ +4: + /* ptr to current */ + lis r2,init_task_union@h + ori r2,r2,init_task_union@l + /* ptr to phys current tss */ + addis r3,r2,-KERNELBASE@h + addi r3,r3,TSS /* init task's TSS */ + mtspr SPRG3,r3 + /* stack */ + addi r1,r2,TASK_UNION_SIZE + li r0,0 + stwu r0,-STACK_FRAME_OVERHEAD(r1) -#if 0 - lis r1,sys_stack@h - ori r1,r1,sys_stack@l -#else - lis r1,init_kernel_stack@h - ori r1,r1,init_kernel_stack@l -#endif - addi r1,r1,0x1000 /* top of stack */ -#if 0 - li r2,0x0FFF /* Mask stack address down to page boundary */ -#endif - andc r1,r1,r2 - subi r1,r1,INT_FRAME_SIZE /* Padding for first frame */ - li r2,0 /* TOC pointer for nanokernel */ - li r0,MSR_ /* Make sure FPU enabled */ - mtmsr r0 - lis r3,_edata@h /* Clear BSS */ - ori r3,r3,_edata@l - andc r3,r3,r7 /* make unmapped address */ - lis r4,_end@h - ori r4,r4,_end@l - andc r4,r4,r7 /* make unmapped address */ - subi r3,r3,4 + /* Clear out the BSS */ + lis r7,_end@ha + addi r7,r7,_end@l + lis r8,__bss_start@ha + addi r8,r8,__bss_start@l + subf r7,r8,r7 + addi r7,r7,3 + rlwinm. r7,r7,30,2,31 + beq 2f + addi r8,r8,-4 + mtctr r7 li r0,0 -00: stwu r0,4(r3) - cmp 0,r3,r4 - blt 00b -#if 0 -/* Save total memory size (passed from bootstrap) */ - lis r3,_TotalMemory@h - ori r3,r3,_TotalMemory@l - andc r3,r3,r7 /* make unmapped address */ - stw r29,0(r3) -#endif -/* Initialize BAT registers */ - lis r3,BAT0@h - ori r3,r3,BAT0@l - andc r3,r3,r7 /* make unmapped address */ - lwz r0,0(r3) +3: stwu r0,4(r8) + bdnz 3b +2: +/* + * Initialize the prom stuff (powermacs only) and the MMU. + */ +#ifdef CONFIG_PMAC + bl prom_init +#endif /* CONFIG_PMAC */ + bl MMU_init + +/* + * Go back to running unmapped so we can load up new values + * for SDR1 (hash table pointer) and the segment registers + * and change to using our exception vectors. + */ + lis r6,_SDR1@ha + lwz r6,_SDR1@l(r6) + li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) + lis r4,2f@h + addis r4,r4,-KERNELBASE@h + ori r4,r4,2f@l + mtspr SRR0,r4 + mtspr SRR1,r3 + rfi +/* Load up the kernel context */ +2: +#ifdef CONFIG_PREP + /* reload the bats now that MMU_init() has setup them up -- Cort */ + LOAD_BATS(r3,r0) +#endif + + SYNC /* Force all PTE updates to finish */ + tlbia /* Clear all TLB entries */ + mtspr SDR1,r6 + li r0,16 /* load up segment register values */ + mtctr r0 /* for context 0 */ + lis r3,0x2000 /* Ku = 1, VSID = 0 */ + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,1 /* increment VSID */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b +#ifdef CONFIG_PMAC + li r0,0 /* zot the BATs */ +#if 1 mtspr IBAT0U,r0 - mtspr DBAT0U,r0 - lwz r0,4(r3) mtspr IBAT0L,r0 + mtspr DBAT0U,r0 mtspr DBAT0L,r0 - lis r3,BAT1@h - ori r3,r3,BAT1@l - andc r3,r3,r7 /* make unmapped address */ - lwz r0,0(r3) +#endif mtspr IBAT1U,r0 - mtspr DBAT1U,r0 - lwz r0,4(r3) mtspr IBAT1L,r0 + mtspr DBAT1U,r0 mtspr DBAT1L,r0 -/* this BAT mapping will cover all of kernel space */ -#ifdef NEWMM - lis r3,BAT2@h - ori r3,r3,BAT2@l -#else - lis r3,TMP_BAT2@h - ori r3,r3,TMP_BAT2@l -#endif - andc r3,r3,r7 /* make unmapped address */ - lwz r0,0(r3) mtspr IBAT2U,r0 - mtspr DBAT2U,r0 - lwz r0,4(r3) mtspr IBAT2L,r0 + mtspr DBAT2U,r0 mtspr DBAT2L,r0 -#if 1 - lis r3,BAT3@h - ori r3,r3,BAT3@l - andc r3,r3,r7 /* make unmapped address */ - lwz r0,0(r3) mtspr IBAT3U,r0 - mtspr DBAT3U,r0 - lwz r0,4(r3) mtspr IBAT3L,r0 + mtspr DBAT3U,r0 mtspr DBAT3L,r0 -#endif -/* Now we can turn on the MMU */ - mfmsr r3 - ori r3,r3,MSR_DR|MSR_IR - mtspr SRR1,r3 - lis r3,10f@h - ori r3,r3,10f@l - mtspr SRR0,r3 -DO_RFI_TRACE_UNMAPPED(0xDEAD0000) - SYNC - rfi /* enables MMU */ -10: bl _EXTERN(MMU_init) /* initialize MMU environment */ -DO_RFI_TRACE_MAPPED(0xDEAD0100) -/* Withdraw BAT2->RAM mapping */ - lis r7,0xF000 /* To mask upper 4 bits */ - lis r3,20f@h - ori r3,r3,20f@l - andc r3,r3,r7 /* make unmapped address */ - mtspr SRR0,r3 - mfmsr r3 - li r4,MSR_DR|MSR_IR - andc r3,r3,r4 - mtspr SRR1,r3 - SYNC -DO_RFI_TRACE_MAPPED(0xDEAD0200) - SYNC - rfi -20: - -DO_RFI_TRACE_UNMAPPED(0xDEAD0400) -20: lis r3,BAT2@h - ori r3,r3,BAT2@l - andc r3,r3,r7 /* make unmapped address */ - lwz r0,0(r3) - mtspr IBAT2U,r0 - mtspr DBAT2U,r0 - lwz r0,4(r3) - mtspr IBAT2L,r0 - mtspr DBAT2L,r0 -/* Load up the kernel context */ - lis r2,init_task@h - ori r2,r2,init_task@l - addi r2,r2,TSS - andc r2,r2,r7 /* make unmapped address */ - SYNC /* Force all PTE updates to finish */ - tlbia /* Clear all TLB entries */ - lis r3,_SDR1@h - ori r3,r3,_SDR1@l - andc r3,r3,r7 /* make unmapped address */ - lwz r3,0(r3) - mtspr SDR1,r3 - lwz r0,MMU_SEG0(r2) - mtsr SR0,r0 - lwz r0,MMU_SEG1(r2) - mtsr SR1,r0 - lwz r0,MMU_SEG2(r2) - mtsr SR2,r0 - lwz r0,MMU_SEG3(r2) - mtsr SR3,r0 - lwz r0,MMU_SEG4(r2) - mtsr SR4,r0 - lwz r0,MMU_SEG5(r2) - mtsr SR5,r0 - lwz r0,MMU_SEG6(r2) - mtsr SR6,r0 - lwz r0,MMU_SEG7(r2) - mtsr SR7,r0 - lwz r0,MMU_SEG8(r2) - mtsr SR8,r0 - lwz r0,MMU_SEG9(r2) - mtsr SR9,r0 - lwz r0,MMU_SEG10(r2) - mtsr SR10,r0 - lwz r0,MMU_SEG11(r2) - mtsr SR11,r0 - lwz r0,MMU_SEG12(r2) - mtsr SR12,r0 - lwz r0,MMU_SEG13(r2) - mtsr SR13,r0 - lwz r0,MMU_SEG14(r2) - mtsr SR14,r0 - lwz r0,MMU_SEG15(r2) - mtsr SR15,r0 +#endif /* Now turn on the MMU for real! */ - mfmsr r3 - ori r3,r3,MSR_DR|MSR_IR - mtspr SRR1,r3 - lis r3,30f@h - ori r3,r3,30f@l + li r4,MSR_KERNEL + lis r3,start_kernel@h + ori r3,r3,start_kernel@l mtspr SRR0,r3 -DO_RFI_TRACE_UNMAPPED(0xDEAD0500) - SYNC - rfi /* enables MMU */ -30: -/* Turn on L1 Data Cache */ - mfspr r3,HID0 /* Caches are controlled by this register */ - ori r4,r3,(HID0_ICE|HID0_ICFI) - ori r3,r3,(HID0_ICE) - ori r4,r4,(HID0_DCE|HID0_DCI) - ori r3,r3,(HID0_DCE) - sync - mtspr HID0,r4 - mtspr HID0,r3 -/* L1 cache enable */ - mfspr r2,PVR /* Check for 603/603e */ - srwi r2,r2,16 - cmpi 0,r2,4 /* 604 */ - bne 40f - mfspr r3,HID0 /* Turn on 604 specific features */ - ori r3,r3,(HID0_SIED|HID0_BHTE) - mtspr HID0,r3 -40: b _EXTERN(start_kernel) /* call main code */ - .long 0 # Illegal! + mtspr SRR1,r4 + rfi /* enable MMU and jump to start_kernel */ +#ifdef CONFIG_PREP /* - * BeBox CPU #2 runs here - */ -Reset_BeBox_CPU1: - lis r1,CPU1_stack@h - ori r1,r1,CPU1_stack@l - li r2,0x0FFF /* Mask stack address down to page boundary */ - andc r1,r1,r2 - subi r1,r1,INT_FRAME_SIZE /* Padding for first frame */ - lis r30,CPU1_trace@h - ori r30,r30,CPU1_trace@l - andc r30,r30,r7 - li r5,1 - stw r5,0(r30) - li r2,0 /* TOC pointer for nanokernel */ - li r0,MSR_ /* Make sure FPU enabled */ + * This is jumped to on prep systems right after the kernel is relocated + * to its proper place in memory by the boot loader. The expected layout + * of the regs is: + * R3: End of image + * R4: Start of image - 0x400 + * R11: Start of command line string + * R12: End of command line string + * + * This just gets a minimal mmu environment setup so we can call + * start_here() to do the real work. + * -- Cort + */ + .globl __start +__start: + .globl _start +_start: + lis r7,0xF000 /* To mask upper 4 bits */ +/* save pointer to residual data */ + lis r1,resptr@h + ori r1,r1,resptr@l + addis r1,r1,-KERNELBASE@h + stw r3,0(r1) +/* save argument string */ + li r0,0 /* Null terminate string */ + stb r0,0(r12) + lis r1,cmd_line@h + ori r1,r1,cmd_line@l + addis r1,r1,-KERNELBASE@h + subi r1,r1,1 + subi r11,r11,1 +00: lbzu r0,1(r11) + cmpi 0,r0,0 + stbu r0,1(r1) + bne 00b +/* setup the msr with sane values */ + li r0,MSR_ mtmsr r0 -/* Initialize BAT registers */ - lis r3,BAT0@h - ori r3,r3,BAT0@l - andc r3,r3,r7 /* make unmapped address */ - lwz r0,0(r3) - mtspr IBAT0U,r0 - mtspr DBAT0U,r0 - lwz r0,4(r3) - mtspr IBAT0L,r0 - mtspr DBAT0L,r0 - lis r3,BAT1@h - ori r3,r3,BAT1@l - andc r3,r3,r7 /* make unmapped address */ - lwz r0,0(r3) - mtspr IBAT1U,r0 - mtspr DBAT1U,r0 - lwz r0,4(r3) - mtspr IBAT1L,r0 - mtspr DBAT1L,r0 - lis r3,TMP_BAT2@h - ori r3,r3,TMP_BAT2@l - andc r3,r3,r7 /* make unmapped address */ - lwz r0,0(r3) - mtspr IBAT2U,r0 - mtspr DBAT2U,r0 - lwz r0,4(r3) - mtspr IBAT2L,r0 - mtspr DBAT2L,r0 -/* Now we can turn on the MMU */ +/* turn on the mmu with bats covering kernel enough to get started */ + LOAD_BATS(r3,r0) mfmsr r3 ori r3,r3,MSR_DR|MSR_IR mtspr SRR1,r3 lis r3,10f@h ori r3,r3,10f@l mtspr SRR0,r3 - li r5,2 - stw r5,0(r30) SYNC rfi /* enables MMU */ -10: - lis r30,CPU1_trace@h - ori r30,r30,CPU1_trace@l - li r5,3 - stw r5,0(r30) - bl _EXTERN(BeBox_CPU1) - -/* - * Machine Check (Bus Errors, etc) - */ -MachineCheck: - TRACE_TRAP(0x0200) - SAVE_INT_REGS(0x0200) - mr r3,r1 /* Set pointer to saved regs */ - bl _EXTERN(MachineCheckException) - RETURN_FROM_INT(0x0200) - -/* - * Data Access exception - */ -DataAccess: - SAVE_INT_REGS(0x0300) -#if 1 - mfspr r3, DAR - mfspr r4, DSISR - li r5, 0 /* not a text fault */ - mr r6, r1 - bl _EXTERN(new_page_fault) -#else - SAVE_PAGE_FAULT_REGS(0x0D00) - mr r3,r1 - bl _EXTERN(DataAccessException) -#endif - RETURN_FROM_INT(0x0300) - -/* - * Instruction Access Exception - */ -InstructionAccess: - SAVE_INT_REGS(0x0400) -#if 1 - mfspr r3, SPR2 /* srr0 was saved here */ - mfspr r4, SPR3 /* srr1 was saved here */ - li r5, 1 /* a text fault */ - mr r6, r1 - bl _EXTERN(new_page_fault) -#else - SAVE_PAGE_FAULT_REGS(0x0D00) - mr r3,r1 - bl _EXTERN(InstructionAccessException) -#endif - RETURN_FROM_INT(0x0400) - -/* - * Hardware Interrupt - */ -HardwareInterrupt: - SAVE_INT_REGS(0x0500) - BUMP(__Hardware_Interrupts) - mr r3,r1 /* Set pointer to saved regs */ - bl _EXTERN(handle_IRQ) - RETURN_FROM_INT(0x0500) - -/* - * Alignment - */ -Alignment: - TRACE_TRAP(0x0600) - SAVE_INT_REGS(0x0600) - mr r3,r1 /* Set pointer to saved regs */ - bl _EXTERN(AlignmentException) - RETURN_FROM_INT(0x0600) - +10: lis r7,start_here@ha /* jump up to our copy at KERNELBASE */ + addi r7,r7,start_here@l + mtlr r7 + blr +#endif /* CONFIG_PREP */ + /* - * Illegal instruction + * FP unavailable trap from kernel - print a message, but let + * the task use FP in the kernel until it returns to user mode. */ -ProgramCheck: - TRACE_TRAP(0x0700) - SAVE_INT_REGS(0x0700) - mr r3,r1 /* Set pointer to saved regs */ - bl _EXTERN(ProgramCheckException) - RETURN_FROM_INT(0x0700) - -/* - * Single Step Exception - */ -SingleStep: - SAVE_INT_REGS(0x0D00) - SAVE_PAGE_FAULT_REGS(0x0D00) - mr r3,r1 /* Set pointer to saved regs */ - bl _EXTERN(SingleStepException) -#if 0 - bl _EXTERN(flush_instruction_cache) +KernelFP: + lwz r3,_MSR(r1) + ori r3,r3,MSR_FP + stw r3,_MSR(r1) /* enable use of FP after return */ + lis r3,86f@h + ori r3,r3,86f@l + mr r4,r2 /* current */ + lwz r5,_NIP(r1) + bl printk + b int_return +86: .string "floating point used in kernel (task=%p, pc=%x)\n" + .align 4 + +/* + * Disable FP for the task which had the FPU previously, + * and save its floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + * (If giveup_fpu_unmapped uses any integer registers other than + * r3 - r6, the return code at load_up_fpu above will have + * to be adjusted.) + */ +giveup_fpu_unmapped: + lis r6,-KERNELBASE@h + b 1f + + .globl giveup_fpu +giveup_fpu: + li r6,0 +1: + addis r3,r6,last_task_used_math@h/*a*/ + lwz r4,last_task_used_math@l(r3) +#if 0 + addis r3,r6,fpu_tss@ha + lwz r4,fpu_tss@l(r3) #endif - RETURN_FROM_INT(0x0D00) - -/* - * Floating point [not available, etc] - */ -FloatingPointCheck: - SAVE_INT_REGS(0x0800) - mr r3,r1 /* Set pointer to saved regs */ - bl _EXTERN(FloatingPointCheckException) - cmpi 0,r3,MSR_FP /* check if fp was turned on by handler */ - bne 00f - RETURN_FROM_INT(0x0f0f) /* 0xf0f tells to restore fp regs */ -00: RETURN_FROM_INT(0x0200) + mfmsr r5 + ori r5,r5,MSR_FP + SYNC + mtmsr r5 /* enable use of fpu now */ + SYNC + cmpi 0,r4,0 + add r4,r4,r6 + beqlr /* if no previous owner, done */ + addi r4,r4,TSS /* want TSS of last_task_used_math */ + li r5,0 + stw r5,last_task_used_math@l(r3) +#if 0 + stw r5,fpu_tss@l(r3) +#endif + SAVE_32FPRS(0, r4) + mffs fr0 + stfd fr0,TSS_FPSCR-4(r4) + lwz r5,PT_REGS(r4) + lwz r5,PT_REGS(r4) + add r5,r5,r6 + lwz r3,_MSR-STACK_FRAME_OVERHEAD(r5) + li r4,MSR_FP + andc r3,r3,r4 /* disable FP for previous task */ + stw r3,_MSR-STACK_FRAME_OVERHEAD(r5) + blr /* - * System Call exception + * Handle a system call. */ -SystemCall: - SAVE_INT_REGS(0x0C00) - lwz r2,_CCR(r1) /* Clear SO bit in CR */ - lis r9,0x1000 - andc r2,r2,r9 - stw r2,_CCR(r1) +DoSyscall: + stw r0,TSS+LAST_SYSCALL(r2) + lwz r11,_CCR(r1) /* Clear SO bit in CR */ + lis r10,0x1000 + andc r11,r11,r10 + stw r11,_CCR(r1) +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + lis r31,show_syscalls_task@ha + lwz r31,show_syscalls_task@l(r31) + cmp 0,r2,r31 + bne 1f +#endif + lis r3,7f@ha + addi r3,r3,7f@l + lwz r4,GPR0(r1) + lwz r5,GPR3(r1) + lwz r6,GPR4(r1) + lwz r7,GPR5(r1) + lwz r8,GPR6(r1) + mr r9,r2 + bl printk + lwz r0,GPR0(r1) + lwz r3,GPR3(r1) + lwz r4,GPR4(r1) + lwz r5,GPR5(r1) + lwz r6,GPR6(r1) + lwz r7,GPR7(r1) + lwz r8,GPR8(r1) +1: +#endif /* SHOW_SYSCALLS */ cmpi 0,r0,0x7777 /* Special case for 'sys_sigreturn' */ - bne+ 10f - mr r3,r1 - bl _EXTERN(sys_sigreturn) - cmpi 0,r3,0 /* Check for restarted system call */ - bge 99f - b 20f -10: lis r2,current_set@ha - lwz r2,current_set@l(r2) - lwz r2,TASK_FLAGS(r2) - andi. r2,r2,PF_TRACESYS - bne 50f - - lis r2,sys_call_table@h - ori r2,r2,sys_call_table@l + beq- 10f + lwz r10,TASK_FLAGS(r2) + andi. r10,r10,PF_TRACESYS + bne- 50f + cmpli 0,r0,NR_syscalls + bge- 66f + lis r10,sys_call_table@h + ori r10,r10,sys_call_table@l slwi r0,r0,2 - lwzx r2,r2,r0 /* Fetch system call handler [ptr] */ -#if 1 - cmpi 0,r2,0 /* make sure syscall handler not 0 */ - beq 99f - cmpi 0,r0,NR_syscalls<<2 /* make sure syscallnum in bounds */ - bgt 99f -#endif - mtlr r2 - mr r9,r1 + lwzx r10,r10,r0 /* Fetch system call handler [ptr] */ + cmpi 0,r10,0 + beq- 66f + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD blrl /* Call handler */ - -20: stw r3,RESULT(r1) /* Save result */ - cmpi 0,r3,0 - bge 30f + .globl syscall_ret_1 +syscall_ret_1: +20: stw r3,RESULT(r1) /* Save result */ +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + cmp 0,r2,r31 + bne 91f +#endif + mr r4,r3 + lis r3,79f@ha + addi r3,r3,79f@l + bl printk + lwz r3,RESULT(r1) +91: +#endif + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 30f neg r3,r3 cmpi 0,r3,ERESTARTNOHAND bne 22f li r3,EINTR -22: lwz r2,_CCR(r1) /* Set SO bit in CR */ - oris r2,r2,0x1000 - stw r2,_CCR(r1) +22: lwz r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + stw r10,_CCR(r1) 30: stw r3,GPR3(r1) /* Update return value */ - b 99f + b int_return +66: li r3,ENOSYS + b 22b +/* sys_sigreturn */ +10: addi r3,r1,STACK_FRAME_OVERHEAD + bl _EXTERN(sys_sigreturn) + cmpi 0,r3,0 /* Check for restarted system call */ + bge int_return + b 20b /* Traced system call support */ 50: bl _EXTERN(syscall_trace) lwz r0,GPR0(r1) /* Restore original registers */ @@ -1045,369 +1149,371 @@ lwz r7,GPR7(r1) lwz r8,GPR8(r1) lwz r9,GPR9(r1) - lis r2,sys_call_table@h - ori r2,r2,sys_call_table@l + cmpli 0,r0,NR_syscalls + bge- 66f + lis r10,sys_call_table@h + ori r10,r10,sys_call_table@l slwi r0,r0,2 - lwzx r2,r2,r0 /* Fetch system call handler [ptr] */ - mtlr r2 - mr r9,r1 + lwzx r10,r10,r0 /* Fetch system call handler [ptr] */ + cmpi 0,r10,0 + beq- 66f + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD blrl /* Call handler */ + .globl syscall_ret_2 +syscall_ret_2: stw r3,RESULT(r1) /* Save result */ - cmpi 0,r3,0 - bge 60f + stw r3,GPR0(r1) /* temporary gross hack to make strace work */ + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 60f neg r3,r3 cmpi 0,r3,ERESTARTNOHAND bne 52f li r3,EINTR -52: lwz r2,_CCR(r1) /* Set SO bit in CR */ - oris r2,r2,0x1000 - stw r2,_CCR(r1) +52: lwz r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + stw r10,_CCR(r1) 60: stw r3,GPR3(r1) /* Update return value */ bl _EXTERN(syscall_trace) -99: - RETURN_FROM_INT(0x0C00) - -/* - * Handle TLB miss for instruction - */ -InstructionTLBMiss: - BUMP_UNMAPPED(__Instruction_TLB_Misses) - mfctr r0 /* Need to save this - CTR can't be touched! */ - mfspr r2,HASH1 /* Get PTE pointer */ - mfspr r3,ICMP /* Partial item compare value */ -00: li r1,8 /* 8 items / bucket */ - mtctr r1 - subi r2,r2,8 /* Preset pointer */ -10: lwzu r1,8(r2) /* Get next PTE */ - cmp 0,r1,r3 /* Found entry yet? */ - bdne 10b /* Jump back if not, until CTR==0 */ - bne 30f /* Try secondary hash if CTR==0 */ - lwz r1,4(r2) /* Get second word of entry */ -#if 0 - andi. r3,r1,0x08 /* Check guard bit - invalid access if set */ - bne InstructionFetchError -#endif - andi. r3,r1,0x100 /* Check R bit (referenced) */ - bne 20f /* If set, all done */ - ori r1,r1,0x100 /* Set bit */ - stw r1,4(r2) /* Update memory image */ -20: mtctr r0 /* Restore CTR */ - mfspr r3,SRR1 /* Need to restore CR0 */ - mtcrf 0x80,r3 - mfspr r0,IMISS /* Set to update TLB */ - mtspr RPA,r1 - tlbli r0 -#if 0 - SYNC + b int_return +66: li r3,ENOSYS + b 52b +#ifdef SHOW_SYSCALLS +7: .string "syscall %d(%x, %x, %x, %x), current=%p\n" +79: .string " -> %x\n" + .align 2 #endif - rfi /* All done */ -/* Secondary hash */ -30: andi. r1,r3,0x40 /* Already doing secondary hash? */ - bne InstructionAddressInvalid /* Yes - item not in hash table */ - mfspr r2,HASH2 /* Get hash table pointer */ - ori r3,r3,0x40 /* Set secondary hash */ - b 00b /* Try lookup again */ /* - * Handle TLB miss for DATA Load operation + * This routine switches between two different tasks. The process + * state of one is saved on its kernel stack. Then the state + * of the other is restored from its kernel stack. The memory + * management hardware is updated to the second process's state. + * Finally, we can return to the second process, via int_return. + * On entry, r3 points to the TSS for the current task, r4 + * points to the TSS for the new task, and r5 contains the + * MMU context number for the new task. + * + * Note: there are two ways to get to the "going out" portion + * of this code; either by coming in via the entry (_switch) + * or via "fork" which must set up an environment equivalent + * to the "_switch" path. If you change this (or in particular, the + * SAVE_REGS macro), you'll have to change the fork code also. + * + * The code which creates the new task context is in 'copy_thread' + * in arch/ppc/kernel/process.c */ -DataLoadTLBMiss: - mfctr r0 /* Need to save this - CTR can't be touched! */ - mfspr r2,HASH1 /* Get PTE pointer */ - mfspr r3,DCMP /* Partial item compare value */ -00: li r1,8 /* 8 items / bucket */ - mtctr r1 - subi r2,r2,8 /* Preset pointer */ -10: lwzu r1,8(r2) /* Get next PTE */ - cmp 0,r1,r3 /* Found entry yet? */ - bdne 10b /* Jump back if not, until CTR==0 */ - bne 30f /* Try secondary hash if CTR==0 */ - lwz r1,4(r2) /* Get second word of entry */ - andi. r3,r1,0x100 /* Check R bit (referenced) */ - ori r1,r1,0x100 /* Set bit */ - bne 20f /* If set, all done */ - stw r1,4(r2) /* Update memory image */ -20: mtctr r0 /* Restore CTR */ - mfspr r3,SRR1 /* Need to restore CR0 */ - mtcrf 0x80,r3 - mfspr r0,DMISS /* Set to update TLB */ - mtspr RPA,r1 -/* SYNC() */ - tlbld r0 -#if 0 +_GLOBAL(_switch) + stwu r1,-INT_FRAME_SIZE-STACK_UNDERHEAD(r1) + stw r0,GPR0(r1) + lwz r0,0(r1) + stw r0,GPR1(r1) + SAVE_10GPRS(2, r1) + SAVE_10GPRS(12, r1) + SAVE_10GPRS(22, r1) + mflr r20 /* Return to switch caller */ + mfmsr r22 + li r0,MSR_FP /* Disable floating-point */ + andc r22,r22,r0 + stw r20,_NIP(r1) + stw r22,_MSR(r1) + stw r20,_LINK(r1) + mfcr r20 + mfctr r22 + mfspr r23,XER + stw r20,_CCR(r1) + stw r22,_CTR(r1) + stw r23,_XER(r1) + li r0,0x0ff0 + stw r0,TRAP(r1) + stw r1,KSP(r3) /* Set old stack pointer */ + sync + addis r0,r4,-KERNELBASE@h + mtspr SPRG3,r0 /* Update current TSS phys addr */ SYNC -#endif - rfi /* All done */ -/* Secondary hash */ -30: andi. r1,r3,0x40 /* Already doing secondary hash? */ - bne DataAddressInvalid /* Yes - item not in hash table */ - mfspr r2,HASH2 /* Get hash table pointer */ - ori r3,r3,0x40 /* Set secondary hash */ - b 00b /* Try lookup again */ + lwz r1,KSP(r4) /* Load new stack pointer */ + addi r2,r4,-TSS /* Update current */ + /* Set up segment registers for new task */ + rlwinm r5,r5,4,8,27 /* VSID = context << 4 */ + addis r5,r5,0x6000 /* Set Ks, Ku bits */ + li r0,8 /* TASK_SIZE / SEGMENT_SIZE */ + mtctr r0 + li r3,0 +3: mtsrin r5,r3 + addi r5,r5,1 /* next VSID */ + addis r3,r3,0x1000 /* address of next segment */ + bdnz 3b + SYNC + +/* FALL THROUGH into int_return */ /* - * Handle TLB miss for DATA STORE + * Trap exit. */ -DataStoreTLBMiss: - BUMP_UNMAPPED(__DataStore_TLB_Misses) - mfctr r0 /* Need to save this - CTR can't be touched! */ - mfspr r2,HASH1 /* Get PTE pointer */ - mfspr r3,DCMP /* Partial item compare value */ -00: li r1,8 /* 8 items / bucket */ - mtctr r1 - subi r2,r2,8 /* Preset pointer */ -10: lwzu r1,8(r2) /* Get next PTE */ - cmp 0,r1,r3 /* Found entry yet? */ - bdne 10b /* Jump back if not, until CTR==0 */ - bne 30f /* Try secondary hash if CTR==0 */ - lwz r1,4(r2) /* Get second word of entry */ - andi. r3,r1,0x80 /* Check C bit (changed) */ -#if 0 /* Note: no validation */ - beq 40f /* If not set (first time) validate access */ -#else - ori r1,r1,0x180 /* Set changed, accessed */ - bne 20f - stw r1,4(r2) -#endif -20: mtctr r0 /* Restore CTR */ - mfspr r3,SRR1 /* Need to restore CR0 */ - mtcrf 0x80,r3 - mfspr r0,DMISS /* Set to update TLB */ - mtspr RPA,r1 - tlbld r0 -#if 0 + .globl int_return +int_return: +0: mfmsr r30 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r30,r30,r4 + SYNC /* Some chip revs need this... */ + mtmsr r30 SYNC -#endif - rfi /* All done */ -/* Secondary hash */ -30: andi. r1,r3,0x40 /* Already doing secondary hash? */ - bne DataAddressInvalid /* Yes - item not in hash table */ - mfspr r2,HASH2 /* Get hash table pointer */ - ori r3,r3,0x40 /* Set secondary hash */ - b 00b /* Try lookup again */ -/* PTE found - validate access */ -40: rlwinm. r3,r1,30,0,1 /* Extract PP bits */ - bge- 50f /* Jump if PP=0,1 */ - andi. r3,r1,1 - beq+ 70f /* Access OK */ - b WriteProtectError /* Not OK - fail! */ -50: mfspr r3,SRR1 /* Check privilege */ + lwz r5,_MSR(r1) + and. r5,r5,r4 + beq 2f +3: lis r4,lost_interrupts@ha + lwz r4,lost_interrupts@l(r4) + cmpi 0,r4,0 + beq+ 1f + addi r3,r1,STACK_FRAME_OVERHEAD + bl handle_IRQ + b 3b +1: lis r4,bh_mask@ha + lwz r4,bh_mask@l(r4) + lis r5,bh_active@ha + lwz r5,bh_active@l(r5) + and. r4,r4,r5 + beq+ 2f + ori r31,r30,MSR_EE /* re-enable interrupts */ + SYNC + mtmsr r31 + SYNC + bl _EXTERN(do_bottom_half) + SYNC + mtmsr r30 /* disable interrupts again */ + SYNC +2: lwz r3,_MSR(r1) /* Returning to user mode? */ andi. r3,r3,MSR_PR - beq+ 60f /* Jump if supervisor mode */ - mfspr r3,DMISS /* Get address */ - mfsrin r3,r3 /* Get segment register */ - andis. r3,r3,0x2000 /* If Kp==0, OK */ - beq+ 70f - b WriteProtectError /* Bad access */ -60: mfspr r3,DMISS /* Get address */ - mfsrin r3,r3 /* Get segment register */ - andis. r3,r3,0x4000 /* If Ks==0, OK */ - beq+ 70f - b WriteProtectError /* Bad access */ -70: ori r1,r1,0x180 /* Set changed, accessed */ - stw r1,4(r2) /* Update PTE in memory */ - b 20b - -/* - * These routines are error paths/continuations of the exception - * handlers above. They are placed here to avoid the problems - * of only 0x100 bytes per exception handler. - */ - -/* Invalid address */ -InstructionAddressInvalid: - mfspr r3,SRR1 - rlwinm r1,r3,9,6,6 /* Get load/store bit */ - addis r1,r1,0x4000 /* Set bit 1 -> PTE not found */ - b 10f - -/* Fetch from guarded or no-access page */ -InstructionFetchError: - mfspr r3,SRR1 - rlwinm r1,r3,9,6,6 /* Get load/store bit */ - addis r1,r1,0x0800 /* Set bit 4 -> protection error */ -10: mtspr DSISR,r1 - mtctr r0 /* Restore CTR */ - andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ - mtspr SRR1,r2 - mfspr r1,IMISS /* Get failing address */ - rlwinm. r2,r2,0,31,31 /* Check for little endian access */ - beq 20f /* Jump if big endian */ - xori r1,r1,3 -20: mtspr DAR,r1 /* Set fault address */ - mfmsr r0 /* Restore "normal" registers */ - xoris r0,r0,MSR_TGPR>>16 - mtcrf 0x80,r3 /* Restore CR0 */ - ori r0,r0,MSR_FP /* Need to keep FP enabled */ - sync /* Some chip revs have problems here... */ - mtmsr r0 - b InstructionAccess + beq+ 10f /* no - no need to mess with stack */ + lis r3,need_resched@ha + lwz r3,need_resched@l(r3) + cmpi 0,r3,0 /* check need_resched flag */ + beq+ 7f + bl _EXTERN(schedule) + b 0b +7: lwz r3,BLOCKED(r2) /* Check for pending unblocked signals */ + lwz r5,SIGNAL(r2) + andc. r0,r5,r3 /* Lets thru any unblocked */ + beq+ 8f + addi r4,r1,STACK_FRAME_OVERHEAD + bl _EXTERN(do_signal) + b 0b +8: addi r4,r1,INT_FRAME_SIZE+STACK_UNDERHEAD /* size of frame */ + stw r4,TSS+KSP(r2) /* save kernel stack pointer */ +10: + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi -/* Invalid address */ -DataAddressInvalid: - mfspr r3,SRR1 - rlwinm r1,r3,9,6,6 /* Get load/store bit */ - addis r1,r1,0x4000 /* Set bit 1 -> PTE not found */ - b 10f +/* + * Fake an interrupt from kernel mode. + * This is used when enable_irq loses an interrupt. + * We only fill in the stack frame minimally. + */ +_GLOBAL(fake_interrupt) + mflr r0 + stw r0,4(r1) + stwu r1,-INT_FRAME_SIZE-STACK_UNDERHEAD(r1) + stw r0,_NIP(r1) + stw r0,_LINK(r1) + mfmsr r3 + stw r3,_MSR(r1) + li r0,0x0fac + stw r0,TRAP(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + bl handle_IRQ + addi r1,r1,INT_FRAME_SIZE+STACK_UNDERHEAD + lwz r0,4(r1) + mtlr r0 + blr -/* Write to read-only space */ -WriteProtectError: - mfspr r3,SRR1 - rlwinm r1,r3,9,6,6 /* Get load/store bit */ - addis r1,r1,0x0800 /* Set bit 4 -> protection error */ -10: mtspr DSISR,r1 - mtctr r0 /* Restore CTR */ - andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ - mtspr SRR1,r2 - mfspr r1,DMISS /* Get failing address */ - rlwinm. r2,r2,0,31,31 /* Check for little endian access */ - beq 20f /* Jump if big endian */ - xori r1,r1,3 -20: mtspr DAR,r1 /* Set fault address */ - mfmsr r0 /* Restore "normal" registers */ - xoris r0,r0,MSR_TGPR>>16 - mtcrf 0x80,r3 /* Restore CR0 */ - ori r0,r0,MSR_FP /* Need to keep FP enabled */ - sync /* Some chip revs have problems here... */ - mtmsr r0 - b DataAccess +/* + * Set up the segment registers for a new context. + */ +_GLOBAL(set_context) + rlwinm r3,r3,4,8,27 /* VSID = context << 4 */ + addis r3,r3,0x6000 /* Set Ks, Ku bits */ + li r0,8 /* TASK_SIZE / SEGMENT_SIZE */ + mtctr r0 + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,1 /* next VSID */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b + SYNC + blr /* - * Flush instruction cache - * *** I'm really paranoid here! + * Flush instruction cache. + * This is a no-op on the 601. */ _GLOBAL(flush_instruction_cache) - mflr r5 - bl _EXTERN(flush_data_cache) - mfspr r3,HID0 /* Caches are controlled by this register */ - li r4,0 - ori r4,r4,(HID0_ICE|HID0_ICFI) - or r3,r3,r4 /* Need to enable+invalidate to clear */ + mfspr r3,PVR + rlwinm r3,r3,16,16,31 + cmpi 0,r3,1 + beqlr /* for 601, do nothing */ + /* 603/604 processor - use invalidate-all bit in HID0 */ + mfspr r3,HID0 + ori r3,r3,HID0_ICFI mtspr HID0,r3 - andc r3,r3,r4 - ori r3,r3,HID0_ICE /* Enable cache */ - mtspr HID0,r3 - mtlr r5 + SYNC blr /* - * Flush data cache - * *** I'm really paranoid here! + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * This is a no-op on the 601. + * + * store_cache_range(unsigned long start, unsigned long stop) */ -_GLOBAL(flush_data_cache) - BUMP(__Cache_Flushes) - lis r3,cache_is_copyback@ha - lwz r3,cache_is_copyback@l(r3) - cmpi 0,r3,0 - beq 10f -/* When DATA CACHE is copy-back */ - lis r3,cache_flush_buffer@h - ori r3,r3,cache_flush_buffer@l - li r4,NUM_CACHE_LINES +CACHE_LINE_SIZE = 32 +LG_CACHE_LINE_SIZE = 5 +_GLOBAL(store_cache_range) + mfspr r5,PVR + rlwinm r5,r5,16,16,31 + cmpi 0,r5,1 + beqlr /* for 601, do nothing */ + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ mtctr r4 -00: dcbz 0,r3 /* Flush cache line with minimal BUS traffic */ - addi r3,r3,CACHE_LINE_SIZE /* Next line, please */ - bdnz 00b -10: blr +2: icbi 0,r6 + addi r6,r6,CACHE_LINE_SIZE + bdnz 2b + sync + isync + blr /* * Flush a particular page from the DATA cache * Note: this is necessary because the instruction cache does *not* * snoop from the data cache. - * void flush_page(void *page) + * This is a no-op on the 601 which has a unified cache. + * + * void flush_page_to_ram(void *page) */ -_GLOBAL(flush_page) +_GLOBAL(flush_page_to_ram) + mfspr r5,PVR + rlwinm r5,r5,16,16,31 + cmpi 0,r5,1 + beqlr /* for 601, do nothing */ li r4,0x0FFF andc r3,r3,r4 /* Get page base address */ li r4,4096/CACHE_LINE_SIZE /* Number of lines in a page */ mtctr r4 -00: dcbf 0,r3 /* Clear line */ - icbi 0,r3 + mr r6,r3 +0: dcbst 0,r3 /* Write line to ram */ addi r3,r3,CACHE_LINE_SIZE - bdnz 00b + bdnz 0b + sync + mtctr r4 +1: icbi 0,r6 + addi r6,r6,CACHE_LINE_SIZE + bdnz 1b + sync + isync blr /* - * This routine switches between two different tasks. The process - * state of one is saved on its kernel stack. Then the state - * of the other is restored from its kernel stack. The memory - * management hardware is updated to the second process's state. - * Finally, we can return to the second process, via the 'return'. - * - * Note: there are two ways to get to the "going out" portion - * of this code; either by coming in via the entry (_switch) - * or via "fork" which must set up an environment equivalent - * to the "_switch" path. If you change this (or in particular, the - * SAVE_ALL_REGS macro), you'll have to change the fork code also. + * Flush entries from the hash table with VSIDs in the range + * given. + */ +_GLOBAL(flush_hash_segments) + rlwinm r3,r3,7,1,24 /* put VSID lower limit in position */ + oris r3,r3,0x8000 /* set V bit */ + rlwinm r4,r4,7,1,24 /* put VSID upper limit in position */ + oris r4,r4,0x8000 + ori r4,r4,0x7f + lis r5,Hash@ha + lwz r5,Hash@l(r5) /* base of hash table */ + lis r6,Hash_size@ha + lwz r6,Hash_size@l(r6) /* size in bytes */ + srwi r6,r6,3 /* # PTEs */ + mtctr r6 + addi r5,r5,-8 + li r0,0 +1: lwzu r6,8(r5) /* get next tag word */ + cmplw 0,r6,r3 + cmplw 1,r6,r4 + cror 0,0,5 /* set cr0.lt if out of range */ + blt 2f /* branch if out of range */ + stw r0,0(r5) /* invalidate entry */ +2: bdnz 1b /* continue with loop */ + sync + tlbia + isync + blr + +/* + * Flush the entry for a particular page from the hash table. * - * The code which creates the new task context is in 'copy_thread' - * in arch/ppc/kernel/process.c - */ -_GLOBAL(_switch) - mtspr SPR0,r1 /* SAVE_ALL_REGS prologue */ - mtspr SPR1,r2 - mflr r2 /* Return to switch caller */ - mtspr SPR2,r2 - mfmsr r2 - mtspr SPR3,r2 - SAVE_ALL_REGS(0x0FF0) - SAVE_FP_REGS() - CHECK_STACK() - SYNC() - stw r1,KSP(r3) /* Set old stack pointer */ - BUMP(__Context_Switches) - lwz r1,KSP(r4) /* Load new stack pointer */ - lwz r0,MMU_SEG0(r4) - mtsr SR0,r0 - lwz r0,MMU_SEG1(r4) - mtsr SR1,r0 - lwz r0,MMU_SEG2(r4) - mtsr SR2,r0 - lwz r0,MMU_SEG3(r4) - mtsr SR3,r0 - lwz r0,MMU_SEG4(r4) - mtsr SR4,r0 - lwz r0,MMU_SEG5(r4) - mtsr SR5,r0 - lwz r0,MMU_SEG6(r4) - mtsr SR6,r0 - lwz r0,MMU_SEG7(r4) - mtsr SR7,r0 -#if 0 - /* segs 8-15 are shared by everyone -- don't need to be changed */ - lwz r0,MMU_SEG8(r4) - mtsr SR8,r0 - lwz r0,MMU_SEG9(r4) - mtsr SR9,r0 - lwz r0,MMU_SEG10(r4) - mtsr SR10,r0 - lwz r0,MMU_SEG11(r4) - mtsr SR11,r0 - lwz r0,MMU_SEG12(r4) - mtsr SR12,r0 - lwz r0,MMU_SEG13(r4) - mtsr SR13,r0 - lwz r0,MMU_SEG14(r4) - mtsr SR14,r0 - lwz r0,MMU_SEG15(r4) - mtsr SR15,r0 -#endif - /* no need to invalidate tlb since each process has a distinct - set of vsid's. -- Cort */ -#if 0 - tlbia /* Invalidate entire TLB */ - BUMP(__TLBIAs) -#endif - /* p5.2 603 users manual - with addr transl. enabled, - the memory access is performed under the control of - the page table entry. I interpret this to mean that - it is tagged with the vsid -- so no need to flush here - since each process has a distinct set of vsid's. - Of course, my intepretation may be wrong. - -- Cort */ - /*bl _EXTERN(flush_instruction_cache)*/ - RETURN_FROM_INT(0x0f0f) - + * flush_hash_page(unsigned context, unsigned long va) + */ +_GLOBAL(flush_hash_page) + rlwinm r3,r3,11,1,20 /* put context into vsid */ + rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */ + oris r3,r3,0x8000 /* set V (valid) bit */ + rlwimi r3,r4,10,26,31 /* put in API (abbrev page index) */ + rlwinm r7,r4,32-6,10,25 /* get page index << 6 */ + rlwinm r5,r3,32-1,7,25 /* vsid << 6 */ + xor r7,r7,r5 /* primary hash << 6 */ + lis r5,Hash_mask@ha + lwz r5,Hash_mask@l(r5) /* hash mask */ + slwi r5,r5,6 /* << 6 */ + and r7,r7,r5 + lis r6,Hash@ha + lwz r6,Hash@l(r6) /* hash table base */ + add r6,r6,r7 /* address of primary PTEG */ + li r8,8 + mtctr r8 + addi r7,r6,-8 +1: lwzu r0,8(r7) /* get next PTE */ + cmpw 0,r0,r3 /* see if tag matches */ + bdnzf 2,1b /* while --ctr != 0 && !cr0.eq */ + beq 3f /* if we found it */ + ori r3,r3,0x40 /* set H (alt. hash) bit */ + xor r6,r6,r5 /* address of secondary PTEG */ + mtctr r8 + addi r7,r6,-8 +2: lwzu r0,8(r7) /* get next PTE */ + cmpw 0,r0,r3 /* see if tag matches */ + bdnzf 2,2b /* while --ctr != 0 && !cr0.eq */ + bne 4f /* if we didn't find it */ +3: li r0,0 + stw r0,0(r7) /* invalidate entry */ +4: sync + tlbie r4 /* in hw tlb too */ + isync + blr /* * This routine is just here to keep GCC happy - sigh... @@ -1415,18 +1521,212 @@ _GLOBAL(__main) blr +#ifdef CONFIG_PMAC +/* + * These exception handlers are used when we have called a prom + * routine after we have taken over the exception vectors and MMU. + */ + .globl prom_exc_table +prom_exc_table: + .long TOPHYS(prom_exception) /* 0 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 400 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 800 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* c00 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 1000 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 1400 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 1800 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 1c00 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 1000 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 1400 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 1800 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) /* 1c00 */ + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + .long TOPHYS(prom_exception) + +/* + * When we come in to these prom exceptions, r1 and lr have been + * saved in sprg1 and sprg2, and lr points to a word containing + * the vector offset. + */ +prom_exception: + mr r1,r21 /* save r21 */ + lis r21,prom_sp@ha /* get a stack to use */ + addis r21,r21,-KERNELBASE@h + lwz r21,prom_sp@l(r21) + addis r21,r21,-KERNELBASE@h /* convert to physical addr */ + subi r21,r21,INT_FRAME_SIZE+STACK_UNDERHEAD + stw r0,GPR0(r21) + stw r2,GPR2(r21) + stw r3,GPR3(r21) + stw r4,GPR4(r21) + stw r5,GPR5(r21) + stw r6,GPR6(r21) + stw r20,GPR20(r21) + stw r1,GPR21(r21) + stw r22,GPR22(r21) + stw r23,GPR23(r21) + mfspr r1,SPRG1 + stw r1,GPR1(r21) + mfcr r3 + mfspr r4,SPRG2 + stw r3,_CCR(r21) + stw r4,_LINK(r21) + mfctr r3 + mfspr r4,XER + stw r3,_CTR(r21) + stw r4,_XER(r21) + mfspr r22,SRR0 + mfspr r23,SRR1 + + /* at this point we have set things up pretty much exactly + how EXCEPTION_PROLOG does */ + mflr r3 + lwz r3,0(r3) /* get exception vector */ + stw r3,TRAP(r21) + cmpi 0,r3,0x300 /* was it a dsi? */ + bne 1f + + mfspr r20,DSISR /* here on data access exc. */ + andis. r0,r20,0x8470 /* weird error? */ + bne 3f /* if not, try to put a PTE */ + mfspr r3,DAR /* into the hash table */ + rlwinm r4,r23,32-13,30,30 /* MSR_PR -> _PAGE_USER */ + rlwimi r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ + b 2f + +1: cmpi 0,r3,0x400 /* was it an isi? */ + bne 3f + andis. r0,r23,0x4000 /* if so, check if no pte found */ + beq 3f /* if so, try to put a PTE */ + mr r3,r22 /* into the hash table */ + rlwinm r4,r23,32-13,30,30 /* MSR_PR -> _PAGE_USER */ + mr r20,r23 /* SRR1 has reason bits */ +2: lis r5,prom_tss@ha /* phys addr of TSS */ + addis r5,r5,-KERNELBASE@h + lwz r5,prom_tss@l(r5) + bl hash_page + +3: addis r1,r21,KERNELBASE@h /* restore kernel stack ptr */ + addi r3,r1,INT_FRAME_SIZE+STACK_UNDERHEAD + stw r3,0(r21) /* set stack chain pointer */ + lis r5,prom_tss@ha + addis r5,r5,-KERNELBASE@h + lwz r5,prom_tss@l(r5) + mtspr SPRG3,r5 /* reset phys TSS pointer */ + lwz r4,TRAP(r21) /* the real exception vector */ + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + bl transfer_to_handler + .long PromException + .long prom_int_return + + .comm prom_sp,4 + .comm prom_tss,4 + + .globl prom_int_return +prom_int_return: + lis r3,prom_exc_table@ha /* restore sprg3 for prom vectors */ + addi r3,r3,prom_exc_table@l + addis r3,r3,-KERNELBASE@h + mtspr SPRG3,r3 + b int_return + +/* + * When entering the prom, we have to change to using a different + * set of exception vectors. + */ + .globl enter_prom +enter_prom: + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + stw r29,20(r1) + stw r30,24(r1) + stw r31,28(r1) + lis r8,prom_entry@ha + lwz r8,prom_entry@l(r8) + mfmsr r31 + andi. r0,r31,MSR_IP /* using our own vectors yet? */ + beq 1f /* if so, have to switch */ + mtlr r8 + blrl /* if not, can just charge ahead */ + b 2f +1: lis r9,prom_sp@ha /* save sp for exception handler */ + stw r1,prom_sp@l(r9) + mfspr r29,SPRG3 /* save physical tss pointer */ + lis r9,prom_tss@ha + stw r29,prom_tss@l(r9) + li r9,0 + ori r9,r9,MSR_EE + andc r30,r31,r9 + lis r9,prom_exc_table@ha /* set pointer to exception table */ + addi r9,r9,prom_exc_table@l + addis r9,r9,-KERNELBASE@h + ori r0,r31,MSR_IP + sync + mtmsr r30 /* disable interrupts */ + mtspr SPRG3,r9 /* while we update MSR_IP and sprg3 */ + sync + mtmsr r0 /* start using exc. vectors in prom */ + mtlr r8 + blrl /* call prom */ + sync + mtmsr r30 /* disable interrupts again */ + mtspr SPRG3,r29 /* while we restore MSR_IP and sprg3 */ + sync + mtmsr r31 /* reenable interrupts */ +2: lwz r0,36(r1) + mtlr r0 + lwz r29,20(r1) + lwz r30,24(r1) + lwz r31,28(r1) + lwz r1,0(r1) + blr +#endif + +/* + * We put a few things here that have to be page-aligned. + * This stuff goes at the beginning of the data segment, + * which is page-aligned. + */ .data .globl sdata sdata: .space 2*4096 - -#if 0 -_GLOBAL(sys_stack) -sys_stack: - .space 4096 -#endif -CPU1_stack: - .globl empty_zero_page empty_zero_page: .space 4096 @@ -1435,6 +1735,7 @@ swapper_pg_dir: .space 4096 +#ifdef CONFIG_PREP /* * This space gets a copy of optional info passed to us by the bootstrap * Used to pass parameters into the kernel like root=/dev/sda1, etc. @@ -1442,54 +1743,5 @@ .globl cmd_line cmd_line: .space 512 - -#ifdef STATS -/* - * Miscellaneous statistics - gathered just for performance info - */ - .globl _INTR_stats -_INTR_stats: - .globl __Instruction_TLB_Misses -__Instruction_TLB_Misses: - .long 0,0 /* Instruction TLB misses */ - .globl __DataLoad_TLB_Misses -__DataLoad_TLB_Misses: - .long 0,0 /* Data [load] TLB misses */ - .globl __DataStore_TLB_Misses -__DataStore_TLB_Misses: - .long 0,0 /* Data [store] TLB misses */ - .globl __Instruction_Page_Faults -__Instruction_Page_Faults: - .long 0,0 /* Instruction page faults */ - .globl __Data_Page_Faults -__Data_Page_Faults: - .long 0,0 /* Data page faults */ - .globl __Cache_Flushes -__Cache_Flushes: - .long 0,0 /* Explicit cache flushes */ - .globl __Context_Switches -__Context_Switches: - .long 0,0 /* Context switches */ - .globl __Hardware_Interrupts -__Hardware_Interrupts: - .long 0,0 /* I/O interrupts (disk, timer, etc) */ - .globl __TLBIAs - .globl __TLBIAs -__TLBIAs: - .long 0,0 /* TLB cache forceably flushed */ - .globl __TLBIEs -__TLBIEs: - .long 0,0 /* Specific TLB entry flushed */ -#endif - - .globl _TotalMemory -_TotalMemory: - .long 0,0 - -/* - * This location is used to break any outstanding "lock"s when - * changing contexts. - */ -_break_lwarx: .long 0 - +#endif /* CONFIG_PREP */ diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c --- v2.1.47/linux/arch/ppc/kernel/irq.c Mon Apr 14 16:28:06 1997 +++ linux/arch/ppc/kernel/irq.c Thu Jul 31 13:09:17 1997 @@ -1,9 +1,13 @@ /* - * linux/arch/ppc/kernel/irq.c + * arch/ppc/kernel/irq.c * - * Copyright (C) 1992 Linus Torvalds - * Adapted from arch/i386 by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) + * Power Macintosh version + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * + * Derived from arch/i386/kernel/irq.c + * Copyright (C) 1992 Linus Torvalds + * Adapted from arch/i386 by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) * * This file contains the code used by various IRQ handling routines: * asking for different IRQ's should be done through these routines @@ -11,7 +15,7 @@ * shouldn't result in any weird surprises, and installing new handlers * should be easier. */ - + /* * IRQ's are in fact implemented a bit like signal handlers for the kernel. * Naturally it's not a 1:1 relation, but there are similarities. @@ -25,323 +29,300 @@ #include #include #include +#include #include #include #include #include -inline int get_irq_list(char *); -void check_irq(void); -void BeBox_CPU1(void); -void BeBox_state(void); -int BeBox_irq(void); -void show_BeBox_state(void); -void BeBox_enable_irq(int ); -void BeBox_disable_irq(int ); -void BeBox_init_IRQ(void); -void _do_bottom_half(void); -static _NOP(void); -static _delay(void); -void hard_disk_LED(int state); +#define IRQ_FLAG ((unsigned *)0xf3000020) +#define IRQ_ENABLE ((unsigned *)0xf3000024) +#define IRQ_ACK ((unsigned *)0xf3000028) +#define IRQ_LEVEL ((unsigned *)0xf300002c) + +#define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */ +#undef SHOW_IRQ 1 -#define SHOW_IRQ -#undef SHOW_IRQ +unsigned lost_interrupts = 0; + +unsigned int local_irq_count[NR_CPUS]; +static struct irqaction irq_action[32]; /* - * For the BeBox, interrupt numbers are 0..15 for 8259 PIC interrupts - * and 16..31 for other BeBox motherboard type interrupts. + * This contains the irq mask for both irq controllers */ - -unsigned long isBeBox[]; -unsigned char *BeBox_IO_page; +static unsigned int cached_irq_mask = 0xffff; + +#define cached_21 (((char *)(&cached_irq_mask))[0]) +#define cached_A1 (((char *)(&cached_irq_mask))[1]) + -static unsigned char cache_21 = 0xff; -static unsigned char cache_A1 = 0xff; +int __ppc_bh_counter; -void disable_irq(unsigned int irq_nr) +void *null_handler(int,void *,struct pt_regs *); + +/* + * disable and enable intrs in software. This is used + * from the non-realtime parts of Linux to disable interrupts. + * The realtime part disables/enables intrs in the hardware. + * -- Cort + */ +unsigned long soft_intr_enable = 1; +void _soft_cli(void) { - unsigned char mask; - int s = _disable_interrupts(); + soft_intr_enable = 0; +} - if (isBeBox[0] && (irq_nr >= 16)) - { - BeBox_disable_irq(irq_nr); - } else +void _soft_sti(void) +{ + soft_intr_enable = 1; + if ( lost_interrupts ) { - mask = 1 << (irq_nr & 7); - if (irq_nr < 8) - { - cache_21 |= mask; - outb(cache_21,0x21); - } else - { - cache_A1 |= mask; - outb(cache_A1,0xA1); - } + printk("lost_interrupts from _soft_sti() %x\n",lost_interrupts); + fake_interrupt(); } - _enable_interrupts(s); } -void enable_irq(unsigned int irq_nr) +void * +null_handler(int a, void *b, struct pt_regs *regs) +{ + /*printk("irq.c: null_handler() called. Should not have happened.\n");*/ +} + +void +disable_irq(unsigned int irq_nr) { - unsigned char mask; int s = _disable_interrupts(); + unsigned char mask; - if (isBeBox[0] && (irq_nr >= 16)) +#ifdef CONFIG_PMAC + out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr)); +#else /* CONFIG_PMAC */ + mask = 1 << (irq_nr & 7); + if (irq_nr < 8) { - BeBox_enable_irq(irq_nr); - _enable_interrupts(s); - return; + cached_21 |= mask; + outb(cached_21,0x21); } else { - mask = ~(1 << (irq_nr & 7)); - if (irq_nr < 8) { - cache_21 &= mask; - outb(cache_21,0x21); - } else - { - cache_A1 &= mask; - outb(cache_A1,0xA1); - } - } + cached_A1 |= mask; + outb(cached_A1,0xA1); + } +#endif /* CONFIG_PMAC */ _enable_interrupts(s); } -/* - * Irq handlers. - */ -struct irq_action { - void (*handler)(int, void *dev, struct pt_regs *); - unsigned long flags; - unsigned long mask; - const char *name; - int notified; - void *dev_id; -}; - -static struct irq_action irq_action[32] = { - { 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 }, - { 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 } -}; - - -inline int get_irq_list(char *buf) -{ - int i, len = 0; - struct irq_action * action = irq_action; - - for (i = 0; i < 32; 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; -} - -inline void -process_IRQ(int irq, int _irq, struct pt_regs *regs) -{ - struct irq_action *action; - atomic_inc(&intr_count); - if (irq < 16) - { - /* Mask interrupt */ - if (irq > 7) - { - cache_A1 |= (1<<(irq-8)); - outb(cache_A1, 0xA1); - } else - { - cache_21 |= (1<handler(1, action->dev_id, regs); - } - count = 0; - } - } - if (action->handler) - { - action->handler(irq, action->dev_id, regs); - } else - { - printk("Bogus interrupt %d/%x, pc %x regs %x\n", - irq, _irq,regs->nip,regs); -#if 0 - printk("BeBox[] = %x/%x\n", isBeBox[0], isBeBox[1]); - show_BeBox_state(); - cnpause(); -#endif - } - if (_disable_interrupts() && !action->notified) - { - action->notified = 1; - printk("*** WARNING! %s handler [IRQ %d] turned interrupts on!\n", - action->name, irq); - } - if (irq < 16) - { - /* Issue EOI to interrupt controller */ - if (irq > 7) - { - outb(0xE0|(irq-8), 0xA0); - outb(0xE2, 0x20); - } else - { - outb(0xE0|irq, 0x20); - } - if (!(action->flags & SA_ONESHOT)) - { - /* Re-enable interrupt */ - if (irq > 7) - { - cache_A1 &= ~(1<<(irq-8)); - outb(cache_A1, 0xA1); - } else - { - cache_21 &= ~(1< IRQ8..IRQ15 */ - outb(0x0C, 0xA0); - irq = (_irq = inb(0xA0)) & 0x07; - irq += 8; - } - } - process_IRQ(irq, _irq, regs); - - /* Sometimes, the cascaded IRQ controller get's "stuck" */ - if ((irq == 0) && (_ints++ == 100)) - { - _ints = 0; - outb(0x0A, 0xA0); _irq = inb(0xA0); - if (_irq & ~cache_A1) - { /* Figure out which IRQs are present */ - _irq &= ~cache_A1; - for (irq = 0; irq < 7; irq++) - { - if (_irq & (1<handler) + continue; + len += sprintf(buf+len, "%2d: %10u %s", + i, kstat.interrupts[i], action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ", %s", action->name); + } + len += sprintf(buf+len, "\n"); + } /* - * Initialize interrupt controllers to a well-known state. + * Linus - should you add NMI counts here ????? */ +#ifdef __SMP_PROF__ + len+=sprintf(buf+len, "IPI: %8lu received\n", + ipi_count); +#endif + return len; +} -static void -reset_int_controllers(void) +asmlinkage void handle_IRQ(struct pt_regs *regs) { - /* Initialize interrupt controllers */ - outb(0x11, 0x20); /* Start init sequence */ - outb(0x40, 0x21); /* Vector base */ - outb(0x04, 0x21); /* Cascade (slave) on IRQ2 */ - outb(0x01, 0x21); /* Select 8086 mode */ - outb(0xFF, 0x21); /* Mask all */ - outb(0x11, 0xA0); /* Start init sequence */ - outb(0x48, 0xA1); /* Vector base */ - outb(0x02, 0xA1); /* Cascade (slave) on IRQ2 */ - outb(0x01, 0xA1); /* Select 8086 mode */ - outb(0xFF, 0xA1); /* Mask all */ -#if 0 - outb(0x00, 0x4D0); /* All edge triggered */ - outb(0xCF, 0x4D1); /* Trigger mode */ + int irq; + unsigned bits; + struct irqaction *action; + int cpu = smp_processor_id(); + + hardirq_enter(cpu); + +#ifdef CONFIG_PMAC + bits = ld_le32(IRQ_FLAG) | lost_interrupts; + lost_interrupts = 0; + + for (irq = NR_IRQS; irq >= 0; --irq) + if (bits & (1U << irq)) + break; +#else /* CONFIG_PMAC */ +#if 1 + if ( lost_interrupts ) + { + irq = ffz(~lost_interrupts); + lost_interrupts &= ~irq; + goto retry; + } + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { +retry_cascade: + outb(0x0C, 0xA0); + irq = inb(0xA0); + /* if no intr left */ + if ( !(irq & 128 ) ) + goto out; + irq = (irq&7) + 8; + } +retry: +#else + /* get the irr from the intr controller */ + outb(0x0A, 0x20); + bits = inb(0x20); + /* handle cascade */ + if ( bits ) + { + bits &= 4; + outb(0x0A, 0xA0); + bits = inb(0xA0)<<8; + } + /* get lost interrupts */ + bits |= lost_interrupts; + /* save intrs that are masked out */ + lost_interrupts = bits & cached_irq_mask; + /* get rid of intrs being masked */ + bits &= ~cached_irq_mask; + /* non-specifc eoi */ + outb(0x20,0x20); + if ( bits & 0xff00 ) + outb(0x20,0xA0); + + printk("bits %04X lost %04X mask %04x\n", + bits, lost_interrupts,cached_irq_mask); + + for (irq = NR_IRQS; irq >= 0; --irq) + if (bits & (1U << irq)) + break; #endif - outb(cache_A1, 0xA1); - outb(cache_21, 0x21); - enable_irq(2); /* Enable cascade interrupt */ +#endif /* CONFIG_PMAC */ + + if (irq < 0) { + printk("Bogus interrupt from PC = %lx, irq %d\n",regs->nip,irq); + goto out; + } + +#ifdef CONFIG_PMAC + out_le32(IRQ_ACK, 1U << irq); +#else /* CONFIG_PMAC */ + /* mask out the irq while handling it */ + disable_irq(irq); + /* + * send eoi to interrupt controller right away or lower + * priority intrs would be ignored even if with intrs enabled + */ + if (irq > 7) + { + outb(0xE0|(irq-8), 0xA0); + outb(0xE2, 0x20); + } else + { + outb(0xE0|irq, 0x20); + } +#endif /* !CONFIG_PMAC */ + + /* + * now that we've acked the irq, if intrs are disabled in software + * we're in the real-time system and non-rt linux has disabled them + * so we just queue it up and return -- Cort + */ + if ( ! soft_intr_enable ) + { + lost_interrupts |= 1UL << irq; + /* can't printk - kernel expects intrs off! */ + /*printk("irq %d while intrs soft disabled\n", irq);*/ + goto out; + } + + action = irq + irq_action; + kstat.interrupts[irq]++; + if (action->handler) { + action->handler(irq, action->dev_id, regs); + _disable_interrupts(); /* in case the handler turned them on */ + } else { + disable_irq( irq ); + } +#ifdef CONFIG_PREP + /* re-enable if the interrupt was good and isn't one-shot */ + if ( action->handler && !(action->flags & SA_ONESHOT) ) + enable_irq(irq); + /* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */ + if ( irq > 7 ) + goto retry_cascade; +#endif + + hardirq_exit(cpu); + /* + * This should be conditional: we should really get + * a return code from the irq handler to tell us + * whether the handler wants us to do software bottom + * half handling or not.. + */ + if (1) + if (bh_active & bh_mask) + do_bottom_half(); + return; +out: + hardirq_exit(cpu); + } int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char * devname, void *dev_id) + unsigned long irqflags, const char * devname, void *dev_id) { - struct irq_action * action; + struct irqaction * action; unsigned long flags; - -#ifdef SHOW_IRQ -if (irq) printk("Request IRQ #%d, Handler: %x\n", irq, handler); -#endif - if (irq > 15) - { - if (!isBeBox[0] || (irq > 31)) - return -EINVAL; - } + +#ifdef SHOW_IRQ + printk("request_irq(): irq %d handler %08x name %s dev_id %04x\n", + irq,handler,devname,dev_id); +#endif /* SHOW_IRQ */ + + if (irq > NR_IRQS) + return -EINVAL; action = irq + irq_action; if (action->handler) return -EBUSY; @@ -361,10 +342,14 @@ void free_irq(unsigned int irq, void *dev_id) { - struct irq_action * action = irq + irq_action; + struct irqaction * action = irq + irq_action; unsigned long flags; - if (irq > 31) { +#ifdef SHOW_IRQ + printk("free_irq(): irq %d dev_id %04x\n", irq, dev_id); +#endif /* SHOW_IRQ */ + + if (irq > NR_IRQS) { printk("Trying to free IRQ%d\n",irq); return; } @@ -383,15 +368,6 @@ restore_flags(flags); } -#define SA_PROBE SA_ONESHOT - -static void no_action(int irq, void *dev, struct pt_regs * regs) -{ -#ifdef DEBUG - printk("Probe got IRQ: %d\n", irq); -#endif -} - unsigned long probe_irq_on (void) { unsigned int i, irqs = 0, irqmask; @@ -399,7 +375,7 @@ /* first, snaffle up any unassigned irqs */ for (i = 15; i > 0; i--) { - if (!request_irq(i, no_action, SA_PROBE, "probe", NULL)) { + if (!request_irq(i, null_handler, SA_ONESHOT, "probe", NULL)) { enable_irq(i); irqs |= (1 << i); } @@ -409,7 +385,7 @@ for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */ /* now filter out any obviously spurious interrupts */ - irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; + irqmask = (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21; for (i = 15; i > 0; i--) { if (irqs & (1 << i) & irqmask) { irqs ^= (1 << i); @@ -426,13 +402,13 @@ { unsigned int i, irqmask; - irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; + irqmask = (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21; for (i = 15; i > 0; i--) { if (irqs & (1 << i)) { free_irq(i, NULL); } } -#ifdef DEBUG +#ifdef SHOW_IRQ printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); #endif irqs &= irqmask; @@ -443,209 +419,65 @@ i = -i; return i; } - -void init_IRQ(void) -{ - int i; - - if ((_get_PVR()>>16) == 1) /* PPC 601 */ - { /* Nobis? */ - reset_int_controllers(); - } -#define TIMER0_COUNT 0x40 -#define TIMER_CONTROL 0x43 - /* set the clock to 100 Hz */ - outb_p(0x34,TIMER_CONTROL); /* binary, mode 2, LSB/MSB, ch 0 */ - outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ - outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ - if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL)) - printk("Unable to get IRQ2 for cascade\n"); - request_region(0x20,0x20,"pic1"); - request_region(0xa0,0x20,"pic2"); - - /* Make sure IRQ2 (cascade) interrupt is "level" based */ - outb(inb(0x4D0)|0x04, 0x4D0); /* IRQ2 level based */ - - /* Set up PCI interrupts */ - route_PCI_interrupts(); - - if (isBeBox[0]) - { - BeBox_init_IRQ(); - } -} - -/* - * Wrapper for "bottom 1/2" of interrupt processing. This routine - * is called whenever an interrupt needs non-interrupt-time service. - */ - -void _do_bottom_half(void) -{ - _enable_interrupts(1); - do_bottom_half(); - _disable_interrupts(); -} -void hard_disk_LED(int state) -{ - if (_Processor == _PROC_IBM) { - outb(state, IBM_HDD_LED); - } -} - - -/* - * Support for interrupts on the BeBox - */ - -#define CPU0_INT_MASK (volatile unsigned long *)(BeBox_IO_page+0x0F0) -#define CPU1_INT_MASK (volatile unsigned long *)(BeBox_IO_page+0x1F0) -#define INT_SOURCE (volatile unsigned long *)(BeBox_IO_page+0x2F0) -#define CPU_RESET (volatile unsigned long *)(BeBox_IO_page+0x4F0) - -#define _CPU0_INT_MASK (volatile unsigned long *)(0xA0000000+0x0F0) -#define _CPU1_INT_MASK (volatile unsigned long *)(0xA0000000+0x1F0) -#define _INT_SOURCE (volatile unsigned long *)(0xA0000000+0x2F0) -#define _CPU_RESET (volatile unsigned long *)(0xA0000000+0x4F0) - -#define CPU_HRESET 0x20000000 -#define CPU_SRESET 0x40000000 - -#define SCSI_IRQ 16 - -#define INT_SCSI (1<<21) -#define INT_8259 (1<<5) - -/* - * Map of pseudo IRQs to actual bits - * Note: We give out IRQ #16..31 for all interrupt sources which are - * not found in the 8259 PIC. - */ - -unsigned long BeBox_IRQ_map[] = - { - INT_SCSI, /* 16 - SCSI */ - 0x00000000, /* 17 - Unused */ - 0x00000000, /* 18 - Unused */ - 0x00000000, /* 19 - Unused */ - 0x00000000, /* 20 - Unused */ - 0x00000000, /* 21 - Unused */ - 0x00000000, /* 22 - Unused */ - 0x00000000, /* 23 - Unused */ - 0x00000000, /* 24 - Unused */ - 0x00000000, /* 25 - Unused */ - 0x00000000, /* 26 - Unused */ - 0x00000000, /* 27 - Unused */ - 0x00000000, /* 28 - Unused */ - 0x00000000, /* 29 - Unused */ - 0x00000000, /* 30 - Unused */ - 0x00000000, /* 31 - Unused */ - }; - -volatile int CPU1_alive; -volatile int CPU1_trace; - -static -_NOP(void) -{ -} - -static -_delay(void) +void init_IRQ(void) { - int i; - for (i = 0; i < 100; i++) _NOP(); -} +#ifdef CONFIG_PMAC + extern void xmon_irq(int, void *, struct pt_regs *); -void -BeBox_init_IRQ(void) -{ - int tmr; - volatile extern long BeBox_CPU1_vector; - *CPU0_INT_MASK = 0x0FFFFFFC; /* Clear all bits? */ - *CPU0_INT_MASK = 0x80000003 | INT_8259; - *CPU1_INT_MASK = 0x0FFFFFFC; -printk("Start CPU #1 - CPU Status: %x\n", *CPU_RESET); - BeBox_CPU1_vector = 0x0100; /* Reset */ - tmr = 0; - while (CPU1_alive == 0) - { - if (++tmr == 1000) - { -printk("CPU #1 not there? - CPU Status: %x, Trace: %x\n", *CPU_RESET, CPU1_trace); - break; - } - _delay(); - } -printk("CPU #1 running!\n"); -} + *IRQ_ENABLE = 0; + request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0); +#else /* CONFIG_PMAC */ + /* Initialize interrupt controllers */ + outb(0x11, 0x20); /* Start init sequence */ + outb(0x40, 0x21); /* Vector base */ +#if 1 + outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ +#else + outb(0x0C, 0x21); /* level triggered, Cascade (slave) on IRQ2 */ +#endif + + outb(0x01, 0x21); /* Select 8086 mode */ + outb(0xFF, 0x21); /* Mask all */ -void -BeBox_disable_irq(int irq) -{ - /* Note: this clears the particular bit */ - *CPU0_INT_MASK = BeBox_IRQ_map[irq-16]; -} + outb(0x11, 0xA0); /* Start init sequence */ + outb(0x48, 0xA1); /* Vector base */ +#if 1 + outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ +#else + outb(0x0A, 0x21); /* level triggered, Cascade (slave) on IRQ2 */ +#endif + outb(0x01, 0xA1); /* Select 8086 mode */ + outb(0xFF, 0xA1); /* Mask all */ -void -BeBox_enable_irq(int irq) -{ - int s = _disable_interrupts(); - /* Sets a single bit */ + /* + * Program level mode for irq's that 'can' be level triggered. + * This does not effect irq's 0,1,2 and 8 since they must be + * edge triggered. This is not the PIC controller. The default + * here is for edge trigger mode. -- Cort + */ #if 0 -printk("BeBox IRQ Mask = %x", *CPU0_INT_MASK); + outb(0xf8, 0x4d0); /* level triggered */ + outb(0xff, 0x4d1); #endif - *CPU0_INT_MASK = 0x80000000 | BeBox_IRQ_map[irq-16]; -#if 0 -printk("/%x\n", *CPU0_INT_MASK); -#endif - _enable_interrupts(s); -} - -void -show_BeBox_state(void) -{ - unsigned long cpu0_int_mask; - unsigned long int_state; - cpu0_int_mask = (*CPU0_INT_MASK & 0x0FFFFFFC) & ~INT_8259; - int_state = cpu0_int_mask & *INT_SOURCE; - printk("Ints[%x] = %x, Mask[%x] = %x/%x, State = %x\n", INT_SOURCE, *INT_SOURCE, CPU0_INT_MASK, *CPU0_INT_MASK, cpu0_int_mask, int_state); -} - -int -BeBox_irq(void) -{ - int i; - unsigned long cpu0_int_mask; - unsigned long int_state; - cpu0_int_mask = (*CPU0_INT_MASK & 0x0FFFFFFC) & ~INT_8259; - int_state = cpu0_int_mask & *INT_SOURCE; - if (int_state) - { /* Determine the pseudo-interrupt # */ #if 0 - printk("Ints[%x] = %x, Mask[%x] = %x/%x, State = %x\n", INT_SOURCE, *INT_SOURCE, CPU0_INT_MASK, *CPU0_INT_MASK, cpu0_int_mask, int_state); -#endif - for (i = 0; i < 16; i++) - { - if (BeBox_IRQ_map[i] & int_state) - { - return (i+16); - } - } -printk("Ints[%x] = %x, Mask[%x] = %x/%x, State = %x\n", INT_SOURCE, *INT_SOURCE, CPU0_INT_MASK, *CPU0_INT_MASK, cpu0_int_mask, int_state); -printk("Can't find BeBox IRQ!\n"); - } - return (0); -} - -void BeBox_state(void) -{ - printk("Int state = %x, CPU0 mask = %x, CPU1 mask = %x\n", *INT_SOURCE, *CPU0_INT_MASK, *CPU1_INT_MASK); -} + outb(0x00, 0x4D0); /* All edge triggered */ + outb(0xCF, 0x4D1); /* Trigger mode */ +#endif + outb(cached_A1, 0xA1); + outb(cached_21, 0x21); + if (request_irq(2, null_handler, SA_INTERRUPT, "cascade", NULL)) + printk("Unable to get IRQ2 for cascade\n"); + enable_irq(2); /* Enable cascade interrupt */ + +#define TIMER0_COUNT 0x40 +#define TIMER_CONTROL 0x43 + /* set timer to periodic mode */ + outb_p(0x34,TIMER_CONTROL); /* binary, mode 2, LSB/MSB, ch 0 */ + /* set the clock to ~100 Hz */ + outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ + outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ -void BeBox_CPU1(void) -{ - CPU1_alive++; - while (1) ; + route_pci_interrupts(); +#endif /* CONFIG_PMAC */ } diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/ksyms.c linux/arch/ppc/kernel/ksyms.c --- v2.1.47/linux/arch/ppc/kernel/ksyms.c Fri Dec 27 02:03:20 1996 +++ linux/arch/ppc/kernel/ksyms.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,40 @@ +#include #include #include +#include +#include + +#include +#include +#include +#include + +void transfer_to_handler(); +void int_return(); +void syscall_trace(); +void handle_IRQ(); +void MachineCheckException(); +void AlignmentException(); +void ProgramCheckException(); +void SingleStepException(); +void FloatingPointCheckException(); +void sys_sigreturn(); +unsigned long sys_call_table[]; + +extern struct task_struct *current_set[1]; /* platform dependent support */ +EXPORT_SYMBOL(current_set); +EXPORT_SYMBOL(do_signal); +EXPORT_SYMBOL(syscall_trace); +EXPORT_SYMBOL(transfer_to_handler); +EXPORT_SYMBOL(int_return); +EXPORT_SYMBOL(handle_IRQ); +EXPORT_SYMBOL(init_task_union); +EXPORT_SYMBOL(MachineCheckException); +EXPORT_SYMBOL(AlignmentException); +EXPORT_SYMBOL(ProgramCheckException); +EXPORT_SYMBOL(SingleStepException); +EXPORT_SYMBOL(FloatingPointCheckException); +EXPORT_SYMBOL(sys_sigreturn); +EXPORT_SYMBOL(sys_call_table); diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S --- v2.1.47/linux/arch/ppc/kernel/misc.S Tue May 13 22:41:03 1997 +++ linux/arch/ppc/kernel/misc.S Thu Jul 31 13:09:17 1997 @@ -1,234 +1,40 @@ /* - * This module contains the PowerPC interrupt fielders - * set of code at specific locations, based on function + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * */ -#include "ppc_asm.tmpl" +#include #include +#include #include -#include "ppc_defs.h" #include - -/* Keep track of low-level exceptions - rather crude, but informative */ -#define STATS - -/* - * Increment a [64 bit] statistic counter - * Uses R2, R3 - */ -#define BUMP(ctr) \ - lis r2,ctr@h; \ - ori r2,r2,ctr@l; \ - lwz r3,4(r2); \ - addic r3,r3,1; \ - stw r3,4(r2); \ - lwz r3,0(r2); \ - addze r3,r3; \ - stw r3,0(r2) +#include "ppc_asm.tmpl" +#include "ppc_defs.h" -/*#ifdef CONFIG_603*/ -/* This instruction is not implemented on the PPC 603 */ +/* This instruction is not implemented on the PPC 601 or 603 */ #define tlbia \ - li r4,64; \ + li r4,128; \ mtspr CTR,r4; \ li r4,0; \ 0: tlbie r4; \ addi r4,r4,0x1000; \ bdnz 0b -/*#endif*/ /* CONFIG_603*/ _TEXT() -#define CPU_CTL 0x80000092 -_GLOBAL(hard_reset_now) - mfmsr r3 /* Disable interrupts */ - li r4,0 - ori r4,r4,MSR_EE - andc r3,r3,r4 - ori r3,r3,MSR_IP /* Set FLASH/ROM interrupt handlers */ - sync - mtmsr r3 - lis r3,CPU_CTL>>16 - ori r3,r3,(CPU_CTL&0xFFFF) - lbz r4,0(r3) /* Turn on SRESET */ - li r5,1 - andc r4,r4,r5 /* Make sure we go from 0->1 */ - stb r4,0(r3) - ori r4,r4,1 - stb r4,0(r3) /* This should do it! */ -99: nop - b 99b - -#if 0 -/* - unsigned short - le16_to_cpu(unsigned short val) -*/ -_GLOBAL(le16_to_cpu) - lis r4,_le_scratch@h - ori r4,r4,_le_scratch@l - sth r3,0(r4) - li r5,0 - lhbrx r3,r4,r5 - blr - -_GLOBAL(le32_to_cpu) - lis r4,_le_scratch@h - ori r4,r4,_le_scratch@l - stw r3,0(r4) - li r5,0 - lwbrx r3,r4,r5 - blr -_GLOBAL(_le_scratch) - .space 4 -#endif -#if 1 -/* -extern int __put_user_8(char, char *); -extern int __put_user_16(short, short *); -extern int __put_user_32(long, long *); -*/ -_GLOBAL(__put_user_8) - /* setup exception stuff */ - lis r2,current_set@ha - lwz r2,current_set@l(r2) - /* increment excount */ - lwz r6,TSS+TSS_EXCOUNT(r2) - addi r6,r6,1 - stw r6,TSS+TSS_EXCOUNT(r2) - /* set expc */ - lis r6,1f@h - ori r6,r6,1f@l - stw r6,TSS+TSS_EXPC(r2) - - stb r3,0(r4) - li r3,0 /* successful return */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr -1: li r3,-EFAULT /* bad access */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr - -_GLOBAL(__put_user_16) - /* setup exception stuff */ - lis r2,current_set@ha - lwz r2,current_set@l(r2) - /* increment excount */ - lwz r6,TSS+TSS_EXCOUNT(r2) - addi r6,r6,1 - stw r6,TSS+TSS_EXCOUNT(r2) - /* set expc */ - lis r6,1f@h - ori r6,r6,1f@l - stw r6,TSS+TSS_EXPC(r2) - - sth r3,0(r4) - li r3,0 /* successful return */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr -1: li r3,-EFAULT /* bad access */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr - -_GLOBAL(__put_user_32) - /* setup exception stuff */ - lis r2,current_set@ha - lwz r2,current_set@l(r2) - /* increment excount */ - lwz r6,TSS+TSS_EXCOUNT(r2) - addi r6,r6,1 - stw r6,TSS+TSS_EXCOUNT(r2) - /* set expc */ - lis r6,1f@h - ori r6,r6,1f@l - stw r6,TSS+TSS_EXPC(r2) - - stw r3,0(r4) - li r3,0 /* successful return */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr -1: li r3,-EFAULT /* bad access */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr - -_GLOBAL(__get_user_8) - /* setup exception stuff */ - lis r2,current_set@ha - lwz r2,current_set@l(r2) - /* increment excount */ - lwz r6,TSS+TSS_EXCOUNT(r2) - addi r6,r6,1 - stw r6,TSS+TSS_EXCOUNT(r2) - /* set expc */ - lis r6,1f@h - ori r6,r6,1f@l - stw r6,TSS+TSS_EXPC(r2) - - lbz r3,0(r4) - li r4,0 /* successful return */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr -1: li r4,-EFAULT /* bad access */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr - -_GLOBAL(__get_user_16) - /* setup exception stuff */ - lis r2,current_set@ha - lwz r2,current_set@l(r2) - /* increment excount */ - lwz r6,TSS+TSS_EXCOUNT(r2) - addi r6,r6,1 - stw r6,TSS+TSS_EXCOUNT(r2) - /* set expc */ - lis r6,1f@h - ori r6,r6,1f@l - stw r6,TSS+TSS_EXPC(r2) - - lhz r3,0(r4) - li r4,0 /* successful return */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr -1: li r4,-EFAULT /* bad access */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr - -_GLOBAL(__get_user_32) - /* setup exception stuff */ - lis r2,current_set@ha - lwz r2,current_set@l(r2) - /* increment excount */ - lwz r6,TSS+TSS_EXCOUNT(r2) - addi r6,r6,1 - stw r6,TSS+TSS_EXCOUNT(r2) - /* set expc */ - lis r6,1f@h - ori r6,r6,1f@l - stw r6,TSS+TSS_EXPC(r2) - lwz r3,0(r4) - li r4,0 /* successful return */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr -1: li r4,-EFAULT /* bad access */ - li r6,0 - stw r6,TSS+TSS_EXCOUNT(r2) - blr -#endif + /* * Disable interrupts * rc = _disable_interrupts() */ _GLOBAL(_disable_interrupts) +_GLOBAL(__cli) +_GLOBAL(_hard_cli) mfmsr r0 /* Get current interrupt state */ rlwinm r3,r0,16+1,32-1,31 /* Extract old value of 'EE' */ li r4,0 /* Need [unsigned] value of MSR_EE */ @@ -244,20 +50,18 @@ * turns on interrupts if state = 1. */ _GLOBAL(_enable_interrupts) - mfmsr r0 /* Get current state */ - rlwimi r0,r3,16-1,32-16,32-16 /* Insert bit */ + cmpi 0,r3,0 /* turning them on? */ + beqlr /* nothing to do if state == 0 */ +_GLOBAL(__sti) +_GLOBAL(_hard_sti) + lis r4,lost_interrupts@ha + lwz r4,lost_interrupts@l(r4) + mfmsr r3 /* Get current state */ + ori r3,r3,MSR_EE /* Turn on 'EE' bit */ + cmpi 0,r4,0 /* lost interrupts to process first? */ + bne- do_lost_interrupts sync /* Some chip revs have problems here... */ - mtmsr r0 /* Update machine state */ - blr - -/* - * Get 'flags' (aka machine status register) - * __save_flags(long *ptr) - */ -_GLOBAL(__save_flags) - mfmsr r0 /* Get current state */ - stw r0,0(r3) - mr r3,r0 + mtmsr r3 /* Update machine state */ blr /* @@ -265,34 +69,37 @@ * __restore_flags(long val) */ _GLOBAL(__restore_flags) - sync /* Some chip revs have problems here... */ + andi. r0,r3,MSR_EE /* enabling interrupts? */ + beq 2f + lis r4,lost_interrupts@ha + lwz r4,lost_interrupts@l(r4) + cmpi 0,r4,0 + bne do_lost_interrupts +2: sync /* Some chip revs have problems here... */ mtmsr r3 isync blr /* - * Disable interrupts - like an 80x86 - * cli() - */ -_GLOBAL(cli) - mfmsr r0 /* Get current interrupt state */ - rlwinm r3,r0,16+1,32-1,31 /* Extract old value of 'EE' */ - li r4,0 /* Need [unsigned] value of MSR_EE */ - ori r4,r4,MSR_EE /* Set to turn off bit */ - andc r0,r0,r4 /* Clears bit in (r4) */ - sync /* Some chip revs have problems here... */ - mtmsr r0 /* Update machine state */ - blr /* Done */ - -/* - * Enable interrupts - like an 80x86 - * sti() + * We were about to enable interrupts but we have to simulate + * some interrupts that were lost by enable_irq first. */ -_GLOBAL(sti) - mfmsr r0 /* Get current state */ - ori r0,r0,MSR_EE /* Turn on 'EE' bit */ - sync /* Some chip revs have problems here... */ - mtmsr r0 /* Update machine state */ +do_lost_interrupts: + stwu r1,-16(r1) + mflr r0 + stw r0,20(r1) + stw r3,8(r1) +1: bl fake_interrupt + lis r4,lost_interrupts@ha + lwz r4,lost_interrupts@l(r4) + cmpi 0,r4,0 + bne- 1b + lwz r3,8(r1) + sync + mtmsr r3 + lwz r0,20(r1) + mtlr r0 + addi r1,r1,16 blr /* @@ -300,7 +107,6 @@ */ _GLOBAL(_tlbia) tlbia - BUMP(__TLBIAs) blr /* @@ -308,17 +114,7 @@ */ _GLOBAL(_tlbie) tlbie r3 - BUMP(__TLBIEs) blr - -/* - * Fetch the current SR register - * get_SR(int index) - */ -_GLOBAL(get_SR) - mfsrin r3,r3 - blr - /* * Atomic [test&set] exchange * @@ -340,7 +136,11 @@ * void atomic_sub(int c, int *v) * void atomic_inc(int *v) * void atomic_dec(int *v) - * void atomic_dec_and_test(int *v) + * int atomic_dec_and_test(int *v) + * int atomic_inc_return(int *v) + * int atomic_dec_return(int *v) + * void atomic_clear_mask(atomic_t mask, atomic_t *addr) + * void atomic_set_mask(atomic_t mask, atomic_t *addr); */ _GLOBAL(atomic_add) 10: lwarx r5,0,r4 /* Fetch old value & reserve */ @@ -360,69 +160,46 @@ stwcx. r5,0,r3 /* Update with new value */ bne- 10b /* Retry if "reservation" (i.e. lock) lost */ blr +_GLOBAL(atomic_inc_return) +10: lwarx r5,0,r3 /* Fetch old value & reserve */ + addi r5,r5,1 /* Perform 'add' operation */ + stwcx. r5,0,r3 /* Update with new value */ + bne- 10b /* Retry if "reservation" (i.e. lock) lost */ + mr r3,r5 /* Return new value */ + blr _GLOBAL(atomic_dec) 10: lwarx r5,0,r3 /* Fetch old value & reserve */ subi r5,r5,1 /* Perform 'add' operation */ stwcx. r5,0,r3 /* Update with new value */ bne- 10b /* Retry if "reservation" (i.e. lock) lost */ blr +_GLOBAL(atomic_dec_return) +10: lwarx r5,0,r3 /* Fetch old value & reserve */ + subi r5,r5,1 /* Perform 'add' operation */ + stwcx. r5,0,r3 /* Update with new value */ + bne- 10b /* Retry if "reservation" (i.e. lock) lost */ + mr r3,r5 /* Return new value */ + blr _GLOBAL(atomic_dec_and_test) 10: lwarx r5,0,r3 /* Fetch old value & reserve */ subi r5,r5,1 /* Perform 'add' operation */ stwcx. r5,0,r3 /* Update with new value */ bne- 10b /* Retry if "reservation" (i.e. lock) lost */ cmpi 0,r5,0 /* Return 'true' IFF 0 */ - bne 15f li r3,1 + beqlr + li r3,0 blr -15: li r3,0 - blr - - -/* - * Delay for a specific # of "loops" - * __delay(int loops) - */ -_GLOBAL(__delay) - mtctr r3 -00: addi r3,r3,0 /* NOP */ - bdnz 00b - blr - -/* - * Delay for a number of microseconds - * udelay(int usecs) - */ -_GLOBAL(udelay) -00: li r0,86 /* Instructions / microsecond? */ - mtctr r0 -10: addi r0,r0,0 /* NOP */ - bdnz 10b - subic. r3,r3,1 - bne 00b - blr - -/* - * Atomically increment [intr_count] - */ -_GLOBAL(start_bh_atomic) - lis r3,intr_count@h - ori r3,r3,intr_count@l -10: lwarx r4,0,r3 - addi r4,r4,1 - stwcx. r4,0,r3 +_GLOBAL(atomic_clear_mask) +10: lwarx r5,0,r4 + andc r5,r5,r3 + stwcx. r5,0,r4 bne- 10b blr - -/* - * Atomically decrement [intr_count] - */ -_GLOBAL(end_bh_atomic) - lis r3,intr_count@h - ori r3,r3,intr_count@l -10: lwarx r4,0,r3 - subic r4,r4,1 - stwcx. r4,0,r3 +_GLOBAL(atomic_set_mask) +10: lwarx r5,0,r4 + or r5,r5,r3 + stwcx. r5,0,r4 bne- 10b blr @@ -431,6 +208,8 @@ * * insw(port, buf, len) * outsw(port, buf, len) + * insl(port, buf, len) + * outsl(port, buf, len) */ _GLOBAL(_insw) mtctr r5 @@ -448,135 +227,39 @@ bdnz 00b blr -#if 0 -/* - *extern inline int find_first_zero_bit(void * vaddr, unsigned size) - *{ - * unsigned long res; - * unsigned long *p; - * unsigned long *addr = vaddr; - * - * if (!size) - * return 0; - * __asm__ __volatile__ (" moveq #-1,d0\n\t" - * "1:" - * " cmpl %1@+,d0\n\t" - * " bne 2f\n\t" - * " subql #1,%0\n\t" - * " bne 1b\n\t" - * " bra 5f\n\t" - * "2:" - * " movel %1@-,d0\n\t" - * " notl d0\n\t" - * " bfffo d0{#0,#0},%0\n\t" - * "5:" - * : "=d" (res), "=a" (p) - * : "0" ((size + 31) >> 5), "1" (addr) - * : "d0"); - * return ((p - addr) << 5) + res; - *} - */ -_GLOBAL(find_first_zero_bit) - li r5,0 /* bit # */ - subi r3,r3,4 /* Adjust pointer for auto-increment */ -00: lwzu r0,4(r3) /* Get next word */ - not. r7,r0 /* Complement to find ones */ - beq 10f /* Jump if all ones */ -02: andi. r7,r0,1 /* Check low-order bit */ - beq 20f /* All done when zero found */ - srawi r0,r0,1 - addi r5,r5,1 - b 02b -10: addi r5,r5,32 /* Update bit # */ - subic. r4,r4,32 /* Any more? */ - bgt 00b -20: mr r3,r5 /* Compute result */ - blr - -/* - *static inline int find_next_zero_bit (void *vaddr, int size, - * int offset) - *{ - * unsigned long *addr = vaddr; - * unsigned long *p = addr + (offset >> 5); - * int set = 0, bit = offset & 31, res; - * - * if (bit) { - * // Look for zero in first longword - * __asm__("bfffo %1{#0,#0},%0" - * : "=d" (set) - * : "d" (~*p << bit)); - * if (set < (32 - bit)) - * return set + offset; - * set = 32 - bit; - * p++; - * } - * // No zero yet, search remaining full bytes for a zero - * res = find_first_zero_bit (p, size - 32 * (p - addr)); - * return (offset + set + res); - *} - */ -_GLOBAL(find_next_zero_bit) - addi r5,r5,1 /* bump offset to start */ - srawi r6,r5,5 /* word offset */ - add r6,r6,r6 /* byte offset */ - add r6,r6,r6 /* byte offset */ - add r3,r3,r6 /* compute byte position */ - sub r4,r4,r5 /* adjust size by starting index */ - andi. r0,r5,0x1F /* offset in current word? */ - beq 10f /* at start of word */ - lwz r0,0(r3) /* get word */ - sraw r0,r0,r5 /* shift right */ - not. r7,r0 - beq 07f /* jump if only ones remain */ -05: andi. r7,r0,1 /* found zero? */ - beq 90f /* yes - all done */ - srawi r0,r0,1 - addi r5,r5,1 - b 05b -07: andi. r6,r5,0x1F - subfic r0,r6,32 - add r5,r5,r0 - sub r4,r4,r0 - b 20f -10: subi r3,r3,4 /* Adjust pointer for auto-increment */ -20: lwzu r0,4(r3) /* Get next word */ - not. r7,r0 /* Complement to find ones */ - beq 40f /* Jump if all ones */ -30: andi. r7,r0,1 /* Check low-order bit */ - beq 90f /* All done when zero found */ - srawi r0,r0,1 - addi r5,r5,1 - b 30b -40: addi r5,r5,32 /* Update bit # */ - subic. r4,r4,32 /* Any more? */ - bgt 20b -90: mr r3,r5 /* Compute result */ +_GLOBAL(_insl) + mtctr r5 + subi r4,r4,4 +00: lwbrx r5,0,r3 + stwu r5,4(r4) + bdnz 00b blr + +_GLOBAL(_outsl) + mtctr r5 + subi r4,r4,4 +00: lwzu r5,4(r4) + stwbrx r5,0,r3 + bdnz 00b + blr + +#ifdef CONFIG_PMAC +_GLOBAL(ide_insw) + mtctr r5 + subi r4,r4,2 +00: lhzx r5,0,r3 + sthu r5,2(r4) + bdnz 00b + blr + +_GLOBAL(ide_outsw) + mtctr r5 + subi r4,r4,2 +00: lhzu r5,2(r4) + sthx r5,0,r3 + bdnz 00b + blr #endif - -/* - * - * ffz = Find First Zero in word. Undefined if no zero exists, - * so code should check against ~0UL first.. - * - *extern inline unsigned long ffz(unsigned long word) - *{ - * __asm__ __volatile__ ("bfffo %1{#0,#0},%0" - * : "=d" (word) - * : "d" (~(word))); - * return word; - *} - */ -_GLOBAL(ffz) - mr r4,r3 - li r3,0 -10: andi. r0,r4,1 /* Find the zero we know is there */ - srawi r4,r4,1 - beq 90f - addi r3,r3,1 - b 10b -90: blr /* * Extended precision shifts @@ -605,43 +288,7 @@ slw r3,r3,r5 /* YYY--- */ or r3,r3,r7 /* YYYZZZ */ blr - -_GLOBAL(abort) - .long 0 - -/* in include/asm/string.h now -- Cort */ -#if 0 -_GLOBAL(bzero) -#define bufp r3 -#define len r4 -#define pat r5 -/* R3 has buffer */ -/* R4 has length */ -/* R5 has pattern */ - cmpi 0,len,0 /* Exit if len <= 0 */ - ble 99f - andi. r0,bufp,3 /* Must be on longword boundary */ - bne 10f /* Use byte loop if not aligned */ - andi. r0,len,3 /* Check for overrage */ - subi bufp,bufp,4 /* Adjust pointer */ - srawi len,len,2 /* Divide by 4 */ - blt 99f /* If negative - bug out! */ - mtspr CTR,len /* Set up counter */ - li pat,0 -00: stwu pat,4(bufp) /* Store value */ - bdnz 00b /* Loop [based on counter] */ - mr len,r0 /* Get remainder (bytes) */ -10: cmpi 0,len,0 /* Any bytes left */ - ble 99f /* No - all done */ - mtspr CTR,len /* Set up counter */ - subi bufp,bufp,1 /* Adjust pointer */ - li pat,0 -20: stbu pat,1(bufp) /* Store value */ - bdnz 20b /* Loop [based on counter] */ -99: blr -#endif - _GLOBAL(abs) cmpi 0,r3,0 bge 10f @@ -651,39 +298,72 @@ _GLOBAL(_get_SP) mr r3,r1 /* Close enough */ blr - -_GLOBAL(_get_SDR1) - mfspr r3,SDR1 + +_GLOBAL(_get_PVR) + mfspr r3,PVR blr -_GLOBAL(_get_SRx) - mfsrin r3,r3 +_GLOBAL(cvt_fd) +cvt_fd: + lfs 0,0(r3) + stfd 0,0(r4) + blr +/* + * Fetch the current SR register + * get_SR(int index) + */ +_GLOBAL(get_SR) + mfsrin r4,r3 + mr r3,r4 blr -_GLOBAL(_get_PVR) - mfspr r3,PVR + +_GLOBAL(cvt_df) +cvt_df: + lfd 0,0(r3) + stfs 0,0(r4) blr /* * Create a kernel thread * __kernel_thread(flags, fn, arg) */ -#if 1 -#define SYS_CLONE 120 _GLOBAL(__kernel_thread) -__kernel_thread: - li r0,SYS_CLONE + li r0,__NR_clone sc cmpi 0,r3,0 /* parent or child? */ bnelr /* return if parent */ mtlr r4 /* fn addr in lr */ mr r3,r5 /* load arg and call fn */ - blr - li 0, 1 /* exit after child exits */ - li 3, 0 + blrl + li r0,__NR_exit /* exit after child exits */ + li r3,0 sc -#endif - + +#define SYSCALL(name) \ +_GLOBAL(name) \ + li r0,__NR_##name; \ + sc; \ + bnslr; \ + lis r4,errno@ha; \ + stw r3,errno@l(r4); \ + li r3,-1; \ + blr + +#define __NR__exit __NR_exit + +SYSCALL(idle) +SYSCALL(setup) +SYSCALL(sync) +SYSCALL(setsid) +SYSCALL(write) +SYSCALL(dup) +SYSCALL(execve) +SYSCALL(open) +SYSCALL(close) +SYSCALL(waitpid) + + /* Why isn't this a) automatic, b) written in 'C'? */ .data .align 4 @@ -694,12 +374,12 @@ .long sys_fork .long sys_read .long sys_write - .long sys_open /* 5 */ + .long sys_open /* 5 */ .long sys_close .long sys_waitpid .long sys_creat .long sys_link - .long sys_unlink /* 10 */ + .long sys_unlink /* 10 */ .long sys_execve .long sys_chdir .long sys_time @@ -709,7 +389,7 @@ .long sys_break .long sys_stat .long sys_lseek - .long sys_getpid /* 20 */ + .long sys_getpid /* 20 */ .long sys_mount .long sys_umount .long sys_setuid @@ -734,12 +414,12 @@ .long sys_pipe .long sys_times .long sys_prof - .long sys_brk /* 45 */ + .long sys_brk /* 45 */ .long sys_setgid .long sys_getgid .long sys_signal .long sys_geteuid - .long sys_getegid /* 50 */ + .long sys_getegid /* 50 */ .long sys_acct .long sys_phys .long sys_lock @@ -754,57 +434,57 @@ .long sys_ustat .long sys_dup2 .long sys_getppid - .long sys_getpgrp /* 65 */ + .long sys_getpgrp /* 65 */ .long sys_setsid .long sys_sigaction .long sys_sgetmask .long sys_ssetmask - .long sys_setreuid /* 70 */ + .long sys_setreuid /* 70 */ .long sys_setregid .long sys_sigsuspend .long sys_sigpending .long sys_sethostname - .long sys_setrlimit /* 75 */ + .long sys_setrlimit /* 75 */ .long sys_getrlimit .long sys_getrusage .long sys_gettimeofday .long sys_settimeofday - .long sys_getgroups /* 80 */ + .long sys_getgroups /* 80 */ .long sys_setgroups - .long sys_select + .long ppc_select .long sys_symlink .long sys_lstat - .long sys_readlink /* 85 */ + .long sys_readlink /* 85 */ .long sys_uselib .long sys_swapon .long sys_reboot .long old_readdir /* was sys_readdir */ - .long sys_mmap /* 90 */ + .long sys_mmap /* 90 */ .long sys_munmap .long sys_truncate .long sys_ftruncate .long sys_fchmod - .long sys_fchown /* 95 */ + .long sys_fchown /* 95 */ .long sys_getpriority .long sys_setpriority .long sys_profil .long sys_statfs - .long sys_fstatfs /* 100 */ + .long sys_fstatfs /* 100 */ .long sys_ioperm .long sys_socketcall .long sys_syslog .long sys_setitimer - .long sys_getitimer /* 105 */ + .long sys_getitimer /* 105 */ .long sys_newstat .long sys_newlstat .long sys_newfstat .long sys_uname - .long sys_iopl /* 110 */ + .long sys_iopl /* 110 */ .long sys_vhangup .long sys_idle .long sys_vm86 .long sys_wait4 - .long sys_swapoff /* 115 */ + .long sys_swapoff /* 115 */ .long sys_sysinfo .long sys_ipc .long sys_fsync @@ -814,7 +494,7 @@ .long sys_newuname .long sys_modify_ldt .long sys_adjtimex - .long sys_mprotect /* 125 */ + .long sys_mprotect /* 125 */ .long sys_sigprocmask .long sys_create_module .long sys_init_module @@ -829,9 +509,9 @@ .long 0 /* for afs_syscall */ .long sys_setfsuid .long sys_setfsgid - .long sys_llseek /* 140 */ + .long sys_llseek /* 140 */ .long sys_getdents - .long sys_newselect + .long ppc_select .long sys_flock .long sys_msync .long sys_readv /* 145 */ @@ -844,7 +524,7 @@ .long sys_mlockall .long sys_munlockall .long sys_sched_setparam - .long sys_sched_getparam /* 155 */ + .long sys_sched_getparam /* 155 */ .long sys_sched_setscheduler .long sys_sched_getscheduler .long sys_sched_yield @@ -853,8 +533,11 @@ .long sys_sched_rr_get_interval .long sys_nanosleep .long sys_mremap - .long SYMBOL_NAME(sys_setresuid) - .long SYMBOL_NAME(sys_getresuid) - .long SYMBOL_NAME(sys_nfsservctl) - .space (NR_syscalls-166)*4 + .long sys_setresuid + .long sys_getresuid /* 165 */ + .long sys_query_module + .long sys_poll + .long sys_nfsservctl + .long sys_debug + .space (NR_syscalls-170)*4 diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/mk_defs.c linux/arch/ppc/kernel/mk_defs.c --- v2.1.47/linux/arch/ppc/kernel/mk_defs.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/kernel/mk_defs.c Thu Jul 31 13:09:17 1997 @@ -1,11 +1,14 @@ /* * This program is used to generate definitions needed by * assembly language modules. + * + * We use the technique used in the OSF Mach kernel code: + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. */ -#define MK_DEFS -#include -#include +#include #include #include #include @@ -16,156 +19,114 @@ #include #include #include +#include #include +#include -extern int errno; +#define DEFINE(sym, val) \ + asm volatile("\n#define\t" #sym "\t%0" : : "i" (val)) -main(int argc, char *argv[]) +void +main(void) { - FILE *out; - struct task_struct task; - struct thread_struct tss; - int i; - char s[256]; - struct pt_regs regs; - if (!(out = fopen(argv[1], "w"))) - { - fprintf(stderr, "Can't create output file: %s\n", strerror(errno)); - exit(1); - } - fprintf(out, "/*\n"); - fprintf(out, " * WARNING! This file is automatically generated - DO NOT EDIT!\n"); - fprintf(out, " */\n"); - put_line(out, "STATE", (int)&task.state-(int)&task); - put_line(out, "COUNTER", (int)&task.counter-(int)&task); - put_line(out, "BLOCKED", (int)&task.blocked-(int)&task); - put_line(out, "SIGNAL", (int)&task.signal-(int)&task); - put_line(out, "KERNEL_STACK_PAGE", (int)&task.kernel_stack_page-(int)&task); - put_line(out, "TSS", (int)&task.tss-(int)&task); - put_line(out, "KSP", (int)&tss.ksp-(int)&tss); - put_line(out, "LAST_PC", (int)&tss.last_pc-(int)&tss); - put_line(out, "USER_STACK", (int)&tss.user_stack-(int)&tss); - put_line(out, "PT_REGS", (int)&tss.regs-(int)&tss); - put_line(out, "PF_TRACESYS", PF_TRACESYS); - put_line(out, "TASK_FLAGS", (int)&task.flags-(int)&task); - put_line(out, "MMU_SEG0", (int)&tss.segs[0]-(int)&tss); - put_line(out, "MMU_SEG1", (int)&tss.segs[1]-(int)&tss); - put_line(out, "MMU_SEG2", (int)&tss.segs[2]-(int)&tss); - put_line(out, "MMU_SEG3", (int)&tss.segs[3]-(int)&tss); - put_line(out, "MMU_SEG4", (int)&tss.segs[4]-(int)&tss); - put_line(out, "MMU_SEG5", (int)&tss.segs[5]-(int)&tss); - put_line(out, "MMU_SEG6", (int)&tss.segs[6]-(int)&tss); - put_line(out, "MMU_SEG7", (int)&tss.segs[7]-(int)&tss); - put_line(out, "MMU_SEG8", (int)&tss.segs[8]-(int)&tss); - put_line(out, "MMU_SEG9", (int)&tss.segs[9]-(int)&tss); - put_line(out, "MMU_SEG10", (int)&tss.segs[10]-(int)&tss); - put_line(out, "MMU_SEG11", (int)&tss.segs[11]-(int)&tss); - put_line(out, "MMU_SEG12", (int)&tss.segs[12]-(int)&tss); - put_line(out, "MMU_SEG13", (int)&tss.segs[13]-(int)&tss); - put_line(out, "MMU_SEG14", (int)&tss.segs[14]-(int)&tss); - put_line(out, "MMU_SEG15", (int)&tss.segs[15]-(int)&tss); - put_line(out, "TSS_EXPC", (int)&tss.expc-(int)&tss); - put_line(out, "TSS_EXCOUNT", (int)&tss.excount-(int)&tss); - put_line(out, "TSS_FPR0", (int)&tss.fpr[0]-(int)&tss); - put_line(out, "TSS_FPR1", (int)&tss.fpr[1]-(int)&tss); - put_line(out, "TSS_FPR2", (int)&tss.fpr[2]-(int)&tss); - put_line(out, "TSS_FPR3", (int)&tss.fpr[3]-(int)&tss); - put_line(out, "TSS_FPR4", (int)&tss.fpr[4]-(int)&tss); - put_line(out, "TSS_FPR5", (int)&tss.fpr[5]-(int)&tss); - put_line(out, "TSS_FPR6", (int)&tss.fpr[6]-(int)&tss); - put_line(out, "TSS_FPR7", (int)&tss.fpr[7]-(int)&tss); - put_line(out, "TSS_FPR8", (int)&tss.fpr[8]-(int)&tss); - put_line(out, "TSS_FPR9", (int)&tss.fpr[9]-(int)&tss); - put_line(out, "TSS_FPR10", (int)&tss.fpr[10]-(int)&tss); - put_line(out, "TSS_FPR11", (int)&tss.fpr[11]-(int)&tss); - put_line(out, "TSS_FPR12", (int)&tss.fpr[12]-(int)&tss); - put_line(out, "TSS_FPR13", (int)&tss.fpr[13]-(int)&tss); - put_line(out, "TSS_FPR14", (int)&tss.fpr[14]-(int)&tss); - put_line(out, "TSS_FPR15", (int)&tss.fpr[15]-(int)&tss); - put_line(out, "TSS_FPR16", (int)&tss.fpr[16]-(int)&tss); - put_line(out, "TSS_FPR17", (int)&tss.fpr[17]-(int)&tss); - put_line(out, "TSS_FPR18", (int)&tss.fpr[18]-(int)&tss); - put_line(out, "TSS_FPR19", (int)&tss.fpr[19]-(int)&tss); - put_line(out, "TSS_FPR20", (int)&tss.fpr[20]-(int)&tss); - put_line(out, "TSS_FPR21", (int)&tss.fpr[21]-(int)&tss); - put_line(out, "TSS_FPR22", (int)&tss.fpr[22]-(int)&tss); - put_line(out, "TSS_FPR23", (int)&tss.fpr[23]-(int)&tss); - put_line(out, "TSS_FPR24", (int)&tss.fpr[24]-(int)&tss); - put_line(out, "TSS_FPR25", (int)&tss.fpr[25]-(int)&tss); - put_line(out, "TSS_FPR26", (int)&tss.fpr[26]-(int)&tss); - put_line(out, "TSS_FPR27", (int)&tss.fpr[27]-(int)&tss); - put_line(out, "TSS_FPR28", (int)&tss.fpr[28]-(int)&tss); - put_line(out, "TSS_FPR29", (int)&tss.fpr[29]-(int)&tss); - put_line(out, "TSS_FPR30", (int)&tss.fpr[30]-(int)&tss); - put_line(out, "TSS_FPR31", (int)&tss.fpr[31]-(int)&tss); - put_line(out, "TSS_FP_USED", (int)&tss.fp_used-(int)&tss); - /* Interrupt register frame */ - put_line(out, "INT_FRAME_SIZE", sizeof(regs)); - put_line(out, "GPR0", (int)®s.gpr[0]-(int)®s); - put_line(out, "GPR1", (int)®s.gpr[1]-(int)®s); - put_line(out, "GPR2", (int)®s.gpr[2]-(int)®s); - put_line(out, "GPR3", (int)®s.gpr[3]-(int)®s); - put_line(out, "GPR4", (int)®s.gpr[4]-(int)®s); - put_line(out, "GPR5", (int)®s.gpr[5]-(int)®s); - put_line(out, "GPR6", (int)®s.gpr[6]-(int)®s); - put_line(out, "GPR7", (int)®s.gpr[7]-(int)®s); - put_line(out, "GPR8", (int)®s.gpr[8]-(int)®s); - put_line(out, "GPR9", (int)®s.gpr[9]-(int)®s); - put_line(out, "GPR10", (int)®s.gpr[10]-(int)®s); - put_line(out, "GPR11", (int)®s.gpr[11]-(int)®s); - put_line(out, "GPR12", (int)®s.gpr[12]-(int)®s); - put_line(out, "GPR13", (int)®s.gpr[13]-(int)®s); - put_line(out, "GPR14", (int)®s.gpr[14]-(int)®s); - put_line(out, "GPR15", (int)®s.gpr[15]-(int)®s); - put_line(out, "GPR16", (int)®s.gpr[16]-(int)®s); - put_line(out, "GPR17", (int)®s.gpr[17]-(int)®s); - put_line(out, "GPR18", (int)®s.gpr[18]-(int)®s); - put_line(out, "GPR19", (int)®s.gpr[19]-(int)®s); - put_line(out, "GPR20", (int)®s.gpr[20]-(int)®s); - put_line(out, "GPR21", (int)®s.gpr[21]-(int)®s); - put_line(out, "GPR22", (int)®s.gpr[22]-(int)®s); - put_line(out, "GPR23", (int)®s.gpr[23]-(int)®s); - put_line(out, "GPR24", (int)®s.gpr[24]-(int)®s); - put_line(out, "GPR25", (int)®s.gpr[25]-(int)®s); - put_line(out, "GPR26", (int)®s.gpr[26]-(int)®s); - put_line(out, "GPR27", (int)®s.gpr[27]-(int)®s); - put_line(out, "GPR28", (int)®s.gpr[28]-(int)®s); - put_line(out, "GPR29", (int)®s.gpr[29]-(int)®s); - put_line(out, "GPR30", (int)®s.gpr[30]-(int)®s); - put_line(out, "GPR31", (int)®s.gpr[31]-(int)®s); -#if 0 - for ( i = 0 ; i <= 31 ; i++) - { - sprintf(s,"FPR%d",i); - put_line(out, s, (int)®s.fpr[i]-(int)®s); - } + /*DEFINE(KERNELBASE, KERNELBASE);*/ + DEFINE(STATE, offsetof(struct task_struct, state)); + DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task)); + DEFINE(COUNTER, offsetof(struct task_struct, counter)); + DEFINE(BLOCKED, offsetof(struct task_struct, blocked)); + DEFINE(SIGNAL, offsetof(struct task_struct, signal)); + DEFINE(TSS, offsetof(struct task_struct, tss)); + DEFINE(KSP, offsetof(struct thread_struct, ksp)); + DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables)); +#ifdef CONFIG_PMAC + DEFINE(LAST_PC, offsetof(struct thread_struct, last_pc)); + DEFINE(USER_STACK, offsetof(struct thread_struct, user_stack)); +#endif + DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); + DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); + DEFINE(PF_TRACESYS, PF_TRACESYS); + DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); + DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0])); +#if 0 + DEFINE(TSS_FPR1, offsetof(struct thread_struct, fpr[1])); + DEFINE(TSS_FPR2, offsetof(struct thread_struct, fpr[2])); + DEFINE(TSS_FPR3, offsetof(struct thread_struct, fpr[3])); + DEFINE(TSS_FPR4, offsetof(struct thread_struct, fpr[4])); + DEFINE(TSS_FPR5, offsetof(struct thread_struct, fpr[5])); + DEFINE(TSS_FPR6, offsetof(struct thread_struct, fpr[6])); + DEFINE(TSS_FPR7, offsetof(struct thread_struct, fpr[7])); + DEFINE(TSS_FPR8, offsetof(struct thread_struct, fpr[8])); + DEFINE(TSS_FPR9, offsetof(struct thread_struct, fpr[9])); + DEFINE(TSS_FPR10, offsetof(struct thread_struct, fpr[10])); + DEFINE(TSS_FPR11, offsetof(struct thread_struct, fpr[11])); + DEFINE(TSS_FPR12, offsetof(struct thread_struct, fpr[12])); + DEFINE(TSS_FPR13, offsetof(struct thread_struct, fpr[13])); + DEFINE(TSS_FPR14, offsetof(struct thread_struct, fpr[14])); + DEFINE(TSS_FPR15, offsetof(struct thread_struct, fpr[15])); + DEFINE(TSS_FPR16, offsetof(struct thread_struct, fpr[16])); + DEFINE(TSS_FPR17, offsetof(struct thread_struct, fpr[17])); + DEFINE(TSS_FPR18, offsetof(struct thread_struct, fpr[18])); + DEFINE(TSS_FPR19, offsetof(struct thread_struct, fpr[19])); + DEFINE(TSS_FPR20, offsetof(struct thread_struct, fpr[20])); + DEFINE(TSS_FPR21, offsetof(struct thread_struct, fpr[21])); + DEFINE(TSS_FPR22, offsetof(struct thread_struct, fpr[22])); + DEFINE(TSS_FPR23, offsetof(struct thread_struct, fpr[23])); + DEFINE(TSS_FPR24, offsetof(struct thread_struct, fpr[24])); + DEFINE(TSS_FPR25, offsetof(struct thread_struct, fpr[25])); + DEFINE(TSS_FPR26, offsetof(struct thread_struct, fpr[26])); + DEFINE(TSS_FPR27, offsetof(struct thread_struct, fpr[27])); + DEFINE(TSS_FPR28, offsetof(struct thread_struct, fpr[28])); + DEFINE(TSS_FPR29, offsetof(struct thread_struct, fpr[29])); + DEFINE(TSS_FPR30, offsetof(struct thread_struct, fpr[30])); + DEFINE(TSS_FPR31, offsetof(struct thread_struct, fpr[31])); #endif - put_line(out, "FPCSR", (int)®s.fpcsr-(int)®s); - /* Note: these symbols include "_" because they overlap with special register names */ - put_line(out, "_NIP", (int)®s.nip-(int)®s); - put_line(out, "_MSR", (int)®s.msr-(int)®s); - /* put_line(out, "_SRR1", (int)®s.srr1-(int)®s); - put_line(out, "_SRR0", (int)®s.srr0-(int)®s); */ - put_line(out, "_CTR", (int)®s.ctr-(int)®s); - put_line(out, "_LINK", (int)®s.link-(int)®s); - put_line(out, "_CCR", (int)®s.ccr-(int)®s); - put_line(out, "_XER", (int)®s.xer-(int)®s); - put_line(out, "_DAR", (int)®s.dar-(int)®s); - put_line(out, "_DSISR", (int)®s.dsisr-(int)®s); - put_line(out, "_HASH1", (int)®s.hash1-(int)®s); - put_line(out, "_HASH2", (int)®s.hash2-(int)®s); - put_line(out, "_IMISS", (int)®s.imiss-(int)®s); - put_line(out, "_DMISS", (int)®s.dmiss-(int)®s); - put_line(out, "_ICMP", (int)®s.icmp-(int)®s); - put_line(out, "_DCMP", (int)®s.dcmp-(int)®s); - put_line(out, "ORIG_GPR3", (int)®s.orig_gpr3-(int)®s); - put_line(out, "RESULT", (int)®s.result-(int)®s); - put_line(out, "TRAP", (int)®s.trap-(int)®s); - put_line(out, "MARKER", (int)®s.marker-(int)®s); - exit(0); -} - -put_line(FILE *out, char *name, int offset) -{ - fprintf(out, "#define %s %d\n", name, offset); + DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr)); + /* Interrupt register frame */ + DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); + DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); + DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs)); + DEFINE(GPR0, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0])); + DEFINE(GPR1, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1])); + DEFINE(GPR2, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[2])); + DEFINE(GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[3])); + DEFINE(GPR4, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[4])); + DEFINE(GPR5, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[5])); + DEFINE(GPR6, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[6])); + DEFINE(GPR7, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[7])); + DEFINE(GPR8, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[8])); + DEFINE(GPR9, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[9])); + DEFINE(GPR10, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[10])); + DEFINE(GPR11, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[11])); + DEFINE(GPR12, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[12])); + DEFINE(GPR13, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[13])); + DEFINE(GPR14, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[14])); + DEFINE(GPR15, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[15])); + DEFINE(GPR16, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[16])); + DEFINE(GPR17, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[17])); + DEFINE(GPR18, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[18])); + DEFINE(GPR19, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[19])); + DEFINE(GPR20, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[20])); + DEFINE(GPR21, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[21])); + DEFINE(GPR22, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[22])); + DEFINE(GPR23, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[23])); + DEFINE(GPR24, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[24])); + DEFINE(GPR25, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[25])); + DEFINE(GPR26, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[26])); + DEFINE(GPR27, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[27])); + DEFINE(GPR28, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[28])); + DEFINE(GPR29, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[29])); + DEFINE(GPR30, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[30])); + DEFINE(GPR31, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[31])); + /* Note: these symbols include _ because they overlap with special register names */ + DEFINE(_NIP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, nip)); + DEFINE(_MSR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, msr)); + DEFINE(_CTR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ctr)); + DEFINE(_LINK, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, link)); + DEFINE(_CCR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ccr)); + DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer)); + DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); + DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); + DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3)); + DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result)); + DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap)); } diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c --- v2.1.47/linux/arch/ppc/kernel/pci.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/kernel/pci.c Thu Jul 31 13:09:17 1997 @@ -11,14 +11,18 @@ #include #include +#include +#include +#include +#include + /* * PCI interrupt configuration. This is motherboard specific. */ - - /* Which PCI interrupt line does a given device [slot] use? */ /* Note: This really should be two dimensional based in slot/pin used */ unsigned char *Motherboard_map; +unsigned char *Motherboard_map_name; /* How is the 82378 PIRQ mapping setup? */ unsigned char *Motherboard_routes; @@ -27,7 +31,7 @@ /* Motorola PowerStack */ static char Blackhawk_pci_IRQ_map[16] = - { +{ 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ 0, /* Slot 2 - unused */ @@ -44,20 +48,20 @@ 0, /* Slot 13 - unused */ 1, /* Slot 14 - Ethernet */ 0, /* Slot 15 - unused */ - }; +}; static char Blackhawk_pci_IRQ_routes[] = - { +{ 0, /* Line 0 - Unused */ 9, /* Line 1 */ 11, /* Line 2 */ 14, /* Line 3 */ 15 /* Line 4 */ - }; +}; /* Motorola MVME16xx */ static char Genesis_pci_IRQ_map[16] = - { +{ 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ 0, /* Slot 2 - unused */ @@ -74,20 +78,20 @@ 0, /* Slot 13 - unused */ 1, /* Slot 14 - Ethernet */ 0, /* Slot 15 - unused */ - }; +}; static char Genesis_pci_IRQ_routes[] = - { +{ 0, /* Line 0 - Unused */ 10, /* Line 1 */ 11, /* Line 2 */ 14, /* Line 3 */ 15 /* Line 4 */ - }; +}; /* Motorola Series-E */ static char Comet_pci_IRQ_map[16] = - { +{ 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ 0, /* Slot 2 - unused */ @@ -104,20 +108,20 @@ 0, /* Slot 13 - unused */ 1, /* Slot 14 - Ethernet */ 0, /* Slot 15 - unused */ - }; +}; static char Comet_pci_IRQ_routes[] = - { +{ 0, /* Line 0 - Unused */ 10, /* Line 1 */ 11, /* Line 2 */ 14, /* Line 3 */ 15 /* Line 4 */ - }; +}; /* BeBox */ static char BeBox_pci_IRQ_map[16] = - { +{ 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ 0, /* Slot 2 - unused */ @@ -134,20 +138,20 @@ 0, /* Slot 13 - unused */ 0, /* Slot 14 - unused */ 0, /* Slot 15 - unused */ - }; +}; static char BeBox_pci_IRQ_routes[] = - { +{ 0, /* Line 0 - Unused */ 9, /* Line 1 */ 11, /* Line 2 */ 14, /* Line 3 */ 15 /* Line 4 */ - }; +}; /* IBM Nobis */ static char Nobis_pci_IRQ_map[16] = - { +{ 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ 0, /* Slot 2 - unused */ @@ -164,54 +168,83 @@ 0, /* Slot 13 - unused */ 0, /* Slot 14 - unused */ 0, /* Slot 15 - unused */ - }; +}; static char Nobis_pci_IRQ_routes[] = - { +{ 0, /* Line 0 - Unused */ 13, /* Line 1 */ 13, /* Line 2 */ 13, /* Line 3 */ 13 /* Line 4 */ - }; +}; + + +/* + * ibm 830 (and 850?). + * This is actually based on the Carolina motherboard + * -- Cort + */ +static char ibm8xx_pci_IRQ_map[23] = { + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - FireCoral */ + 4, /* Slot 12 - Ethernet PCIINTD# */ + 2, /* Slot 13 - PCI Slot #2 */ + 2, /* Slot 14 - S3 Video PCIINTD# */ + 0, /* Slot 15 - onboard SCSI (INDI) [1] */ + 3, /* Slot 16 - NCR58C810 RS6000 Only PCIINTC# */ + 0, /* Slot 17 - unused */ + 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ +}; +static char ibm8xx_pci_IRQ_routes[] = { + 0, /* Line 0 - unused */ + 13, /* Line 1 */ + 10, /* Line 2 */ + 15, /* Line 3 */ + 15, /* Line 4 */ +}; +/* This just changes the PCI slots & onboard SCSI + S3 to IRQ10, but + * it really needs some logic to set them to unique IRQ's, or even + * add some logic to the drivers to ask an irq.c routine to re-map + * the IRQ if it needs one to itself... + */ +static char Carolina_PIRQ_routes[] = { + 0xad, /* INTB# = 10, INTA# = 13 */ + 0xff /* INTD# = 15, INTC# = 15 */ +}; +/* We have to turn on LEVEL mode for changed IRQ's */ +/* All PCI IRQ's need to be level mode, so this should be something + * other than hard-coded as well... IRQ's are individually mappable + * to either edge or level. + */ +#define CAROLINA_IRQ_EDGE_MASK_LO 0x00 /* IRQ's 0-7 */ +#define CAROLINA_IRQ_EDGE_MASK_HI 0xA4 /* IRQ's 8-15 [10,13,15] */ +#define PCI_DEVICE_ID_IBM_CORAL 0x000a -#define PCI_DEBUG #undef PCI_DEBUG #ifdef PCI_STATS int PCI_conversions[2]; #endif -unsigned long pcibios_init(unsigned long mem_start, - unsigned long mem_end) -{ - return mem_start; -} unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end) { - return mem_start; -} - - -unsigned long -_LE_to_BE_long(unsigned long val) -{ - unsigned char *p = (unsigned char *)&val; -#ifdef PCI_STATS - PCI_conversions[0]++; -#endif - return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | (p[0] << 0)); -} - -unsigned short -_LE_to_BE_short(unsigned long val) -{ - unsigned char *p = (unsigned char *)&val; -#ifdef PCI_STATS - PCI_conversions[1]++; -#endif - return ((p[3] << 8) | (p[2] << 0)); + return mem_start; } int @@ -225,7 +258,7 @@ int pcibios_read_config_dword (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned int *val) + unsigned char dev, unsigned char offset, unsigned int *val) { unsigned long _val; unsigned long *ptr; @@ -243,7 +276,7 @@ #ifdef PCI_DEBUG printk("[%x] ", ptr); #endif - _val = _LE_to_BE_long(*ptr); + _val = le32_to_cpu(*ptr); } #ifdef PCI_DEBUG printk("%x\n", _val); @@ -254,27 +287,27 @@ int pcibios_read_config_word (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned short *val) + unsigned char dev, unsigned char offset, unsigned short *val) { unsigned short _val; unsigned short *ptr; dev >>= 3; -#ifdef PCI_DEBUG +#ifdef PCI_DEBUG printk("PCI Read config word[%d.%d.%x] = ", bus, dev, offset); #endif if ((bus != 0) || (dev < 11) || (dev > 16)) { - *val = 0xFFFFFFFF; + *val = (unsigned short)0xFFFFFFFF; return PCIBIOS_DEVICE_NOT_FOUND; } else { ptr = (unsigned short *)(0x80800000 | (1<>= 3; - _val = _LE_to_BE_long(val); + _val = le32_to_cpu(val); #ifdef PCI_DEBUG printk("PCI Write config dword[%d.%d.%x] = %x\n", bus, dev, offset, _val); #endif @@ -349,12 +385,12 @@ int pcibios_write_config_word (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned short val) + unsigned char dev, unsigned char offset, unsigned short val) { unsigned short _val; unsigned short *ptr; dev >>= 3; - _val = _LE_to_BE_short(val); + _val = le16_to_cpu(val); #ifdef PCI_DEBUG printk("PCI Write config word[%d.%d.%x] = %x\n", bus, dev, offset, _val); #endif @@ -371,7 +407,7 @@ int pcibios_write_config_byte (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned char val) + unsigned char dev, unsigned char offset, unsigned char val) { unsigned char _val; unsigned char *ptr; @@ -420,7 +456,7 @@ int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *dev) + unsigned char *bus, unsigned char *dev) { int dev_nr, class, indx; indx = 0; @@ -437,7 +473,7 @@ *bus = 0; *dev = dev_nr<<3; #ifdef PCI_DEBUG - printk(" - device: %x\n", dev_nr); + printk(" - device: %x\n", dev_nr); #endif return (0); } @@ -455,22 +491,22 @@ static char buf[32]; switch (error) { case PCIBIOS_SUCCESSFUL: - return ("PCI BIOS: no error"); - case PCIBIOS_FUNC_NOT_SUPPORTED: - return ("PCI BIOS: function not supported"); - case PCIBIOS_BAD_VENDOR_ID: - return ("PCI BIOS: bad vendor ID"); - case PCIBIOS_DEVICE_NOT_FOUND: - return ("PCI BIOS: device not found"); - case PCIBIOS_BAD_REGISTER_NUMBER: - return ("PCI BIOS: bad register number"); - case PCIBIOS_SET_FAILED: - return ("PCI BIOS: set failed"); - case PCIBIOS_BUFFER_TOO_SMALL: - return ("PCI BIOS: buffer too small"); - default: - sprintf(buf, "PCI BIOS: invalid error #%d", error); - return(buf); + return ("PCI BIOS: no error"); + case PCIBIOS_FUNC_NOT_SUPPORTED: + return ("PCI BIOS: function not supported"); + case PCIBIOS_BAD_VENDOR_ID: + return ("PCI BIOS: bad vendor ID"); + case PCIBIOS_DEVICE_NOT_FOUND: + return ("PCI BIOS: device not found"); + case PCIBIOS_BAD_REGISTER_NUMBER: + return ("PCI BIOS: bad register number"); + case PCIBIOS_SET_FAILED: + return ("PCI BIOS: set failed"); + case PCIBIOS_BUFFER_TOO_SMALL: + return ("PCI BIOS: buffer too small"); + default: + sprintf(buf, "PCI BIOS: invalid error #%d", error); + return(buf); } } @@ -478,42 +514,112 @@ * Note: This routine has to access the PCI configuration space * for the PCI bridge chip (Intel 82378). */ - -void route_PCI_interrupts(void) +unsigned long pcibios_init(unsigned long mem_start,unsigned long mem_end) +{ + return mem_start; +} + +unsigned long route_pci_interrupts(void) { unsigned char *ibc_pirq = (unsigned char *)0x80800860; unsigned char *ibc_pcicon = (unsigned char *)0x80800840; extern unsigned long isBeBox[]; int i; - /* Decide which motherboard this is & how the PCI interrupts are routed */ - if (isBeBox[0]) - { - Motherboard_map = BeBox_pci_IRQ_map; - Motherboard_routes = BeBox_pci_IRQ_routes; - } else - if ((_get_PVR()>>16) == 1) - { /* Nobis */ - Motherboard_map = Nobis_pci_IRQ_map; - Motherboard_routes = Nobis_pci_IRQ_routes; - } else - { /* Motorola hardware */ + + if ( _machine == _MACH_Motorola) + { switch (inb(0x800) & 0xF0) { - case 0x10: /* MVME16xx */ - Motherboard_map = Genesis_pci_IRQ_map; - Motherboard_routes = Genesis_pci_IRQ_routes; - break; - case 0x20: /* Series E */ - Motherboard_map = Comet_pci_IRQ_map; - Motherboard_routes = Comet_pci_IRQ_routes; - break; - case 0x40: /* PowerStack */ - default: /* Can't hurt, can it? */ - Motherboard_map = Blackhawk_pci_IRQ_map; - Motherboard_routes = Blackhawk_pci_IRQ_routes; - break; + case 0x10: /* MVME16xx */ + Motherboard_map_name = "Genesis"; + Motherboard_map = Genesis_pci_IRQ_map; + Motherboard_routes = Genesis_pci_IRQ_routes; + break; + case 0x20: /* Series E */ + Motherboard_map_name = "Series E"; + Motherboard_map = Comet_pci_IRQ_map; + Motherboard_routes = Comet_pci_IRQ_routes; + break; + case 0x40: /* PowerStack */ + default: /* Can't hurt, can it? */ + Motherboard_map_name = "Blackhawk (Powerstack)"; + Motherboard_map = Blackhawk_pci_IRQ_map; + Motherboard_routes = Blackhawk_pci_IRQ_routes; + break; + } + } else + { + if ( _machine == _MACH_IBM ) + { + unsigned char pl_id; + unsigned long flags; + unsigned index; + unsigned char fn, bus; + unsigned int addr; + unsigned char dma_mode, ide_mode; + int i; + + Motherboard_map_name = "IBM 8xx (Carolina)"; + Motherboard_map = ibm8xx_pci_IRQ_map; + Motherboard_routes = ibm8xx_pci_IRQ_routes; +ll_printk("before loop\n"); + + for (index = 0; + !pcibios_find_device (PCI_VENDOR_ID_IBM, + PCI_DEVICE_ID_IBM_CORAL, + index, &bus, &fn); ++index) + { + pcibios_read_config_dword(bus, fn, 0x10, &addr); + addr &= ~0x3; + outb(0x26, addr); + dma_mode = inb(addr+4); + outb(0x25, addr); + ide_mode = inb(addr+4); + /*printk("CORAL I/O at 0x%x, DMA mode: %x, IDE mode: %x", + addr, dma_mode, ide_mode);*/ + /* Make CDROM non-DMA */ + ide_mode = (ide_mode & 0x0F) | 0x20; + outb(0x25, addr); + outb(ide_mode, addr+4); + dma_mode = dma_mode & ~0x80; + outb(0x26, addr); + outb(dma_mode, addr+4); + outb(0x26, addr); + dma_mode = inb(addr+4); + outb(0x25, addr); + ide_mode = inb(addr+4); + /*printk("=> DMA mode: %x, IDE mode: %x\n", + dma_mode, ide_mode);*/ + } + + /* Setup the PCI INT mappings for the Carolina */ + /* These are PCI Interrupt Route Control [1|2] Register */ + outb(Carolina_PIRQ_routes[0], 0x0890); + outb(Carolina_PIRQ_routes[1], 0x0891); + + pl_id=inb(0x0852); + /*printk("CPU Planar ID is %#0x\n", pl_id);*/ + + if (pl_id == 0x0C) { + /* INDI */ + Motherboard_map[12] = 1; + } +ll_printk("before edge/level\n"); +#if 0 + /*printk("Changing IRQ mode\n");*/ + pl_id=inb(0x04d0); + /*printk("Low mask is %#0x\n", pl_id);*/ + outb(pl_id|CAROLINA_IRQ_EDGE_MASK_LO, 0x04d0); + + pl_id=inb(0x04d1); + /*printk("Hi mask is %#0x\n", pl_id);*/ + outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1); + pl_id=inb(0x04d1); + /*printk("Hi mask now %#0x\n", pl_id);*/ +#endif } } + /* Set up mapping from slots */ for (i = 1; i <= 4; i++) { @@ -521,4 +627,4 @@ } /* Enable PCI interrupts */ *ibc_pcicon |= 0x20; -} +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c --- v2.1.47/linux/arch/ppc/kernel/pmac_setup.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/pmac_setup.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,260 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * + * Derived from "arch/alpha/kernel/setup.c" + * Copyright (C) 1995 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +/* + * bootup setup stuff.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int root_mountflags; + +extern char command_line[]; +char saved_command_line[256]; + +unsigned char aux_device_present; /* XXX */ +unsigned char kbd_read_mask; +unsigned char drive_info; + +#define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ + +extern unsigned long find_available_memory(void); + +unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) +{ + return memory_start; +} + +void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + extern unsigned long *end_of_DRAM; + struct device_node *cpu; + int *fp; + + strcpy(saved_command_line, command_line); + *cmdline_p = command_line; + + *memory_start_p = find_available_memory(); + *memory_end_p = (unsigned long) end_of_DRAM; + + set_prom_callback(); + + *memory_start_p = copy_device_tree(*memory_start_p, *memory_end_p); + + /* Set loops_per_sec to a half-way reasonable value, + for use until calibrate_delay gets called. */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "clock-frequency", NULL); + if (fp != 0) { + switch (_get_PVR() >> 16) { + case 4: /* 604 */ + loops_per_sec = *fp; + break; + default: /* 601, 603, etc. */ + loops_per_sec = *fp / 2; + } + } else + loops_per_sec = 50000000; + } +} + +char *bootpath; +char bootdevice[256]; +void *boot_host; +int boot_target; +int boot_part; +kdev_t boot_dev; + +unsigned long +pmac_find_devices(unsigned long mem_start, unsigned long mem_end) +{ + struct device_node *chosen_np; + + nvram_init(); + via_cuda_init(); + read_rtc_time(); + pmac_find_display(); + bootpath = NULL; + chosen_np = find_devices("chosen"); + if (chosen_np != NULL) + bootpath = (char *) get_property(chosen_np, "bootpath", NULL); + if (bootpath != NULL) { + /* + * There's a bug in the prom. (Why am I not surprised.) + * If you pass a path like scsi/sd@1:0 to canon, it returns + * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 + * That is, the scsi target number doesn't get preserved. + */ + call_prom("canon", 3, 1, bootpath, bootdevice, sizeof(bootdevice)); + } + return mem_start; +} + +void +note_scsi_host(struct device_node *node, void *host) +{ + int l; + char *p; + + l = strlen(node->full_name); + if (strncmp(node->full_name, bootdevice, l) == 0 + && (bootdevice[l] == '/' || bootdevice[l] == 0)) { + boot_host = host; + p = strstr(bootpath, "/sd@"); + if (p != NULL) { + p += 4; + boot_target = simple_strtoul(p, NULL, 10); + p = strchr(p, ':'); + if (p != NULL) + boot_part = simple_strtoul(p + 1, NULL, 10); + } + } +} + +void find_scsi_boot() +{ + int dev; + + if (kdev_t_to_nr(ROOT_DEV) != 0) + return; + ROOT_DEV = to_kdev_t(DEFAULT_ROOT_DEVICE); + if (boot_host == NULL) + return; + dev = sd_find_target(boot_host, boot_target); + if (dev == 0) + return; + boot_dev = to_kdev_t(dev + boot_part); +} + +void ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + struct device_node *np; + int i; + static struct device_node *atas; + static int atas_valid; + + *p = 0; + *irq = 0; + if (!atas_valid) { + atas = find_devices("ATA"); + atas_valid = 1; + } + for (i = (int)base, np = atas; i > 0 && np != NULL; --i, np = np->next) + ; + if (np == NULL) + return; + if (np->n_addrs == 0) { + printk("ide: no addresses for device %s\n", np->full_name); + return; + } + if (np->n_intrs == 0) { + printk("ide: no intrs for device %s, using 13\n", + np->full_name); + np->intrs[0] = 13; + } + base = (unsigned long) ioremap(np->addrs[0].address, 0x200); + for (i = 0; i < 8; ++i) + *p++ = base + i * 0x10; + *p = base + 0x160; + *irq = np->intrs[0]; +} + +int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + return -EIO; +} + +#if 0 +extern char builtin_ramdisk_image; +extern long builtin_ramdisk_size; +#endif + +void +builtin_ramdisk_init(void) +{ +#if 0 + if ((ROOT_DEV == to_kdev_t(DEFAULT_ROOT_DEVICE)) && (builtin_ramdisk_size != 0)) + { + rd_preloaded_init(&builtin_ramdisk_image, builtin_ramdisk_size); + } else +#endif + { /* Not ramdisk - assume root needs to be mounted read only */ + root_mountflags |= MS_RDONLY; + } +} + +int +get_cpuinfo(char *buffer) +{ + int pvr = _get_PVR(); + char *model; + struct device_node *cpu; + int l, *fp; + + l = 0; + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "clock-frequency", NULL); + if (fp != 0) + l += sprintf(buffer, "%dMHz ", *fp / 1000000); + } + + switch (pvr>>16) { + case 1: + model = "601"; + break; + case 3: + model = "603"; + break; + case 4: + model = "604"; + break; + case 6: + model = "603e"; + break; + case 7: + model = "603ev"; + break; + default: + model = "unknown"; + break; + } + return l + sprintf(buffer+l, "PowerPC %s rev %d.%d\n", model, + (pvr & 0xff) >> 8, pvr & 0xff); +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/pmac_support.c linux/arch/ppc/kernel/pmac_support.c --- v2.1.47/linux/arch/ppc/kernel/pmac_support.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/pmac_support.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,82 @@ +/* + * Miscellaneous procedures for dealing with the PowerMac hardware. + */ +#include +#include +#include +#include +#include +#include +#include + +void hard_reset_now(void) +{ + struct cuda_request req; + + cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); + for (;;) + cuda_poll(); +} + +void poweroff_now(void) +{ + struct cuda_request req; + + cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); + for (;;) + cuda_poll(); +} + +/* + * Read and write the non-volatile RAM on PowerMacs. + */ +static int nvram_naddrs; +static volatile unsigned char *nvram_addr; +static volatile unsigned char *nvram_data; + +void nvram_init(void) +{ + struct device_node *dp; + + dp = find_devices("nvram"); + if (dp == NULL) + panic("Can't find NVRAM device"); + nvram_naddrs = dp->n_addrs; + if (nvram_naddrs == 1) + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + else if (nvram_naddrs == 2) { + nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + } else { + printk("Found %d addresses for NVRAM\n", nvram_naddrs); + panic("don't understand NVRAM"); + } +} + +int nvram_readb(int addr) +{ + switch (nvram_naddrs) { + case 1: + return nvram_data[(addr & 0x1fff) << 4]; + case 2: + *nvram_addr = addr >> 5; + eieio(); + return nvram_data[(addr & 0x1f) << 4]; + } + return -1; +} + +void nvram_writeb(int addr, int val) +{ + switch (nvram_naddrs) { + case 1: + nvram_data[(addr & 0x1fff) << 4] = val; + break; + case 2: + *nvram_addr = addr >> 5; + eieio(); + nvram_data[(addr & 0x1f) << 4] = val; + break; + } + eieio(); +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/pmac_time.c linux/arch/ppc/kernel/pmac_time.c --- v2.1.47/linux/arch/ppc/kernel/pmac_time.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/pmac_time.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,168 @@ +/* + * Support for periodic interrupts (100 per second) and for getting + * the current time from the RTC on Power Macintoshes. + * + * At present, we use the decrementer register in the 601 CPU + * for our periodic interrupts. This will probably have to be + * changed for other processors. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int get_dec(void); +static void set_dec(int); +static unsigned long get_rtc_time(void); + +/* Apparently the RTC stores seconds since 1 Jan 1904 */ +#define RTC_OFFSET 2082844800 + +/* Accessor functions for the decrementer register. */ +static inline int +get_dec() +{ + int ret; + + asm volatile("mfspr %0,22" : "=r" (ret) :); + return ret; +} + +static inline void +set_dec(int val) +{ + asm volatile("mtspr 22,%0" : : "r" (val)); +} + +/* The decrementer counts down by 128 every 128ns on a 601. */ +#define DECREMENTER_COUNT_601 (1000000000 / HZ) +#define COUNT_PERIOD_NUM_601 1 +#define COUNT_PERIOD_DEN_601 1000 + +unsigned decrementer_count; /* count value for 1e6/HZ microseconds */ +unsigned count_period_num; /* 1 decrementer count equals */ +unsigned count_period_den; /* count_period_num / count_period_den us */ + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + + save_flags(flags); + cli(); + *tv = xtime; + tv->tv_usec += (decrementer_count - get_dec()) + * count_period_num / count_period_den; + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } + restore_flags(flags); +} + +void do_settimeofday(struct timeval *tv) +{ + unsigned long flags; + int frac_tick; + + frac_tick = tv->tv_usec % (1000000 / HZ); + save_flags(flags); + cli(); + xtime.tv_sec = tv->tv_sec; + xtime.tv_usec = tv->tv_usec - frac_tick; + set_dec(frac_tick * count_period_den / count_period_num); + restore_flags(flags); +} + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * We set it up to overflow again in 1/HZ seconds. + */ +void timer_interrupt(struct pt_regs * regs) +{ + int dval, d; + + while ((dval = get_dec()) < 0) { + /* + * Wait for the decrementer to change, then jump + * in and add decrementer_count to its value + * (quickly, before it changes again!) + */ + while ((d = get_dec()) == dval) + ; + set_dec(d + decrementer_count); + do_timer(regs); + } + +} + +void +time_init(void) +{ + struct device_node *cpu; + int freq, *fp, divisor; + + if ((_get_PVR() >> 16) == 1) { + /* 601 processor: dec counts down by 128 every 128ns */ + decrementer_count = DECREMENTER_COUNT_601; + count_period_num = COUNT_PERIOD_NUM_601; + count_period_den = COUNT_PERIOD_DEN_601; + } else { + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + cpu = find_type_devices("cpu"); + if (cpu == 0) + panic("can't find cpu node in time_init"); + fp = (int *) get_property(cpu, "timebase-frequency", NULL); + if (fp == 0) + panic("can't get cpu timebase frequency"); + freq = *fp * 60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %d/%d\n", + freq, divisor); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; + } + set_dec(decrementer_count); +} + +/* + * We can't do this in time_init, because via_cuda_init hasn't + * been called at that stage. + */ +void +read_rtc_time(void) +{ + xtime.tv_sec = get_rtc_time(); + xtime.tv_usec = 0; +} + +static unsigned long +get_rtc_time() +{ + struct cuda_request req; + + /* Get the time from the RTC */ + cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME); + while (!req.got_reply) + cuda_poll(); + if (req.reply_len != 7) + panic("get_rtc_time: didn't expect %d byte reply", + req.reply_len); + return (req.reply[3] << 24) + (req.reply[4] << 16) + + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET; +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/port_io.c linux/arch/ppc/kernel/port_io.c --- v2.1.47/linux/arch/ppc/kernel/port_io.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/kernel/port_io.c Thu Jul 31 13:09:17 1997 @@ -1,149 +1,146 @@ /* * I/O 'port' access routines */ +#include +#include -/* This is really only correct for the MVME16xx (PreP)? */ +#define inb_asm(port) {( \ + unsigned char ret; \ + asm ( "lbz %0,0(%1)\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" : "=r" (ret) : "r" (port+_IO_BASE)); \ + return ret; \ +}) -#define _IO_BASE ((unsigned long)0x80000000) - -unsigned char +inline unsigned char inb(int port) { - return (*((unsigned char *)(_IO_BASE+port))); + unsigned char ret; + asm("/*inb*/\n"); + asm ( "lbz %0,0(%1)" : "=r" (ret) : "r" (port+_IO_BASE)); + return ret; } -unsigned short +inline unsigned short inw(int port) { - return (_LE_to_BE_short(*((unsigned short *)(_IO_BASE+port)))); + unsigned short ret; + asm("/*inw*/\n"); + asm ( "lhbrx %0,%1,%2" : "=r" (ret) : "r" (port+_IO_BASE), "r" (0)); + return ret; } -unsigned long +inline unsigned long inl(int port) { - return (_LE_to_BE_long(*((unsigned long *)(_IO_BASE+port)))); + unsigned long ret; + asm("/*inl*/\n"); + asm ( "lwbrx %0,%1,%2" : "=r" (ret) : "r" (port+_IO_BASE), "r" (0)); + return ret; } -void insb(int port, char *ptr, int len) +inline unsigned char +outb(unsigned char val,int port) { - unsigned char *io_ptr = (unsigned char *)(_IO_BASE+port); - while (len-- > 0) - { - *ptr++ = *io_ptr; - } + asm("/*outb*/\n"); + asm ( "stb %0,0(%1)" :: "r" (val), "r" (port+_IO_BASE)); + return (val); } -#if 0 -void insw(int port, short *ptr, int len) -{ - unsigned short *io_ptr = (unsigned short *)(_IO_BASE+port); - while (len-- > 0) - { - *ptr++ = _LE_to_BE_short(*io_ptr); - } -} -#else -void insw(int port, short *ptr, int len) +inline unsigned short +outw(unsigned short val,int port) { - unsigned short *io_ptr = (unsigned short *)(_IO_BASE+port); - _insw(io_ptr, ptr, len); + asm("/*outw*/\n"); + asm ( "sthbrx %0,%1,%2" :: "r" (val), "r" (port+_IO_BASE), "r" (0)); + return (val); } -#endif -void insw_unswapped(int port, short *ptr, int len) +inline unsigned long +outl(unsigned long val,int port) { - unsigned short *io_ptr = (unsigned short *)(_IO_BASE+port); - while (len-- > 0) - { - *ptr++ = *io_ptr; - } + asm("/*outl*/\n"); + asm ( "stwbrx %0,%1,%2" :: "r" (val), "r" (port+_IO_BASE), "r" (0)); + return (val); } -void insl(int port, long *ptr, int len) +void insb(int port, char *ptr, int len) { - unsigned long *io_ptr = (unsigned long *)(_IO_BASE+port); - while (len-- > 0) - { - *ptr++ = _LE_to_BE_long(*io_ptr); - } + memcpy( (void *)ptr, (void *)(port+_IO_BASE), len); } -unsigned char inb_p(int port) {return (inb(port)); } -unsigned short inw_p(int port) {return (inw(port)); } -unsigned long inl_p(int port) {return (inl(port)); } - -unsigned char -outb(unsigned char val,int port) +void insw(int port, short *ptr, int len) { - *((unsigned char *)(_IO_BASE+port)) = (val); - return (val); + asm ("mtctr %2 \n\t" + "subi %1,%1,2 \n\t" + "00:\n\t" + "lhbrx %2,0,%0 \n\t" + "sthu %2,2(%1) \n\t" + "bdnz 00b \n\t" + :: "r" (port+_IO_BASE), "r" (ptr), "r" (len)); } -unsigned short -outw(unsigned short val,int port) +void insw_unswapped(int port, short *ptr, int len) { - *((unsigned short *)(_IO_BASE+port)) = _LE_to_BE_short(val); - return (val); + memcpy( (void *)ptr, (void *)(port+_IO_BASE), (len*sizeof(short)) ); } -unsigned long -outl(unsigned long val,int port) +void insl(int port, long *ptr, int len) { - *((unsigned long *)(_IO_BASE+port)) = _LE_to_BE_long(val); - return (val); + asm ("mtctr %2 \n\t" + "subi %1,%1,4 \n\t" + "00:\n\t" + "lhbrx %2,0,%0 \n\t" + "sthu %2,4(%1) \n\t" + "bdnz 00b \n\t" + :: "r" (port+_IO_BASE), "r" (ptr), "r" (len)); } void outsb(int port, char *ptr, int len) { - unsigned char *io_ptr = (unsigned char *)(_IO_BASE+port); - while (len-- > 0) - { - *io_ptr = *ptr++; - } + memcpy( (void *)ptr, (void *)(port+_IO_BASE), len ); } -#if 0 void outsw(int port, short *ptr, int len) { - unsigned short *io_ptr = (unsigned short *)(_IO_BASE+port); - while (len-- > 0) - { - *io_ptr = _LE_to_BE_short(*ptr++); - } + asm ("mtctr %2\n\t" + "subi %1,%1,2\n\t" + "00:lhzu %2,2(%1)\n\t" + "sthbrx %2,0,%0\n\t" + "bdnz 00b\n\t" + :: "r" (port+_IO_BASE), "r" (ptr), "r" (len)); } -#else -void outsw(int port, short *ptr, int len) -{ - unsigned short *io_ptr = (unsigned short *)(_IO_BASE+port); - _outsw(io_ptr, ptr, len); -} -#endif void outsw_unswapped(int port, short *ptr, int len) { - unsigned short *io_ptr = (unsigned short *)(_IO_BASE+port); - while (len-- > 0) - { - *io_ptr = *ptr++; - } + memcpy( (void *)ptr, (void *)(port+_IO_BASE), len*sizeof(short) ); } void outsl(int port, long *ptr, int len) { - unsigned long *io_ptr = (unsigned long *)(_IO_BASE+port); - while (len-- > 0) - { - *io_ptr = _LE_to_BE_long(*ptr++); - } -} - -unsigned char outb_p(unsigned char val,int port) { return (outb(val,port)); } -unsigned short outw_p(unsigned short val,int port) { return (outw(val,port)); } -unsigned long outl_p(unsigned long val,int port) { return (outl(val,port)); } - - -/* makes writing to the ibm acorn power management stuff easier -- Cort */ -/* args in forn of PA.B as in tech spec for ibm carolina */ -void ibm_write(unsigned char val,unsigned int port) -{ + asm ("mtctr %2\n\t" + "subi %1,%1,4\n\t" + "00:lwzu %2,4(%1)\n\t" + "sthbrx %2,0,%0\n\t" + "bdnz 00b\n\t" + :: "r" (port+_IO_BASE), "r" (ptr), "r" (len)); +} + +void insl_unswapped(int port, long *ptr, int len) +{ + unsigned long *io_ptr = (unsigned long *)(_IO_BASE+port); + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + while (len-- > 0) + { + *ptr++ = (*io_ptr); + } +} + +void outsl_unswapped(int port, long *ptr, int len) +{ + unsigned long *io_ptr = (unsigned long *)(_IO_BASE+port); + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + while (len-- > 0) + { + *io_ptr = (*ptr++); + } } diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/ppc_asm.tmpl linux/arch/ppc/kernel/ppc_asm.tmpl --- v2.1.47/linux/arch/ppc/kernel/ppc_asm.tmpl Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/kernel/ppc_asm.tmpl Thu Jul 31 13:09:17 1997 @@ -2,19 +2,10 @@ * This file contains all the macros and symbols which define * a PowerPC assembly language environment. */ - +#include #define _TEXT()\ .text -#if 0 /* Old way */ -#define _EXTERN(n) .##n - -#define _GLOBAL(n)\ - .globl n;\ -n: .long _EXTERN(n);\ - .globl _EXTERN(n);\ -_EXTERN(n): -#else #define _EXTERN(n) n #define SYMBOL_NAME(x) x @@ -22,7 +13,6 @@ #define _GLOBAL(n)\ .globl n;\ n: -#endif #ifndef FALSE #define FALSE 0 @@ -136,9 +126,13 @@ #define SDR1 25 /* MMU hash base register */ #define DAR 19 /* Data Address Register */ #define SPR0 272 /* Supervisor Private Registers */ +#define SPRG0 272 #define SPR1 273 +#define SPRG1 273 #define SPR2 274 +#define SPRG2 274 #define SPR3 275 +#define SPRG3 275 #define DSISR 18 #define SRR0 26 /* Saved Registers (exception) */ #define SRR1 27 @@ -164,7 +158,36 @@ #define SR14 14 #define SR15 15 +#define curptr r2 + +/* + * Macros for storing registers into and loading registers from + * exception frames. + */ +#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) +#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) +#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) +#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) +#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) +#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) +#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) +#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) +#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) +#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) + +#define SAVE_FPR(n, base) stfd n,TSS_FPR0+8*(n)(base) +#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) +#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) +#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) +#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) +#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) +#define REST_FPR(n, base) lfd n,TSS_FPR0+8*(n)(base) +#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) +#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) +#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) +#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) +#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) + /* Missing instructions */ #define bdne bc 0,2, -#include "asm/ppc_machine.h" diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/ppc_defs.h linux/arch/ppc/kernel/ppc_defs.h --- v2.1.47/linux/arch/ppc/kernel/ppc_defs.h Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/ppc_defs.h Thu Jul 31 13:09:17 1997 @@ -0,0 +1,63 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ +#define STATE 0 +#define NEXT_TASK 68 +#define COUNTER 4 +#define BLOCKED 16 +#define SIGNAL 12 +#define TSS 544 +#define KSP 0 +#define PG_TABLES 4 +#define LAST_SYSCALL 288 +#define PT_REGS 280 +#define PF_TRACESYS 32 +#define TASK_FLAGS 20 +#define TSS_FPR0 16 +#define TSS_FPSCR 12 +#define TASK_UNION_SIZE 8192 +#define STACK_FRAME_OVERHEAD 16 +#define INT_FRAME_SIZE 192 +#define GPR0 16 +#define GPR1 20 +#define GPR2 24 +#define GPR3 28 +#define GPR4 32 +#define GPR5 36 +#define GPR6 40 +#define GPR7 44 +#define GPR8 48 +#define GPR9 52 +#define GPR10 56 +#define GPR11 60 +#define GPR12 64 +#define GPR13 68 +#define GPR14 72 +#define GPR15 76 +#define GPR16 80 +#define GPR17 84 +#define GPR18 88 +#define GPR19 92 +#define GPR20 96 +#define GPR21 100 +#define GPR22 104 +#define GPR23 108 +#define GPR24 112 +#define GPR25 116 +#define GPR26 120 +#define GPR27 124 +#define GPR28 128 +#define GPR29 132 +#define GPR30 136 +#define GPR31 140 +#define _NIP 144 +#define _MSR 148 +#define _CTR 152 +#define _LINK 156 +#define _CCR 160 +#define _XER 164 +#define _DAR 168 +#define _DSISR 172 +#define ORIG_GPR3 176 +#define RESULT 180 +#define TRAP 184 diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/ppc_defs.head linux/arch/ppc/kernel/ppc_defs.head --- v2.1.47/linux/arch/ppc/kernel/ppc_defs.head Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/ppc_defs.head Thu Jul 31 13:09:17 1997 @@ -0,0 +1,3 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c --- v2.1.47/linux/arch/ppc/kernel/prep_setup.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/prep_setup.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,368 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + */ + +/* + * bootup setup stuff.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* for the mac fs */ +kdev_t boot_dev; + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_sec; + +unsigned long empty_zero_page[1024]; +unsigned char aux_device_present; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + +/* copy of the residual data */ +RESIDUAL res; +/* ptr to residual data from hw, must be initialized so not in bss (gets cleared )*/ +unsigned long resptr = 0; +int _machine; +extern unsigned long _TotalMemory; + +#define COMMAND_LINE_SIZE 256 +static char command_line[COMMAND_LINE_SIZE] = { 0, }; +char saved_command_line[COMMAND_LINE_SIZE]; +#ifdef HASHSTATS +unsigned long evicts; +#endif + +struct screen_info screen_info = { + 0, 25, /* 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 */ + 1, /* orig-video-isVGA */ + 16 /* orig-video-points */ +}; + +void machine_halt(void) +{ + machine_restart(NULL); +} + +void machine_power_off(void) +{ + machine_restart(NULL); +} + +void machine_restart(char *cmd) +{ + unsigned char ctl; + unsigned long flags; + unsigned long i = 10000; + + _disable_interrupts(); + + /* set exception prefix high - to the prom */ + save_flags( flags ); + restore_flags( flags|MSR_IP ); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( i != 0 ) i++; + panic("restart failed\n"); +} + +int +get_cpuinfo(char *buffer) +{ + extern char *Motherboard_map_name; + int i; + int pvr = _get_PVR(); + int len; + char *model; + PTE *ptr; + unsigned long kptes = 0, uptes = 0, overflow = 0; + unsigned int ti; + + + switch (pvr>>16) + { + case 1: + model = "601"; + break; + case 3: + model = "603"; + break; + case 4: + model = "604"; + break; + case 6: + model = "603e"; + break; + case 7: + model = "603ev"; + break; + default: + model = "unknown"; + break; + } + +#ifdef __SMP__ +#define CD(X) (cpu_data[n].X) +#else +#define CD(X) (X) +#define CPUN 0 +#endif + + len = sprintf(buffer,"processor\t: %d\n" + "cpu\t\t: %s\n" + "revision\t: %d.%d\n" + "upgrade\t\t: %s\n" + "clock\t\t: %dMHz\n" + "bus clock\t: %dMHz\n" + "machine\t\t: %s (sn %s)\n" + "pci map\t\t: %s\n", + CPUN, + model, + MAJOR(pvr), MINOR(pvr), + (inb(IBM_EQUIP_PRESENT) & 2) ? "not upgrade" : "upgrade", + (res.VitalProductData.ProcessorHz > 1024) ? + res.VitalProductData.ProcessorHz>>20 : + res.VitalProductData.ProcessorHz, + (res.VitalProductData.ProcessorBusHz > 1024) ? + res.VitalProductData.ProcessorBusHz>>20 : + res.VitalProductData.ProcessorBusHz, + res.VitalProductData.PrintableModel, + res.VitalProductData.Serial, + Motherboard_map_name + ); + + /* print info about SIMMs */ + len += sprintf(buffer+len,"simms\t\t: "); + for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ ) + { + if ( res.Memories[i].SIMMSize != 0 ) + len += sprintf(buffer+len,"%d:%dM ",i, + (res.Memories[i].SIMMSize > 1024) ? + res.Memories[i].SIMMSize>>20 : + res.Memories[i].SIMMSize); + } + len += sprintf(buffer+len,"\n"); + + /* TLB */ + len += sprintf(buffer+len,"tlb\t\t:"); + switch(res.VitalProductData.TLBAttrib) + { + case CombinedTLB: + len += sprintf(buffer+len," %d entries\n", + res.VitalProductData.TLBSize); + break; + case SplitTLB: + len += sprintf(buffer+len," (split I/D) %d/%d entries\n", + res.VitalProductData.I_TLBSize, + res.VitalProductData.D_TLBSize); + break; + case NoneTLB: + len += sprintf(buffer+len," not present\n"); + break; + } + + /* L1 */ + len += sprintf(buffer+len,"l1\t\t: "); + switch(res.VitalProductData.CacheAttrib) + { + case CombinedCAC: + len += sprintf(buffer+len,"%dkB LineSize\n", + res.VitalProductData.CacheSize, + res.VitalProductData.CacheLineSize); + break; + case SplitCAC: + len += sprintf(buffer+len,"(split I/D) %dkB/%dkB Linesize %dB/%dB\n", + res.VitalProductData.I_CacheSize, + res.VitalProductData.D_CacheSize, + res.VitalProductData.D_CacheLineSize, + res.VitalProductData.D_CacheLineSize); + break; + case NoneCAC: + len += sprintf(buffer+len,"not present\n"); + break; + } + + /* L2 */ + if ( (inb(IBM_EQUIP_PRESENT) & 1) == 0) /* l2 present */ + { + int size; + + len += sprintf(buffer+len,"l2\t\t: %dkB %s\n", + ((inb(IBM_L2_STATUS) >> 5) & 1) ? 512 : 256, + (inb(IBM_SYS_CTL) & 64) ? "enabled" : "disabled"); + } + else + { + len += sprintf(buffer+len,"l2\t\t: not present\n"); + } + + + len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", + CD(loops_per_sec+2500)/500000, + (CD(loops_per_sec+2500)/5000) % 100); + + /* + * Ooh's and aah's info about zero'd pages in idle task + */ + { + extern unsigned int zerocount, zerototal, zeropage_hits; + len += sprintf(buffer+len,"zero pages\t: total %u (%uKb) " + "current: %u (%uKb) hits: %u\n", + zerototal, (zerototal*PAGE_SIZE)>>10, + zerocount, (zerocount*PAGE_SIZE)>>10, + zeropage_hits); + } + + + /* ram/hash table info */ + len += sprintf(buffer+len,"hash table\t: %dkB (%dk buckets)\n", + Hash_size>>10,(Hash_size/(sizeof(PTE)*8)) >> 10); + + /* if booted print info about hash table use (overflows, etc) */ +#ifdef HASHSTATS + for ( ptr = Hash ; ptr < (PTE *)(Hash+Hash_size) ; ptr++) + { + if (ptr->v) + { + /* user not allowed read or write */ + if (ptr->pp == PP_RWXX) + kptes++; + else + uptes++; + if (ptr->h == 1) + overflow++; + } + } + /*len+=sprintf(buffer+len,"Hash %x Hash+Hash_size %x MemEnd %x\n", + Hash,Hash+Hash_size,KERNELBASE+_TotalMemory);*/ + /*len += sprintf(buffer+len,"PTEs: (user/kernel/max) %d (%d%%)/%d " + "(%d%%)/%d (%d%% full)\n", + uptes,(uptes*100)/(Hash_size/sizeof(PTE)), + kptes,(kptes*100)/(Hash_size/sizeof(PTE)), + Hash_size/sizeof(PTE), + ((uptes+kptes)*100)/(Hash_size/sizeof(PTE))); + len += sprintf(buffer+len,"Current Ovflw PTE's: %d Total Evicts: %u\n", + overflow,evicts);*/ +#endif /* HASHSTATS */ + return len; +} + +__initfunc(unsigned long +bios32_init(unsigned long memory_start, unsigned long memory_end)) +{ + return memory_start; +} + +__initfunc(void +setup_arch(char **cmdline_p, unsigned long * memory_start_p, + unsigned long * memory_end_p)) +{ + extern char cmd_line[]; + extern char _etext[], _edata[], _end[]; + unsigned char reg; + extern int panic_timeout; + + /* Save unparsed command line copy for /proc/cmdline */ + strcpy( saved_command_line, cmd_line ); + *cmdline_p = cmd_line; + + *memory_start_p = (unsigned long) Hash+Hash_size; + (unsigned long *)*memory_end_p = (unsigned long *)(_TotalMemory+KERNELBASE); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_sec = 50000000; + + /* reboot on panic */ + /*panic_timeout = 180;*/ + + init_task.mm->start_code = PAGE_OFFSET; + init_task.mm->end_code = (unsigned long) _etext; + init_task.mm->end_data = (unsigned long) _edata; + init_task.mm->brk = (unsigned long) _end; + + aux_device_present = 0xaa; + + switch ( _machine ) + { + case _MACH_IBM: + ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ + break; + case _MACH_Motorola: + ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ + break; + } + /*ROOT_DEV = to_kdev_t(0x0811);*/ /* sdb1 */ +#if 0 + strcpy(cmd_line+strlen(cmd_line),"console=1,9600,n8"); +#endif + +#if 0 + if ( _machine == _MACH_Motorola ) + { + /* get root via nfs from gordito -- only used for testing */ + ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255); /* nfs */ + /*nfsaddrs=myip:serverip:gateip:netmaskip:clientname*/ + strcpy(cmd_line+strlen(cmd_line), + "nfsaddrs=129.138.6.13:129.138.6.101:129.138.6.1:255.255.255.0:" + "pandora nfsroot=/usr/src/root/"); + } +#endif + +#ifdef CONFIG_BLK_DEV_RAM +#if 0 + ROOT_DEV = to_kdev_t(0x0200); /* floppy */ + rd_prompt = 1; + rd_doload = 1; + rd_image_start = 0; +#endif +#endif + + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +} + diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/prep_time.c linux/arch/ppc/kernel/prep_time.c --- v2.1.47/linux/arch/ppc/kernel/prep_time.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/prep_time.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,315 @@ +/* + * linux/arch/i386/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * Adapted for PowerPC (PreP) by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * copied and modified from intel version + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* last time the cmos clock got updated */ +static long last_rtc_update = 0; +static int set_rtc_mmss(unsigned long nowtime); +unsigned long get_cmos_time(void); +static inline unsigned long mktime(unsigned int, unsigned int,unsigned int, + unsigned int, unsigned int, unsigned int); +#define TIMER_IRQ 0 + +/* Cycle counter value at the previous timer interrupt.. */ +static unsigned long long last_timer_cc = 0; +static unsigned long long init_timer_cc = 0; + +#define TICK_SIZE tick +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static unsigned long do_slow_gettimeoffset(void) +{ + int count; + unsigned long offset = 0; + + /* timer count may underflow right here */ + outb_p(0x00, 0x43); /* latch the count ASAP */ + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + /* we know probability of underflow is always MUCH less than 1% */ + if (count > (LATCH - LATCH/100)) { + /* check for pending timer interrupt */ + outb_p(0x0a, 0x20); + if (inb(0x20) & 1) + offset = TICK_SIZE; + } + count = ((LATCH-1) - count) * TICK_SIZE; + count = (count + LATCH/2) / LATCH; + return offset + count; +} + +static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; + +/* + * This version of gettimeofday has near microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + save_flags(flags); + cli(); + *tv = xtime; + tv->tv_usec += do_gettimeoffset(); + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } + restore_flags(flags); +} + +void do_settimeofday(struct timeval *tv) +{ + cli(); + tv->tv_usec -= do_gettimeoffset(); + + if (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_state = TIME_ERROR; + time_maxerror = 0x70000000; + time_esterror = 0x70000000; + sti(); +} + +void to_tm(int tim, struct rtc_time * tm) +{ + register int i; + register long hms, day; + + day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) + day -= days_in_year(i); + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) + days_in_month(FEBRUARY) = 29; + for (i = 1; day >= days_in_month(i); i++) + day -= days_in_month(i); + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; +} + +/* + * Set the hardware clock. -- Cort + */ +static int set_rtc_mmss(unsigned long nowtime) +{ + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + unsigned char save_control, save_freq_select; + struct rtc_time tm; + + to_tm(nowtime, &tm); + + save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year -= 1900; + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + CMOS_WRITE(tm.tm_sec,RTC_SECONDS); + CMOS_WRITE(tm.tm_min,RTC_MINUTES); + CMOS_WRITE(tm.tm_hour,RTC_HOURS); + CMOS_WRITE(tm.tm_mon,RTC_MONTH); + CMOS_WRITE(tm.tm_mday,RTC_DAY_OF_MONTH); + CMOS_WRITE(tm.tm_year,RTC_YEAR); + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + + if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) + time_state = TIME_OK; + return 0; +} + +unsigned long get_cmos_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + /* read RTC exactly on falling edge of update flag */ + for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) + break; + do { /* Isn't this overkill ? UIP above should guarantee consistency */ + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static inline void timer_interrupt(int irq, void *dev, struct pt_regs * regs) +{ + do_timer(regs); + + /* update the hw clock if: + * the time is marked out of sync (TIME_ERROR) + * or ~11 minutes have expired since the last update -- Cort + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ( time_state == TIME_BAD || + xtime.tv_sec > last_rtc_update + 660 ) + /*if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec > 500000 - (tick >> 1) && + xtime.tv_usec < 500000 + (tick >> 1))*/ + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + + +#ifdef CONFIG_HEARTBEAT + /* use hard disk LED as a heartbeat instead -- much more useful + for debugging -- Cort */ + switch(kstat.interrupts[0] % 101) + { + /* act like an actual heart beat -- ie thump-thump-pause... */ + case 0: + case 20: + outb(1,IBM_HDD_LED); + break; + case 7: + case 27: + outb(0,IBM_HDD_LED); + break; + case 100: + break; + } +#endif +} + + +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +static inline unsigned long mktime(unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((( + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + +void time_init(void) +{ + void (*irq_handler)(int, void *,struct pt_regs *); + xtime.tv_sec = get_cmos_time(); + xtime.tv_usec = 0; + + /* If we have the CPU hardware time counters, use them */ + irq_handler = timer_interrupt; + if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL) != 0) + panic("Could not allocate timer IRQ!"); +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c --- v2.1.47/linux/arch/ppc/kernel/process.c Tue May 13 22:41:03 1997 +++ linux/arch/ppc/kernel/process.c Thu Jul 31 13:09:17 1997 @@ -1,13 +1,20 @@ /* * linux/arch/ppc/kernel/process.c * - * Copyright (C) 1995 Linus Torvalds - * Adapted for PowerPC by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) - */ - -/* - * This file handles the architecture-dependent parts of process handling.. + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/process.c" + * Copyright (C) 1995 Linus Torvalds + * + * Modified by Cort Dougan (cort@cs.nmt.edu) and + * Paul Mackerras (paulus@cs.anu.edu.au) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * */ #include @@ -22,173 +29,449 @@ #include #include #include +#include #include #include #include #include +#include + +int dump_fpu(void); +void switch_to(struct task_struct *, struct task_struct *); +void print_backtrace(unsigned long *); +void show_regs(struct pt_regs * regs); +void inline zero_paged(void); +extern unsigned long _get_SP(void); -#include +#undef SHOW_TASK_SWITCHES 1 +#undef CHECK_STACK 1 +#undef IDLE_ZERO 1 -/* - * Initial task structure. Make this a per-architecture thing, - * because different architectures tend to have different - * alignment requirements and potentially different initial - * setup. - */ -static unsigned long init_kernel_stack[1024] = { STACK_MAGIC, }; -unsigned long init_user_stack[1024] = { STACK_MAGIC, }; +unsigned long +kernel_stack_top(struct task_struct *tsk) +{ + return ((unsigned long)tsk) + sizeof(union task_union); +} + +unsigned long +task_top(struct task_struct *tsk) +{ + return ((unsigned long)tsk) + sizeof(struct task_struct); +} + static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; -struct task_struct init_task = INIT_TASK; - - -int dump_fpu(void); -void hard_reset_now(void); -void switch_to(struct task_struct *, struct task_struct *); -void copy_thread(int,unsigned long,unsigned long,struct task_struct *, - struct pt_regs *); -void print_backtrace(unsigned long *); +union task_union init_task_union = { INIT_TASK }; int dump_fpu(void) { - return (1); + return (1); } - /* check to make sure the kernel stack is healthy */ int check_stack(struct task_struct *tsk) { - extern unsigned long init_kernel_stack[PAGE_SIZE/sizeof(long)]; - int ret = 0; - int i; - - /* skip check in init_kernel_task -- swapper */ - if ( tsk->kernel_stack_page == (unsigned long)&init_kernel_stack ) - return; - /* check bounds on stack -- above/below kstack page */ - if ( (tsk->tss.ksp-1 & KERNEL_STACK_MASK) != tsk->kernel_stack_page ) - { - printk("check_stack(): not in bounds %s/%d ksp %x/%x\n", - tsk->comm,tsk->pid,tsk->tss.ksp,tsk->kernel_stack_page); - ret |= 1; - } - - /* check for magic on kstack */ - if ( *(unsigned long *)(tsk->kernel_stack_page) != STACK_MAGIC) - { - printk("check_stack(): no magic %s/%d ksp %x/%x magic %x\n", - tsk->comm,tsk->pid,tsk->tss.ksp,tsk->kernel_stack_page, - *(unsigned long *)(tsk->kernel_stack_page)); - ret |= 2; - } - -#ifdef KERNEL_STACK_BUFFER - /* check extra padding page under kernel stack */ - for ( i = PAGE_SIZE/sizeof(long) ; i >= 1; i--) - { - struct pt_regs *regs; - - if ( *((unsigned long *)(tsk->kernel_stack_page)-1) ) - { - printk("check_stack(): padding touched %s/%d ksp %x/%x value %x/%d\n", - tsk->comm,tsk->pid,tsk->tss.ksp,tsk->kernel_stack_page, - *(unsigned long *)(tsk->kernel_stack_page-i),i*sizeof(long)); - regs = (struct pt_regs *)(tsk->kernel_stack_page-(i*sizeof(long))); - printk("marker %x trap %x\n", regs->marker,regs->trap); - print_backtrace((unsigned long *)(tsk->tss.ksp)); - - ret |= 4; - break; - } - } + unsigned long stack_top = kernel_stack_top(tsk); + unsigned long tsk_top = task_top(tsk); + int ret = 0; + unsigned long *i; + +#if 0 + /* check tss magic */ + if ( tsk->tss.magic != TSS_MAGIC ) + { + ret |= 1; + printk("tss.magic bad: %08x\n", tsk->tss.magic); + } #endif - -#if 0 - if (ret) - panic("bad stack"); + + if ( !tsk ) + printk("check_stack(): tsk bad tsk %p\n",tsk); + + /* check if stored ksp is bad */ + if ( (tsk->tss.ksp > stack_top) || (tsk->tss.ksp < tsk_top) ) + { + printk("stack out of bounds: %s/%d\n" + " tsk_top %08x ksp %08x stack_top %08x\n", + tsk->comm,tsk->pid, + tsk_top, tsk->tss.ksp, stack_top); + ret |= 2; + } + + /* check if stack ptr RIGHT NOW is bad */ + if ( (tsk == current) && ((_get_SP() > stack_top ) || (_get_SP() < tsk_top)) ) + { + printk("current stack ptr out of bounds: %s/%d\n" + " tsk_top %08x sp %08x stack_top %08x\n", + current->comm,current->pid, + tsk_top, _get_SP(), stack_top); + ret |= 4; + } + +#if 0 + /* check amount of free stack */ + for ( i = (unsigned long *)task_top(tsk) ; i < kernel_stack_top(tsk) ; i++ ) + { + if ( !i ) + printk("check_stack(): i = %p\n", i); + if ( *i != 0 ) + { + /* only notify if it's less than 900 bytes */ + if ( (i - (unsigned long *)task_top(tsk)) < 900 ) + printk("%d bytes free on stack\n", + i - task_top(tsk)); + break; + } + } #endif - return(ret); -} + if (ret) + { + panic("bad kernel stack"); + } + return(ret); +} void switch_to(struct task_struct *prev, struct task_struct *new) { - struct pt_regs *regs; struct thread_struct *new_tss, *old_tss; int s = _disable_interrupts(); - regs = (struct pt_regs *)(new->tss.ksp); -#if 1 + struct pt_regs *regs = (struct pt_regs *)(new->tss.ksp+STACK_FRAME_OVERHEAD); + +#if CHECK_STACK check_stack(prev); check_stack(new); #endif - /* if a process has used fp 15 times, then turn - on the fpu for good otherwise turn it on with the fp - exception handler as needed. - skip this for kernel tasks. - -- Cort */ - if ( (regs->msr & MSR_FP)&&(regs->msr & MSR_PR)&&(new->tss.fp_used < 15) ) - { -#if 0 - printk("turning off fpu: %s/%d fp_used %d\n", - new->comm,new->pid,new->tss.fp_used); -#endif - regs->msr = regs->msr & ~MSR_FP; - } -#if 0 - printk("%s/%d -> %s/%d\n",prev->comm,prev->pid,new->comm,new->pid); + /* turn off fpu for task last to run */ + /*prev->tss.regs->msr &= ~MSR_FP;*/ + +#ifdef SHOW_TASK_SWITCHES + printk("%s/%d (%x) -> %s/%d (%x) ctx %x\n", + prev->comm,prev->pid,prev->tss.regs->nip, + new->comm,new->pid,new->tss.regs->nip,new->mm->context); #endif new_tss = &new->tss; old_tss = ¤t->tss; - current_set[0] = new; - _switch(old_tss, new_tss); + _switch(old_tss, new_tss, new->mm->context); + /* turn off fpu for task last to run */ _enable_interrupts(s); } + +#include +asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs) +{ +#if 1 + struct task_struct *p; + printk("sys_debug(): r3 %x r4 %x r5 %x r6 %x\n", a,b,c,d); + printk("last %x\n", last_task_used_math); + printk("cur %x regs %x/%x tss %x/%x\n", + current, current->tss.regs,regs,¤t->tss,current->tss); + for_each_task(p) + { + if ((long)p < KERNELBASE) + { + printk("nip %x lr %x r3 %x\n", regs->nip,regs->link,a); + print_mm_info(); + __cli(); + while(1); + } + } + return regs->gpr[3]; +#endif +#if 0 + /* set the time in the cmos clock */ + unsigned long hwtime, nowtime; + struct rtc_time tm; + + hwtime = get_cmos_time(); + to_tm(hwtime, &tm); + printk("hw: H:M:S M/D/Y %02d:%02d:%02d %d/%d/%d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_mon, + tm.tm_mday, tm.tm_year); + return; +#endif +} -asmlinkage int sys_debug(unsigned long r3) +/* + * vars for idle task zero'ing out pages + */ +unsigned long zero_list = 0; /* head linked list of pre-zero'd pages */ +unsigned long bytecount = 0; /* pointer into the currently being zero'd page */ +unsigned long zerocount = 0; /* # currently pre-zero'd pages */ +unsigned long zerototal = 0; /* # pages zero'd over time -- for ooh's and ahhh's */ +unsigned long pageptr = 0; /* current page being zero'd */ +unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */ + +/* + * Returns a pre-zero'd page from the list otherwise returns + * NULL. + */ +unsigned long get_prezerod_page(void) { - lock_kernel(); - if (!strcmp(current->comm,"crashme")) - printk("sys_debug(): r3 (syscall) %d\n", r3); - unlock_kernel(); + unsigned long page; + unsigned long s; + + if ( zero_list ) + { + /* atomically remove this page from the list */ + asm ( "101:lwarx %1,0,%2\n" /* reserve zero_list */ + " lwz %0,0(%1)\n" /* get next -- new zero_list */ + " stwcx. %0,0,%2\n" /* update zero_list */ + " bne- 101b\n" /* if lost reservation try again */ + : "=&r" (zero_list), "=&r" (page) + : "r" (&zero_list) + : "cc" ); + /* we can update zerocount after the fact since it is not + * used for anything but control of a loop which doesn't + * matter since it won't effect anything if it zero's one + * less page -- Cort + */ + atomic_inc((atomic_t *)&zeropage_hits); + atomic_dec((atomic_t *)&zerocount); + /* zero out the pointer to next in the page */ + *(unsigned long *)page = 0; + return page; + } return 0; } +/* + * Experimental stuff to zero out pages in the idle task + * to speed up get_free_pages() -- Cort + * Zero's out pages until we need to resched or + * we've reached the limit of zero'd pages. + */ +void inline zero_paged(void) +{ + extern pte_t *get_pte( struct mm_struct *mm, unsigned long address ); + unsigned long tmp; + pte_t ptep; + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + + sprintf(current->comm, "zero_paged"); + printk("Started zero_paged\n"); + /* want priority over idle task and powerd */ + current->priority = -98; + current->counter = -98; + __sti(); + + while ( zerocount < 128 ) + { + /* + * Mark a page as reserved so we can mess with it + * If we're interrupted we keep this page and our place in it + * since we validly hold it and it's reserved for us. + */ + pageptr = __get_free_pages(GFP_ATOMIC, 0, 0 ); + if ( !pageptr ) + { + printk("!pageptr in zero_paged\n"); + goto retry; + } + + if ( need_resched ) + schedule(); + + /* + * Make the page no cache so we don't blow our cache with 0's + */ + dir = pgd_offset( init_task.mm, pageptr ); + if (dir) + { + pmd = pmd_offset(dir, pageptr & PAGE_MASK); + if (pmd && pmd_present(*pmd)) + { + pte = pte_offset(pmd, pageptr & PAGE_MASK); + if (pte && pte_present(*pte)) + { + pte_uncache(*pte); + flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); + } + } + } + + /* + * Important here to not take time away from real processes. + */ + for ( bytecount = 0; bytecount < PAGE_SIZE ; bytecount += 4 ) + { + if ( need_resched ) + schedule(); + *(unsigned long *)(bytecount + pageptr) = 0; + } + + /* + * If we finished zero-ing out a page add this page to + * the zero_list atomically -- we can't use + * down/up since we can't sleep in idle. + * Disabling interrupts is also a bad idea since we would + * steal time away from real processes. + * We can also have several zero_paged's running + * on different processors so we can't interfere with them. + * So we update the list atomically without locking it. + * -- Cort + */ + /* turn cache on for this page */ + pte_cache(*pte); + flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); + + /* atomically add this page to the list */ + asm ( "101:lwarx %0,0,%1\n" /* reserve zero_list */ + " stw %0,0(%2)\n" /* update *pageptr */ +#ifdef __SMP__ + " sync\n" /* let store settle */ +#endif + " mr %0,%2\n" /* update zero_list in reg */ + " stwcx. %2,0,%1\n" /* update zero_list in mem */ + " bne- 101b\n" /* if lost reservation try again */ + : "=&r" (zero_list) + : "r" (&zero_list), "r" (pageptr) + : "cc" ); + /* + * This variable is used in the above loop and nowhere + * else so the worst that could happen is we would + * zero out one more or one less page than we want + * per processor on the machine. This is because + * we could add our page to the list but not have + * zerocount updated yet when another processor + * reads it. -- Cort + */ + atomic_inc((atomic_t *)&zerocount); + atomic_inc((atomic_t *)&zerototal); +retry: + schedule(); + } +} + +void powerd(void) +{ + unsigned long msr, hid0; + + sprintf(current->comm, "powerd"); + __sti(); + while (1) + { + /* want priority over idle task 'swapper' -- Cort */ + current->priority = -99; + current->counter = -99; + asm volatile( + /* clear powersaving modes and set nap mode */ + "mfspr %3,1008 \n\t" + "andc %3,%3,%4 \n\t" + "or %3,%3,%5 \n\t" + "mtspr 1008,%3 \n\t" + /* enter the mode */ + "mfmsr %0 \n\t" + "oris %0,%0,%2 \n\t" + "sync \n\t" + "mtmsr %0 \n\t" + "isync \n\t" + : "=&r" (msr) + : "0" (msr), "i" (MSR_POW>>16), + "r" (hid0), + "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP), + "r" (HID0_NAP)); + if ( need_resched ) + schedule(); + /* + * The ibm carolina spec says that the eagle memory + * controller will detect the need for a snoop + * and wake up the processor so we don't need to + * check for cache operations that need to be + * snooped. The ppc book says the run signal + * must be asserted while napping for this though. + * -- Cort + */ + } +} + asmlinkage int sys_idle(void) { int ret = -EPERM; - - lock_kernel(); if (current->pid != 0) goto out; - /* endless idle loop with no priority at all */ +#ifdef IDLE_ZERO + /* + * want one per cpu since it would be nice to have all + * processors who aren't doing anything + * zero-ing pages since this daemon is lock-free + * -- Cort + */ + kernel_thread(zero_paged, NULL, 0); +#endif /* IDLE_ZERO */ + +#ifdef CONFIG_POWERSAVING + /* no powersaving modes on 601 - one per processor */ + if( (_get_PVR()>>16) != 1 ) + kernel_thread(powerd, NULL, 0); +#endif /* CONFIG_POWERSAVING */ + + /* endless loop with no priority at all */ + current->priority = -100; current->counter = -100; - for (;;) { + for (;;) + { schedule(); } ret = 0; out: - unlock_kernel(); return ret; } void show_regs(struct pt_regs * regs) { + int i; + + printk("NIP: %08X XER: %08X LR: %08X REGS: %08X TRAP: %04x\n", + regs->nip, regs->xer, regs->link, regs,regs->trap); + printk("MSR: %08x EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + printk("TASK = %x[%d] '%s' mm->pgd %08X ", + current, current->pid, current->comm, current->mm->pgd); + printk("Last syscall: %d ", current->tss.last_syscall); + printk("\nlast math %08X\n", last_task_used_math); + for (i = 0; i < 32; i++) + { + long r; + if ((i % 8) == 0) + { + printk("GPR%02d: ", i); + } + + if ( get_user(r, &(regs->gpr[i])) ) + goto out; + printk("%08X ", r); + if ((i % 8) == 7) + { + printk("\n"); + } + } +out: } void exit_thread(void) { + if (last_task_used_math == current) + last_task_used_math = NULL; } void flush_thread(void) { + if (last_task_used_math == current) + last_task_used_math = NULL; } void @@ -197,50 +480,25 @@ } /* - * Copy a thread.. - */ -int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, - struct task_struct * p, struct pt_regs * regs) + * Copy a thread.. + */ +int +copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + struct task_struct * p, struct pt_regs * regs) { int i; - SEGREG *segs; struct pt_regs * childregs; - - /* Construct segment registers */ - segs = (SEGREG *)(p->tss.segs); - for (i = 0; i < 8; i++) - { - segs[i].ks = 0; - segs[i].kp = 1; -#if 0 - segs[i].vsid = i | (nr << 4); -#else - segs[i].vsid = i | ((nr * 10000) << 4); -#endif - } - if ((p->mm->context == 0) || (p->mm->count == 1)) - { -#if 0 - p->mm->context = ((nr)<<4); -#else - p->mm->context = ((nr*10000)<<4); -#endif - } - - /* Last 8 are shared with kernel & everybody else... */ - for (i = 8; i < 16; i++) - { - segs[i].ks = 0; - segs[i].kp = 1; - segs[i].vsid = i; - } - /* Copy registers */ - childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 2; + childregs = ((struct pt_regs *) + ((unsigned long)p + sizeof(union task_union) + - STACK_FRAME_OVERHEAD)) - 2; + *childregs = *regs; - *childregs = *regs; /* STRUCT COPY */ + if ((childregs->msr & MSR_PR) == 0) + childregs->gpr[2] = (unsigned long) p; /* `current' in new task */ childregs->gpr[3] = 0; /* Result from fork() */ - p->tss.ksp = (unsigned long)(childregs); + p->tss.ksp = (unsigned long)(childregs) - STACK_FRAME_OVERHEAD; + p->tss.regs = (struct pt_regs *)(childregs); if (usp >= (unsigned long)regs) { /* Stack is in kernel space - must adjust */ childregs->gpr[1] = (long)(childregs+1); @@ -248,23 +506,29 @@ { /* Provided stack is in user space */ childregs->gpr[1] = usp; } - p->tss.fp_used = 0; - return 0; -} + p->tss.last_syscall = -1; -/* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ + /* + * copy fpu info - assume lazy fpu switch now always + * this should really be conditional on whether or + * not the process has used the fpu + * -- Cort + */ + if ( last_task_used_math == current ) + giveup_fpu(); + + memcpy(&p->tss.fpr, ¤t->tss.fpr, sizeof(p->tss.fpr)); + p->tss.fpscr = current->tss.fpscr; + childregs->msr &= ~MSR_FP; + + return 0; } - -asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) +asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) { int ret; - lock_kernel(); ret = do_fork(SIGCHLD, regs->gpr[1], regs); unlock_kernel(); @@ -272,46 +536,21 @@ } 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) + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs *regs) { int error; char * filename; - - lock_kernel(); - /* getname does it's own verification of the address - when it calls get_max_filename() but - it will assume it's valid if get_fs() == KERNEL_DS - which is always true on the ppc so we check - it here - - this doesn't completely check any of these data structures, - it just makes sure that the 1st long is in a good area - and from there we assume that it's safe then - -- Cort - */ - /* works now since get_fs/set_fs work properly */ -#if 0 - if ( verify_area(VERIFY_READ,(void *)a0,1) - && verify_area(VERIFY_READ,(void *)a1,1) - && verify_area(VERIFY_READ,(void *)a2,1) - ) - { - return -EFAULT; - } -#endif - error = getname((char *) a0, &filename); - if (error) + filename = (int) getname((char *) a0); + error = PTR_ERR(filename); + if(IS_ERR(filename)) goto out; - flush_instruction_cache(); + if ( last_task_used_math == current ) + last_task_used_math = NULL; error = do_execve(filename, (char **) a1, (char **) a2, regs); -#if 0 -if (error) -{ -printk("EXECVE - file = '%s', error = %d\n", filename, error); -} -#endif + putname(filename); + out: unlock_kernel(); return error; @@ -321,7 +560,7 @@ { unsigned long clone_flags = p1; int res; - + lock_kernel(); res = do_fork(clone_flags, regs->gpr[1], regs); unlock_kernel(); @@ -331,13 +570,17 @@ void print_backtrace(unsigned long *sp) { -#if 0 int cnt = 0; - printk("... Call backtrace:\n"); - while (verify_area(VERIFY_READ,sp,sizeof(long)) && *sp) + int i; + printk("Call backtrace: "); + while ( !get_user(i, sp) && i) { - printk("%08X ", sp[1]); - sp = (unsigned long *)*sp; + if ( get_user( i, &sp[1] ) ) + return; + printk("%08X ", i); + if ( get_user( (ulong)sp, sp) ) + return; + if (cnt == 6 ) cnt = 7; /* wraparound early -- Cort */ if (++cnt == 8) { printk("\n"); @@ -345,43 +588,66 @@ if (cnt > 32) break; } printk("\n"); -#endif -} - -void -print_user_backtrace(unsigned long *sp) -{ -#if 0 - int cnt = 0; - printk("... [User] Call backtrace:\n"); - while (verify_area(VERIFY_READ,sp,sizeof(long)) && *sp) - { - printk("%08X ", sp[1]); - sp = (unsigned long *)*sp; - if (++cnt == 8) - { - printk("\n"); - } - if (cnt > 16) break; - } - printk("\n"); -#endif } -void -print_kernel_backtrace(void) -{ -#if 0 - unsigned long *_get_SP(void); - print_backtrace(_get_SP()); -#endif -} inline void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp) { + set_fs(USER_DS); regs->nip = eip; regs->gpr[1] = esp; regs->msr = MSR_USER; - set_fs(USER_DS); } +/* + * Low level print for debugging - Cort + */ +int ll_printk(const char *fmt, ...) +{ + va_list args; + char buf[256]; + int i; + + va_start(args, fmt); + i=sprintf(buf,fmt,args); + ll_puts(buf); + va_end(args); + return i; +} + +char *vidmem = (char *)0xC00B8000; +int lines = 24, cols = 80; +int orig_x = 0, orig_y = 0; + +void ll_puts(const char *s) +{ + int x,y; + char c; + + x = orig_x; + y = orig_y; + + while ( ( c = *s++ ) != '\0' ) { + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + /*scroll();*/ + /*y--;*/ + y = 0; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + /*scroll();*/ + /*y--;*/ + y = 0; + } + } + } + } + + orig_x = x; + orig_y = y; +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/ptrace.c linux/arch/ppc/kernel/ptrace.c --- v2.1.47/linux/arch/ppc/kernel/ptrace.c Tue May 13 22:41:03 1997 +++ linux/arch/ppc/kernel/ptrace.c Thu Jul 31 13:09:17 1997 @@ -1,12 +1,14 @@ /* * linux/arch/ppc/kernel/ptrace.c * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" * Copyright (C) 1994 by Hamish Macdonald * Taken from linux/kernel/ptrace.c and modified for M680x0. * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds * - * Adapted from 'linux/arch/m68k/kernel/ptrace.c' - * PowerPC version by Gary Thomas (gdt@linuxppc.org) * Modified by Cort Dougan (cort@cs.nmt.edu) * * This file is subject to the terms and conditions of the GNU General @@ -34,52 +36,13 @@ * in exit.c or in signal.c. */ -/* Find the stack offset for a register, relative to tss.ksp. */ -#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) -/* Mapping from PT_xxx to the stack offset at which the register is - saved. Notice that usp has no stack-slot and needs to be treated - specially (see get_reg/put_reg below). */ -static int regoff[] = { -}; - /* * Get contents of register REGNO in task TASK. */ static inline long get_reg(struct task_struct *task, int regno) { - struct pt_regs *regs = task->tss.regs; - if (regno <= PT_R31) - { - return (regs->gpr[regno]); - } else - if (regno == PT_NIP) - { - return (regs->nip); - } else - if (regno == PT_MSR) - { - return (regs->msr); - } else - if (regno == PT_ORIG_R3) - { - return (regs->orig_gpr3); - } else - if (regno == PT_CTR) - { - return (regs->ctr); - } else - if (regno == PT_LNK) - { - return (regs->link); - } else - if (regno == PT_XER) - { - return (regs->xer); - } else - if (regno == PT_CCR) - { - return (regs->ccr); - } + if (regno <= PT_CCR) + return ((unsigned long *)task->tss.regs)[regno]; return (0); } @@ -89,52 +52,21 @@ static inline int put_reg(struct task_struct *task, int regno, unsigned long data) { - struct pt_regs *regs = task->tss.regs; - if (regno <= PT_R31) - { - regs->gpr[regno] = data; - } else - if (regno == PT_NIP) - { - regs->nip = data; - } else - if (regno == PT_MSR) - { - regs->msr = data; - } else - if (regno == PT_CTR) - { - regs->ctr = data; - } else - if (regno == PT_LNK) - { - regs->link = data; - } else - if (regno == PT_XER) - { - regs->xer = data; - } else - if (regno == PT_CCR) - { - regs->ccr = data; - } else - { /* Invalid register */ - return (-1); + if (regno <= PT_CCR) { + ((unsigned long *)task->tss.regs)[regno] = data; + return 0; } - return (0); + return -1; } -static inline +static inline void set_single_step(struct task_struct *task) { struct pt_regs *regs = task->tss.regs; -printk("Set single step - Task: %x, Regs: %x", task, regs); -printk(", MSR: %x/", regs->msr); regs->msr |= MSR_SE; -printk("%x\n", regs->msr); } -static inline +static inline void clear_single_step(struct task_struct *task) { struct pt_regs *regs = task->tss.regs; @@ -159,33 +91,32 @@ repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - do_no_page(tsk, vma, addr, 0); + handle_mm_fault(tsk, vma, addr, 0); goto repeat; } if (pgd_bad(*pgdir)) { - printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); + printk("ptrace[1]: bad page directory %lx\n", pgd_val(*pgdir)); pgd_clear(pgdir); return 0; } pgmiddle = pmd_offset(pgdir,addr); if (pmd_none(*pgmiddle)) { - do_no_page(tsk, vma, addr, 0); + handle_mm_fault(tsk, vma, addr, 0); goto repeat; } if (pmd_bad(*pgmiddle)) { - printk("ptrace: bad page directory %08lx\n", - pmd_val(*pgmiddle)); + printk("ptrace[3]: bad pmd %lx\n", pmd_val(*pgmiddle)); pmd_clear(pgmiddle); return 0; } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - do_no_page(tsk, vma, addr, 0); + handle_mm_fault(tsk, vma, addr, 0); goto repeat; } page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ - if (page >= high_memory) + if (MAP_NR(page) >= max_mapnr) return 0; page += addr & ~PAGE_MASK; return *(unsigned long *) page; @@ -200,8 +131,8 @@ * Now keeps R/W state of page so that a text page stays readonly * even if a debugger scribbles breakpoints into it. -M.U- */ -static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr, - unsigned long data) +static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, + unsigned long addr, unsigned long data) { pgd_t *pgdir; pmd_t *pgmiddle; @@ -211,42 +142,40 @@ repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - do_no_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } if (pgd_bad(*pgdir)) { - printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); + printk("ptrace[2]: bad page directory %lx\n", pgd_val(*pgdir)); pgd_clear(pgdir); return; } pgmiddle = pmd_offset(pgdir,addr); if (pmd_none(*pgmiddle)) { - do_no_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } if (pmd_bad(*pgmiddle)) { - printk("ptrace: bad page directory %08lx\n", - pmd_val(*pgmiddle)); + printk("ptrace[4]: bad pmd %lx\n", pmd_val(*pgmiddle)); pmd_clear(pgmiddle); return; } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - do_no_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - do_wp_page(tsk, vma, addr, 2); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } /* this is a hack for non-kernel-mapped video buffers and similar */ - if (page < high_memory) { + if (MAP_NR(page) < max_mapnr) *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; - } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ /* this should also re-instate whatever read-only mode there was before */ - *pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot)); + set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); flush_tlb_all(); } @@ -366,7 +295,6 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; - struct user * dummy = NULL; int ret = -EPERM; lock_kernel(); @@ -436,8 +364,10 @@ case PTRACE_PEEKUSR: { unsigned long tmp; - if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) - return -EIO; + if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) { + ret = -EIO; + goto out; + } ret = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); @@ -445,15 +375,19 @@ goto out; tmp = 0; /* Default return condition */ addr = addr >> 2; /* temporary hack. */ - if (addr < PT_FPR0) + if (addr < PT_FPR0) { tmp = get_reg(child, addr); -#if 0 - else if (addr >= PT_FPR0 && addr < PT_FPR31) - tmp = child->tss.fpr[addr - PT_FPR0]; -#endif + } +#if 1 + else if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) { + if (last_task_used_math == child) + giveup_fpu(); + tmp = ((long *)child->tss.fpr)[addr - PT_FPR0]; + } +#endif else ret = -EIO; - if(!ret) + if (!ret) put_user(tmp,(unsigned long *) data); goto out; } @@ -486,13 +420,13 @@ ret = 0; goto out; } -#if 0 - if (addr >= 21 && addr < 48) { - child->tss.fp[addr - 21] = data; + if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) { + if (last_task_used_math == child) + giveup_fpu(); + ((long *)child->tss.fpr)[addr - PT_FPR0] = data; ret = 0; goto out; } -#endif goto out; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c --- v2.1.47/linux/arch/ppc/kernel/setup.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/kernel/setup.c Wed Dec 31 16:00:00 1969 @@ -1,409 +0,0 @@ -/* - * linux/arch/ppc/kernel/setup.c - * - * Copyright (C) 1995 Linus Torvalds - * Adapted from 'alpha' version by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) - */ - -/* - * bootup setup stuff.. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -extern unsigned long *end_of_DRAM; -extern PTE *Hash; -extern unsigned long Hash_size, Hash_mask; -extern int probingmem; -unsigned long empty_zero_page[1024]; - -unsigned char aux_device_present; -#ifdef CONFIG_BLK_DEV_RAM -extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ -extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ -extern int rd_image_start; /* starting block # of image */ -#endif - -#undef HASHSTATS - -extern unsigned long isBeBox[]; - -/* copy of the residual data */ -RESIDUAL res; -unsigned long resptr = 0; /* ptr to residual data from hw */ - -/* - * 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. - */ - /* this is changed only in minor ways from the original - -- Cort - */ -struct screen_info screen_info = { - 0, 25, /* 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 */ - 1, /* orig-video-isVGA */ - 16 /* orig-video-points */ -}; - - -unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) -{ - return memory_start; -} - -#ifdef HASHSTATS -unsigned long *hashhits; -#endif - -extern unsigned long _TotalMemory; -/* find the physical size of RAM and setup hardware hash table */ -unsigned long *find_end_of_memory(void) -{ - extern BAT BAT2; - _TotalMemory = res.TotalMemory; - - if (_TotalMemory == 0 ) - { - printk("Ramsize from residual data was 0 -- Probing for value\n"); - /* this needs be done differently since the bats actually map - addresses beyond physical memory! -- Cort */ -#if 0 - probingmem = 1; - while ( probingmem ) - { - _TotalMemory += 0x00800000; /* 8M */ - *(unsigned long *)_TotalMemory+KERNELBASE; - } - _TotalMemory -= 0x00800000; -#else - _TotalMemory = 0x03000000; -#endif - printk("Ramsize probed to be %dM\n", _TotalMemory>>20); - } - - /* setup BAT2 mapping so that it covers kernelbase to kernelbase+ramsize */ - switch(_TotalMemory) - { - case 0x01000000: /* 16M */ - BAT2.batu.bl = BL_16M; - Hash_size = HASH_TABLE_SIZE_128K; - Hash_mask = HASH_TABLE_MASK_128K; - break; - case 0x00800000: /* 8M */ - BAT2.batu.bl = BL_8M; - Hash_size = HASH_TABLE_SIZE_64K; - Hash_mask = HASH_TABLE_MASK_64K; - break; - case 0x01800000: /* 24M */ - case 0x02000000: /* 32M */ - BAT2.batu.bl = BL_32M; - Hash_size = HASH_TABLE_SIZE_256K; - Hash_mask = HASH_TABLE_MASK_256K; - break; - case 0x03000000: /* 48M */ - case 0x04000000: /* 64M */ - BAT2.batu.bl = BL_64M; - Hash_size = HASH_TABLE_SIZE_512K; - Hash_mask = HASH_TABLE_MASK_512K; - break; - case 0x05000000: /* 80M */ - BAT2.batu.bl = BL_128M; - Hash_size = HASH_TABLE_SIZE_1M; - Hash_mask = HASH_TABLE_MASK_1M; - break; - default: - printk("WARNING: setup.c: find_end_of_memory() unknown total ram size %x\n", - _TotalMemory); - break; - } - - Hash = (PTE *)((_TotalMemory-Hash_size)+KERNELBASE); - bzero(Hash, Hash_size); - - -#ifdef HASHSTATS - hashhits = (unsigned long *)Hash - (Hash_size/sizeof(struct _PTE))/2; - bzero(hashhits, (Hash_size/sizeof(struct _PTE))/2); - return ((unsigned long *)hashhits); -#else - return ((unsigned long *)Hash); -#endif -} - -int size_memory; - -/* - * This is set up by the setup-routine at boot-time - */ -#define EXT_MEM_K (*(unsigned short *) (PARAM+2)) -#ifdef CONFIG_APM -#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64)) -#endif -#define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) -#define SCREEN_INFO (*(struct screen_info *) (PARAM+0)) -#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) -#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) -#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) -#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) -#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210)) -#define KERNEL_START (*(unsigned long *) (PARAM+0x214)) -#define INITRD_START (*(unsigned long *) (PARAM+0x218)) -#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) -#define COMMAND_LINE ((char *) (PARAM+2048)) -#define COMMAND_LINE_SIZE 256 - -#define RAMDISK_IMAGE_START_MASK 0x07FF -#define RAMDISK_PROMPT_FLAG 0x8000 -#define RAMDISK_LOAD_FLAG 0x4000 - -static char command_line[COMMAND_LINE_SIZE] = { 0, }; - char saved_command_line[COMMAND_LINE_SIZE]; - - -void -setup_arch(char **cmdline_p, unsigned long * memory_start_p, - unsigned long * memory_end_p) -{ - extern int _end; - extern char cmd_line[]; - unsigned char reg; - extern int panic_timeout; - char inf[512]; - int i; - - if (isBeBox[0]) - _Processor = _PROC_Be; - else - { - if (strncmp(res.VitalProductData.PrintableModel,"IBM",3)) - { - _Processor = _PROC_Motorola; - } - else - _Processor = _PROC_IBM; - } - - get_cpuinfo(&inf); - printk("%s",inf); - - /* Set up floppy in PS/2 mode */ - outb(0x09, SIO_CONFIG_RA); - reg = inb(SIO_CONFIG_RD); - reg = (reg & 0x3F) | 0x40; - outb(reg, SIO_CONFIG_RD); - outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ - - switch ( _Processor ) - { - case _PROC_IBM: - ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ - break; - case _PROC_Motorola: - ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ - break; - } - aux_device_present = 0xaa; - - panic_timeout = 300; /* reboot on panic */ - -#if 0 - /* get root via nfs from charon -- was only used for testing */ - ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255); /* nfs */ - /*nfsaddrs=myip:serverip:gateip:netmaskip:clientname*/ - strcpy(cmd_line, - "nfsaddrs=129.138.6.101:129.138.6.90:129.138.6.1:255.255.255.0:gordito nfsroot=/joplin/ppc/root/"); -#endif - *cmdline_p = cmd_line; - *memory_start_p = (unsigned long) &_end; - (unsigned long *)*memory_end_p = (unsigned long *)end_of_DRAM; - size_memory = *memory_end_p - KERNELBASE; /* Relative size of memory */ - -#ifdef CONFIG_BLK_DEV_RAM - rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; - rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); - rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); -#if 1 - rd_prompt = 1; - rd_doload = 1; - rd_image_start = 0; -#endif -#endif - - /* Save unparsed command line copy for /proc/cmdline */ - memcpy(saved_command_line, cmd_line,strlen(cmd_line)+1); - printk("Command line: %s\n", cmd_line); -} - -asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) -{ - return -EIO; -} - - -int -get_cpuinfo(char *buffer) -{ - extern unsigned long loops_per_sec; - int i; - int pvr = _get_PVR(); - int len; - char *model; - unsigned long full = 0, overflow = 0; - unsigned int ti; - PTE *ptr; - - switch (pvr>>16) - { - case 3: - model = "603"; - break; - case 4: - model = "604"; - break; - case 6: - model = "603e"; - break; - case 7: - model = "603ev"; - break; - default: - model = "unknown"; - break; - } - -#ifdef __SMP__ -#define CD(X) (cpu_data[n].X) -#else -#define CD(X) (X) -#define CPUN 0 -#endif - - len = sprintf(buffer, "PowerPC %s/%dMHz revision %d.%d %s\n", - model, - (res.VitalProductData.ProcessorHz > 1024) ? - res.VitalProductData.ProcessorHz>>20 : - res.VitalProductData.ProcessorHz, - MAJOR(pvr), MINOR(pvr), - (inb(IBM_EQUIP_PRESENT) & 2) ? "" : "upgrade"); -#if 1 - if ( res.VitalProductData.PrintableModel[0] ) - len += sprintf(buffer+len,"%s\n",res.VitalProductData.PrintableModel); - - len += sprintf(buffer+len,"Bus %dMHz\n", - (res.VitalProductData.ProcessorBusHz > 1024) ? - res.VitalProductData.ProcessorBusHz>>20 : - res.VitalProductData.ProcessorBusHz); - - /* make sure loops_per_sec has been setup -- ie not at boottime -- Cort */ - if ( CD(loops_per_sec+2500)/500000 > 0) - len += sprintf(buffer+len, - "bogomips: %lu.%02lu\n", - CD(loops_per_sec+2500)/500000, - (CD(loops_per_sec+2500)/5000) % 100); - - - len += sprintf(buffer+len,"Total Ram: %dM Hash Table: %dkB (%dk buckets)\n", - _TotalMemory>>20, Hash_size>>10, - (Hash_size/(sizeof(PTE)*8)) >> 10); - - for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ ) - { - if (i == 0) - len += sprintf(buffer+len,"SIMM Banks: "); - if ( res.Memories[i].SIMMSize != 0 ) - len += sprintf(buffer+len,"%d:%dM ",i, - (res.Memories[i].SIMMSize > 1024) ? - res.Memories[i].SIMMSize>>20 : - res.Memories[i].SIMMSize); - if ( i == MAX_MEMS-1) - len += sprintf(buffer+len,"\n"); - } - - /* TLB */ - len += sprintf(buffer+len,"TLB"); - switch(res.VitalProductData.TLBAttrib) - { - case CombinedTLB: - len += sprintf(buffer+len,": %d entries\n", - res.VitalProductData.TLBSize); - break; - case SplitTLB: - len += sprintf(buffer+len,": (split I/D) %d/%d entries\n", - res.VitalProductData.I_TLBSize, - res.VitalProductData.D_TLBSize); - break; - case NoneTLB: - len += sprintf(buffer+len,": not present\n"); - break; - } - - /* L1 */ - len += sprintf(buffer+len,"L1: "); - switch(res.VitalProductData.CacheAttrib) - { - case CombinedCAC: - len += sprintf(buffer+len,"%dkB LineSize\n", - res.VitalProductData.CacheSize, - res.VitalProductData.CacheLineSize); - break; - case SplitCAC: - len += sprintf(buffer+len,"(split I/D) %dkB/%dkB Linesize %dB/%dB\n", - res.VitalProductData.I_CacheSize, - res.VitalProductData.D_CacheSize, - res.VitalProductData.D_CacheLineSize, - res.VitalProductData.D_CacheLineSize); - break; - case NoneCAC: - len += sprintf(buffer+len,"not present\n"); - break; - } - - /* L2 */ - if ( (inb(IBM_EQUIP_PRESENT) & 1) == 0) /* l2 present */ - { - int size; - - len += sprintf(buffer+len,"L2: %dkB %s\n", - ((inb(IBM_L2_STATUS) >> 5) & 1) ? 512 : 256, - (inb(IBM_SYS_CTL) & 64) ? "enabled" : "disabled"); - } - else - { - len += sprintf(buffer+len,"L2: not present\n"); - } -#if 0 - len+= sprintf(buffer+len,"Equip register %x\n", - inb(IBM_EQUIP_PRESENT)); - len+= sprintf(buffer+len,"L2Status register %x\n", - inb(IBM_L2_STATUS)); -#endif -#endif - - - return len; -} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/signal.c linux/arch/ppc/kernel/signal.c --- v2.1.47/linux/arch/ppc/kernel/signal.c Sun Jan 26 02:07:05 1997 +++ linux/arch/ppc/kernel/signal.c Thu Jul 31 13:09:17 1997 @@ -1,9 +1,17 @@ /* * linux/arch/ppc/kernel/signal.c * - * Copyright (C) 1991, 1992 Linus Torvalds - * Adapted for PowerPC by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/signal.c" + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * */ #include @@ -22,6 +30,12 @@ #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) +#define DEBUG_SIGNALS +#undef DEBUG_SIGNALS + +#define PAUSE_AFTER_SIGNAL +#undef PAUSE_AFTER_SIGNAL + asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); /* @@ -30,79 +44,85 @@ asmlinkage int sys_sigsuspend(unsigned long set, int p2, int p3, int p4, int p6, int p7, struct pt_regs *regs) { unsigned long mask; - int ret = -EINTR; - lock_kernel(); + spin_lock_irq(¤t->sigmask_lock); mask = current->blocked; current->blocked = set & _BLOCKABLE; + spin_unlock_irq(¤t->sigmask_lock); + regs->gpr[3] = -EINTR; -#if 0 +#ifdef DEBUG_SIGNALS printk("Task: %x[%d] - SIGSUSPEND at %x, Mask: %x\n", current, current->pid, regs->nip, set); #endif while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(mask,regs)) - goto out; + if (do_signal(mask,regs)) { + /* + * If a signal handler needs to be called, + * do_signal() has set R3 to the signal number (the + * first argument of the signal handler), so don't + * overwrite that with EINTR ! + * In the other cases, do_signal() doesn't touch + * R3, so it's still set to -EINTR (see above). + */ + return regs->gpr[3]; + } } -out: - unlock_kernel(); - return ret; } +/* + * This sets regs->esp even though we don't actually use sigstacks yet.. + */ asmlinkage int sys_sigreturn(struct pt_regs *regs) { struct sigcontext_struct *sc; struct pt_regs *int_regs; int signo, ret; - lock_kernel(); #if 1 if (verify_area(VERIFY_READ, (void *) regs->gpr[1], sizeof(sc)) - || (regs->gpr[1] >=KERNELBASE)) + || (regs->gpr[1] >= KERNELBASE)) goto badframe; #endif - sc = (struct sigcontext_struct *)regs->gpr[1]; - current->blocked = sc->oldmask & _BLOCKABLE; - int_regs = sc->regs; - signo = sc->signal; - sc++; /* Pop signal 'context' */ + sc = (struct sigcontext_struct *)(regs->gpr[1]+STACK_FRAME_OVERHEAD); + get_user(current->blocked, &sc->oldmask); + current->blocked &= _BLOCKABLE; + get_user(int_regs, &sc->regs); + get_user(signo, &sc->signal); + sc++; /* Pop signal 'context' */ +#ifdef DEBUG_SIGNALS +printk("Sig return - Regs: %x, sc: %x, sig: %d\n", int_regs, sc, signo); +#endif if (sc == (struct sigcontext_struct *)(int_regs)) { /* Last stacked signal */ -#if 0 - /* This doesn't work - it blows away the return address! */ memcpy(regs, int_regs, sizeof(*regs)); -#else - /* Don't mess up 'my' stack frame */ - memcpy(®s->gpr, &int_regs->gpr, sizeof(*regs)-sizeof(regs->_overhead)); -#endif - if ((int)regs->orig_gpr3 >= 0 && + if (regs->trap == 0x0C00 /* System Call! */ && ((int)regs->result == -ERESTARTNOHAND || (int)regs->result == -ERESTARTSYS || - (int)regs->result == -ERESTARTNOINTR)) - { + (int)regs->result == -ERESTARTNOINTR)) { regs->gpr[3] = regs->orig_gpr3; regs->nip -= 4; /* Back up & retry system call */ regs->result = 0; } - ret = (regs->result); - } else { /* More signals to go */ - regs->gpr[1] = (unsigned long)sc; - regs->gpr[3] = sc->signal; - regs->gpr[4] = sc->regs; - regs->link = (unsigned long)((sc->regs)+1); - regs->nip = sc->handler; - ret = sc->signal; + ret = regs->result; + } else { + /* More signals to go */ + regs->gpr[1] = (unsigned long)sc - STACK_FRAME_OVERHEAD; + get_user(regs->gpr[3], &sc->signal); + get_user(int_regs, (struct pt_regs **) &sc->regs); + regs->gpr[4] = (unsigned long) int_regs; + regs->link = (unsigned long) (int_regs+1); + get_user(regs->nip, &sc->handler); + ret = regs->gpr[3]; } - goto out; + return ret; badframe: - /*printk("sys_sigreturn(): badstack regs %x cur %s/%d\n", - regs,current->comm,current->pid);*/ + lock_kernel(); do_exit(SIGSEGV); -out: unlock_kernel(); - return ret; + return -EFAULT; } @@ -120,21 +140,24 @@ unsigned long mask; unsigned long handler_signal = 0; unsigned long *frame = NULL; - unsigned long *trampoline, *regs_ptr; + unsigned long *trampoline; + unsigned long *regs_ptr; unsigned long nip = 0; unsigned long signr; struct sigcontext_struct *sc; struct sigaction * sa; - int bitno, s, ret; + int bitno; - lock_kernel(); mask = ~current->blocked; while ((signr = current->signal & mask)) { +#if 0 + signr = ffz(~signr); /* Compute bit # */ +#else for (bitno = 0; bitno < 32; bitno++) if (signr & (1<signal &= ~(1<sig->action + signr; signr++; @@ -150,6 +173,8 @@ continue; if (_S(signr) & current->blocked) { current->signal |= _S(signr); + spin_lock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sigmask_lock); continue; } sa = current->sig->action + signr - 1; @@ -169,32 +194,45 @@ case SIGCONT: case SIGCHLD: case SIGWINCH: continue; - case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + case SIGSTOP: if (current->flags & PF_PTRACED) continue; current->state = TASK_STOPPED; current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & - SA_NOCLDSTOP)) + SA_NOCLDSTOP)) notify_parent(current); schedule(); continue; case SIGQUIT: case SIGILL: case SIGTRAP: case SIGIOT: case SIGFPE: case SIGSEGV: + lock_kernel(); if (current->binfmt && current->binfmt->core_dump) { if (current->binfmt->core_dump(signr, regs)) signr |= 0x80; } + unlock_kernel(); /* fall through */ default: + spin_lock_irq(¤t->sigmask_lock); current->signal |= _S(signr & 0x7f); + spin_unlock_irq(¤t->sigmask_lock); + + current->flags |= PF_SIGNALED; + + lock_kernel(); /* 8-( */ do_exit(signr); + unlock_kernel(); } } - - /* handle signal */ - if ((int)regs->orig_gpr3 >= 0) { + /* + * OK, we're invoking a handler + */ + if (regs->trap == 0x0C00 /* System Call! */) { if ((int)regs->result == -ERESTARTNOHAND || ((int)regs->result == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART))) @@ -203,9 +241,18 @@ handler_signal |= 1 << (signr-1); mask &= ~sa->sa_mask; } - ret = 0; - if (!handler_signal) /* no handler will be called - return 0 */ - goto out; + + if (regs->trap == 0x0C00 /* System Call! */ && + ((int)regs->result == -ERESTARTNOHAND || + (int)regs->result == -ERESTARTSYS || + (int)regs->result == -ERESTARTNOINTR)) { + regs->gpr[3] = regs->orig_gpr3; + regs->nip -= 4; /* Back up & retry system call */ + regs->result = 0; + } + + if (!handler_signal) /* no handler will be called - return 0 */ + return 0; nip = regs->nip; frame = (unsigned long *) regs->gpr[1]; @@ -213,20 +260,18 @@ /* Build trampoline code on stack */ frame -= 2; trampoline = frame; -#if 1 /* verify stack is valid for writing regs struct */ if (verify_area(VERIFY_WRITE,(void *)frame, sizeof(long)*2+sizeof(*regs)) - || (frame >= KERNELBASE )) + || ((unsigned long) frame >= KERNELBASE )) goto badframe; -#endif - trampoline[0] = 0x38007777; /* li r0,0x7777 */ - trampoline[1] = 0x44000002; /* sc */ + put_user(0x38007777UL, trampoline); /* li r0,0x7777 */ + put_user(0x44000002UL, trampoline+1); /* sc */ frame -= sizeof(*regs) / sizeof(long); regs_ptr = frame; - memcpy(regs_ptr, regs, sizeof(*regs)); + copy_to_user(regs_ptr, regs, sizeof(*regs)); signr = 1; sa = current->sig->action; - + for (mask = 1 ; mask ; sa++,signr++,mask += mask) { if (mask > handler_signal) break; @@ -234,50 +279,39 @@ continue; frame -= sizeof(struct sigcontext_struct) / sizeof(long); -#if 1 if (verify_area(VERIFY_WRITE,(void *)frame, sizeof(struct sigcontext_struct)/sizeof(long))) goto badframe; -#endif sc = (struct sigcontext_struct *)frame; nip = (unsigned long) sa->sa_handler; -#if 0 /* Old compiler */ - nip = *(unsigned long *)nip; -#endif if (sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; - sc->handler = nip; - sc->oldmask = current->blocked; - sc->regs = (unsigned long)regs_ptr; - sc->signal = signr; + put_user(nip, &sc->handler); + put_user(oldmask, &sc->oldmask); /* was current->blocked */ + put_user(regs_ptr, &sc->regs); + put_user(signr, &sc->signal); current->blocked |= sa->sa_mask; regs->gpr[3] = signr; regs->gpr[4] = (unsigned long)regs_ptr; } regs->link = (unsigned long)trampoline; regs->nip = nip; - regs->gpr[1] = (unsigned long)sc; + regs->gpr[1] = (unsigned long)sc - STACK_FRAME_OVERHEAD; - /* The DATA cache must be flushed here to insure coherency - * between the DATA & INSTRUCTION caches. Since we just - * created an instruction stream using the DATA [cache] space - * and since the instruction cache will not look in the DATA - * cache for new data, we have to force the data to go on to - * memory and flush the instruction cache to force it to look - * there. The following function performs this magic - */ - flush_instruction_cache(); - ret = 1; - goto out; + /* The DATA cache must be flushed here to insure coherency */ + /* between the DATA & INSTRUCTION caches. Since we just */ + /* created an instruction stream using the DATA [cache] space */ + /* and since the instruction cache will not look in the DATA */ + /* cache for new data, we have to force the data to go on to */ + /* memory and flush the instruction cache to force it to look */ + /* there. The following function performs this magic */ + store_cache_range((unsigned long) trampoline, + (unsigned long) (trampoline + 2)); + return 1; badframe: -#if 0 - printk("do_signal(): badstack signr %d frame %x regs %x cur %s/%d\n", - signr, frame, regs, current->comm, current->pid); -#endif + lock_kernel(); do_exit(SIGSEGV); - -out: unlock_kernel(); - return ret; + return 0; } diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/strcase.c linux/arch/ppc/kernel/strcase.c --- v2.1.47/linux/arch/ppc/kernel/strcase.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/strcase.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,12 @@ +#include + +int strcasecmp(const char *s1, const char *s2) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while (c1 == c2 && c1 != 0); + return c1 - c2; +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/stubs.c linux/arch/ppc/kernel/stubs.c --- v2.1.47/linux/arch/ppc/kernel/stubs.c Sun Jan 26 02:07:05 1997 +++ linux/arch/ppc/kernel/stubs.c Wed Dec 31 16:00:00 1969 @@ -1,58 +0,0 @@ -/*#include */ -#include -#include -#include - -void sys_iopl(void) -{ - lock_kernel(); - panic("sys_iopl"); - unlock_kernel(); -} -void sys_vm86(void) -{ - lock_kernel(); - panic("sys_vm86"); - unlock_kernel(); -} -void sys_modify_ldt(void) -{ - lock_kernel(); - panic("sys_modify_ldt"); - unlock_kernel(); -} - -void sys_ipc(void) -{ - lock_kernel(); - panic("sys_ipc"); - unlock_kernel(); -} - -void sys_newselect(void) -{ - lock_kernel(); - panic("sys_newselect"); - unlock_kernel(); -} - -#ifndef CONFIG_MODULES -void -scsi_register_module(void) -{ - lock_kernel(); - panic("scsi_register_module"); - unlock_kernel(); -} - -void -scsi_unregister_module(void) -{ - lock_kernel(); - panic("scsi_unregister_module"); - unlock_kernel(); -} -#endif - - - diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/support.c linux/arch/ppc/kernel/support.c --- v2.1.47/linux/arch/ppc/kernel/support.c Mon May 27 21:46:04 1996 +++ linux/arch/ppc/kernel/support.c Wed Dec 31 16:00:00 1969 @@ -1,84 +0,0 @@ -/* - * Miscellaneous support routines - */ - -#include - -/*extern __inline__*/ int find_first_zero_bit(void *add, int len) -{ - int mask, nr, i; - BITFIELD *addr = add; - nr = 0; - while (len) - { - if (~*addr != 0) - { /* Contains at least one zero */ - for (i = 0; i < 32; i++, nr++) - { - mask = BIT(nr); - if ((mask & *addr) == 0) - { - return (nr); - } - } - } - len -= 32; - addr++; - nr += 32; - } - return (0); /* Shouldn't happen */ -} - -/*extern __inline__*/ int find_next_zero_bit(void *add, int last_bit, int nr) -{ - int mask, i; - BITFIELD *addr = add; -#if 0 -printk("Find next (%x, %x)", addr, nr); -#endif - addr += nr >> 5; -#if 0 -printk(" - Pat: %x(%08X)\n", addr, *addr); -#endif - if ((nr & 0x1F) != 0) - { - if (*addr != 0xFFFFFFFF) - { /* At least one more bit available in this longword */ - for (i = (nr&0x1F); i < 32; i++, nr++) - { - mask = BIT(nr); - if ((mask & *addr) == 0) - { -#if 0 -printk("(1)Bit: %x(%d), Pat: %x(%08x)\n", nr, nr&0x1F, addr, *addr); -#endif - return (nr); - } - } - } - addr++; - nr = (nr + 0x1F) & ~0x1F; - } - while (nr < last_bit) - { - if (*addr != 0xFFFFFFFF) - { /* Contains at least one zero */ - for (i = 0; i < 32; i++, nr++) - { - mask = BIT(nr); - if ((mask & *addr) == 0) - { -#if 0 -printk("(2)Bit: %x(%d), Pat: %x(%08x)\n", nr, nr&0x1F, addr, *addr); -#endif - return (nr); - } - } - } - addr++; - nr += 32; - } - return (nr); /* Shouldn't happen */ -} - - diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/syscalls.c linux/arch/ppc/kernel/syscalls.c --- v2.1.47/linux/arch/ppc/kernel/syscalls.c Sun Jan 26 02:07:05 1997 +++ linux/arch/ppc/kernel/syscalls.c Thu Jul 31 13:09:17 1997 @@ -9,6 +9,7 @@ * platform. */ +#include #include #include #include @@ -19,105 +20,45 @@ #include #include #include +#include #include +#include -/* - * sys_pipe() is the normal C calling standard for creating - * a pipe. It's not the way unix traditionally does this, though. - */ -asmlinkage int sys_pipe(unsigned long * fildes) -{ - int error; - lock_kernel(); - error = verify_area(VERIFY_WRITE,fildes,8); - if (error) - goto out; - error = do_pipe(fildes); -out: - unlock_kernel(); - return error; +void +check_bugs(void) +{ } -asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, int prot, - int flags, int fd, off_t offset) +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) { - struct file * file = NULL; - int ret = -EBADF; + printk("sys_ioperm()\n"); + return -EIO; +} +int sys_iopl(int a1, int a2, int a3, int a4) +{ lock_kernel(); - if (!(flags & MAP_ANONYMOUS)) { - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - goto out; - } - flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - ret = do_mmap(file, addr, len, prot, flags, offset); -out: + printk( "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4); unlock_kernel(); - return ret; + return (ENOSYS); } -/* - * Perform the select(nd, in, out, ex, tv) and mmap() system - * calls. Linux/i386 didn't use to be able to handle more than - * 4 system call parameters, so these system calls used a memory - * block for parameter passing.. - */ -asmlinkage int old_mmap(unsigned long *buffer) +int sys_vm86(int a1, int a2, int a3, int a4) { - int error; - unsigned long flags; - long a,b,c,d,e; - struct file * file = NULL; - lock_kernel(); - error = verify_area(VERIFY_READ, buffer, 6*sizeof(long)); - if (error) - goto out; - get_user(flags,buffer+3); - if (!(flags & MAP_ANONYMOUS)) { - unsigned long fd; - get_user(fd,buffer+4); - error = -EBADF; - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - goto out; - } - flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - error = -EFAULT; - if ( get_user(a,buffer) || get_user(b,buffer+1) || - get_user(c,buffer+2)||get_user(d,buffer+5) ) - goto out; - error = do_mmap(file,a,b,c, flags, d); -out: + printk( "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4); unlock_kernel(); - return error; + return (ENOSYS); } -extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); - -asmlinkage int old_select(unsigned long *buffer) +int sys_modify_ldt(int a1, int a2, int a3, int a4) { - int n; - fd_set *inp; - fd_set *outp; - fd_set *exp; - struct timeval *tvp; - - lock_kernel(); - n = verify_area(VERIFY_READ, buffer, 5*sizeof(unsigned long)); - if (n) - goto out; - get_user(n,buffer); - get_user(inp,buffer+1); - get_user(outp,buffer+2); - get_user(exp,buffer+3); - get_user(tvp,buffer+4); - n = sys_select(n, inp, outp, exp, tvp); -out: + lock_kernel(); + printk( "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4); unlock_kernel(); - return n; + return (ENOSYS); } -#if 0 /* * sys_ipc() is the de-multiplexer for the SysV IPC calls.. @@ -145,12 +86,12 @@ ret = -EINVAL; if (!ptr) goto out; - if ((ret = verify_area (VERIFY_READ, ptr, sizeof(long)))) - goto out; - fourth.__pad = (void *) get_fs_long(ptr); + ret = -EFAULT; + if (get_user(fourth.__pad, (void **) ptr)) + goto out; ret = sys_semctl (first, second, third, fourth); goto out; - } + } default: ret = -EINVAL; goto out; @@ -168,13 +109,13 @@ ret = -EINVAL; if (!ptr) goto out; - if ((ret = verify_area (VERIFY_READ, ptr, sizeof(tmp)))) - goto out; - memcpy_fromfs (&tmp,(struct ipc_kludge *) ptr, - sizeof (tmp)); + ret = -EFAULT; + if (copy_from_user(&tmp,(struct ipc_kludge *) ptr, + sizeof (tmp))) + goto out; ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); goto out; - } + } case 1: default: ret = sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third); goto out; @@ -195,15 +136,12 @@ switch (version) { case 0: default: { ulong raddr; - if ((ret = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong)))) - goto out; ret = sys_shmat (first, (char *) ptr, second, &raddr); if (ret) goto out; - put_fs_long (raddr, (ulong *) third); - ret = 0; + ret = put_user (raddr, (ulong *) third); goto out; - } + } case 1: /* iBCS2 emulator entry point */ ret = -EINVAL; if (get_fs() != get_ds()) @@ -230,4 +168,81 @@ unlock_kernel(); return ret; } + + +#ifndef CONFIG_MODULES +void +scsi_register_module(void) +{ + lock_kernel(); + panic("scsi_register_module"); + unlock_kernel(); +} + +void +scsi_unregister_module(void) +{ + lock_kernel(); + panic("scsi_unregister_module"); + unlock_kernel(); +} #endif + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +asmlinkage int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + error = verify_area(VERIFY_WRITE,fildes,8); + if (error) + return error; + error = do_pipe(fd); + if (error) + return error; + put_user(fd[0],0+fildes); + put_user(fd[1],1+fildes); + return 0; +} + +asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t offset) +{ + struct file * file = NULL; + if (!(flags & MAP_ANONYMOUS)) { + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + return -EBADF; + } + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + return do_mmap(file, addr, len, prot, flags, offset); +} + +extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); + +/* + * Due to some executables calling the wrong select we sometimes + * get wrong args. This determines how the args are being passed + * (a single ptr to them all args passed) then calls + * sys_select() with the appropriate args. -- Cort + */ +asmlinkage int +ppc_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) +{ + int err; + if ( (unsigned long)n >= 4096 ) + { + unsigned long *buffer = (unsigned long *)n; + if ( get_user(n, buffer) || + get_user(inp,buffer+1) || + get_user(outp,buffer+2) || + get_user(exp,buffer+3) || + get_user(tvp,buffer+4) ) + return -EFAULT; + } + return sys_select(n, inp, outp, exp, tvp); +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c --- v2.1.47/linux/arch/ppc/kernel/time.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/kernel/time.c Wed Dec 31 16:00:00 1969 @@ -1,465 +0,0 @@ -/* - * linux/arch/i386/kernel/time.c - * - * Copyright (C) 1991, 1992, 1995 Linus Torvalds - * - * Adapted for PowerPC (PreP) by Gary Thomas - * - * This file contains the PC-specific time handling details: - * reading the RTC at bootup, etc.. - * 1994-07-02 Alan Modra - * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime - * 1995-03-26 Markus Kuhn - * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 - * precision CMOS clock update - */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -extern int isBeBox[]; - -#define TIMER_IRQ 0 - -/* Cycle counter value at the previous timer interrupt.. */ -static unsigned long long last_timer_cc = 0; -static unsigned long long init_timer_cc = 0; - -static inline int CMOS_READ(int addr) -{ - outb(addr>>8, NVRAM_AS1); - outb(addr, NVRAM_AS0); - return (inb(NVRAM_DATA)); -} - -static inline int CMOS_WRITE(int addr, int val) -{ - outb(addr>>8, NVRAM_AS1); - outb(addr, NVRAM_AS0); - return (outb(val, NVRAM_DATA)); -} - -/* This function must be called with interrupts disabled - * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs - * - * However, the pc-audio speaker driver changes the divisor so that - * it gets interrupted rather more often - it loads 64 into the - * counter rather than 11932! This has an adverse impact on - * do_gettimeoffset() -- it stops working! What is also not - * good is that the interval that our timer function gets called - * is no longer 10.0002 ms, but 9.9767 ms. To get around this - * would require using a different timing source. Maybe someone - * could use the RTC - I know that this can interrupt at frequencies - * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix - * it so that at startup, the timer code in sched.c would select - * using either the RTC or the 8253 timer. The decision would be - * based on whether there was any other device around that needed - * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz, - * and then do some jiggery to have a version of do_timer that - * advanced the clock by 1/1024 s. Every time that reached over 1/100 - * of a second, then do all the old code. If the time was kept correct - * then do_gettimeoffset could just return 0 - there is no low order - * divider that can be accessed. - * - * Ideally, you would be able to use the RTC for the speaker driver, - * but it appears that the speaker driver really needs interrupt more - * often than every 120 us or so. - * - * Anyway, this needs more thought.... pjsg (1993-08-28) - * - * If you are really that interested, you should be reading - * comp.protocols.time.ntp! - */ - -#define TICK_SIZE tick - -static unsigned long do_slow_gettimeoffset(void) -{ - int count; - unsigned long offset = 0; - - /* timer count may underflow right here */ - outb_p(0x00, 0x43); /* latch the count ASAP */ - count = inb_p(0x40); /* read the latched count */ - count |= inb(0x40) << 8; - /* we know probability of underflow is always MUCH less than 1% */ - if (count > (LATCH - LATCH/100)) { - /* check for pending timer interrupt */ - outb_p(0x0a, 0x20); - if (inb(0x20) & 1) - offset = TICK_SIZE; - } - count = ((LATCH-1) - count) * TICK_SIZE; - count = (count + LATCH/2) / LATCH; - return offset + count; -} - -static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; - -/* - * This version of gettimeofday has near microsecond resolution. - */ -void do_gettimeofday(struct timeval *tv) -{ - unsigned long flags; - - save_flags(flags); - cli(); - *tv = xtime; - tv->tv_usec += do_gettimeoffset(); - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; - } - restore_flags(flags); -} - -void do_settimeofday(struct timeval *tv) -{ - cli(); - /* This is revolting. We need to set the xtime.tv_usec - * correctly. However, the value in this location is - * is value at the last tick. - * Discover what correction gettimeofday - * would have done, and then undo it! - */ - tv->tv_usec -= do_gettimeoffset(); - - if (tv->tv_usec < 0) { - tv->tv_usec += 1000000; - tv->tv_sec--; - } - - xtime = *tv; - time_state = TIME_BAD; - time_maxerror = 0x70000000; - time_esterror = 0x70000000; - set_rtc(xtime.tv_sec); - sti(); -} - -static int month_days[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -#define FEBRUARY 2 -#define STARTOFTIME 1970 -#define SECDAY 86400L -#define SECYR (SECDAY * 365) -#define leapyear(year) ((year) % 4 == 0) -#define days_in_year(a) (leapyear(a) ? 366 : 365) -#define days_in_month(a) (month_days[(a) - 1]) - -struct _tm -{ - int tm_sec; - int tm_min; - int tm_hour; - int tm_day; - int tm_month; - int tm_year; -}; - -static _to_tm(int tim, struct _tm * tm) -{ - register int i; - register long hms, day; - - day = tim / SECDAY; - hms = tim % SECDAY; - - /* Hours, minutes, seconds are easy */ - tm->tm_hour = hms / 3600; - tm->tm_min = (hms % 3600) / 60; - tm->tm_sec = (hms % 3600) % 60; - - /* Number of years in days */ - for (i = STARTOFTIME; day >= days_in_year(i); i++) - day -= days_in_year(i); - tm->tm_year = i; - - /* Number of months in days left */ - if (leapyear(tm->tm_year)) - days_in_month(FEBRUARY) = 29; - for (i = 1; day >= days_in_month(i); i++) - day -= days_in_month(i); - days_in_month(FEBRUARY) = 28; - tm->tm_month = i; - - /* Days are what is left over (+1) from all that. */ - tm->tm_day = day + 1; -} - -/* - * Set the time into the CMOS - */ -static void set_rtc(unsigned long nowtime) -{ - int retval = 0; - struct _tm tm; - unsigned char save_control, save_freq_select; - - /*if (_Processor != _PROC_IBM) return;*/ - - _to_tm(nowtime, &tm); - - /* tell the clock it's being set */ - save_control = CMOS_MCRTC_READ(MCRTC_CONTROL); - CMOS_MCRTC_WRITE((save_control|MCRTC_SET), MCRTC_CONTROL); - /* stop and reset prescaler */ - save_freq_select = CMOS_MCRTC_READ(MCRTC_FREQ_SELECT); - CMOS_MCRTC_WRITE((save_freq_select|MCRTC_DIV_RESET2), MCRTC_FREQ_SELECT); - - printk("Set RTC H:M:S M/D/Y %d:%02d:%02d %d/%d/%d\n", - tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_month, tm.tm_day, tm.tm_year); - if (!(save_control & MCRTC_DM_BINARY) || MCRTC_ALWAYS_BCD) { - BIN_TO_BCD(tm.tm_sec); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_month); - BIN_TO_BCD(tm.tm_day); - BIN_TO_BCD(tm.tm_year); - } - - CMOS_MCRTC_WRITE(tm.tm_sec, MCRTC_SECONDS); - CMOS_MCRTC_WRITE(tm.tm_min, MCRTC_MINUTES); - CMOS_MCRTC_WRITE(tm.tm_hour, MCRTC_HOURS); - CMOS_MCRTC_WRITE(tm.tm_month, MCRTC_MONTH); - CMOS_MCRTC_WRITE(tm.tm_day, MCRTC_MINUTES); - CMOS_MCRTC_WRITE(tm.tm_year - 1900, MCRTC_MINUTES); - - /* The following flags have to be released exactly in this order, - * otherwise the DS12887 (popular MC146818A clone with integrated - * battery and quartz) will not reset the oscillator and will not - * update precisely 500 ms later. You won't find this mentioned in - * the Dallas Semiconductor data sheets, but who believes data - * sheets anyway ... -- Markus Kuhn - */ - CMOS_MCRTC_WRITE(save_control, MCRTC_CONTROL); - CMOS_MCRTC_WRITE(save_freq_select, MCRTC_FREQ_SELECT); -} - -/* - * In order to set the CMOS clock precisely, set_rtc_mmss has to be - * called 500 ms after the second nowtime has started, because when - * nowtime is written into the registers of the CMOS clock, it will - * jump to the next second precisely 500 ms later. Check the Motorola - * MC146818A or Dallas DS12887 data sheet for details. - */ -static int set_rtc_mmss(unsigned long nowtime) -{ - int retval = 0; - int real_seconds, real_minutes, cmos_minutes; - unsigned char save_control, save_freq_select; - -#ifdef __powerpc__ -printk("%s: %d - set TOD\n", __FILE__, __LINE__); -return (-1); /* Not implemented */ -#else - -printk("%s: %d - set TOD\n", __FILE__, __LINE__); - save_control = CMOS_MCRTC_READ(MCRTC_CONTROL); /* tell the clock it's being set */ - CMOS_MCRTC_WRITE((save_control|MCRTC_SET), MCRTC_CONTROL); - - save_freq_select = CMOS_MCRTC_READ(MCRTC_FREQ_SELECT); /* stop and reset prescaler */ - CMOS_MCRTC_WRITE((save_freq_select|MCRTC_DIV_RESET2), MCRTC_FREQ_SELECT); - - cmos_minutes = CMOS_MCRTC_READ(MCRTC_MINUTES); - if (!(save_control & MCRTC_DM_BINARY) || MCRTC_ALWAYS_BCD) - BCD_TO_BIN(cmos_minutes); - - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - cmos_minutes) < 30) { - if (!(save_control & MCRTC_DM_BINARY) || MCRTC_ALWAYS_BCD) { - BIN_TO_BCD(real_seconds); - BIN_TO_BCD(real_minutes); - } - CMOS_MCRTC_WRITE(real_seconds,MCRTC_SECONDS); - CMOS_MCRTC_WRITE(real_minutes,MCRTC_MINUTES); - } else - retval = -1; - - /* The following flags have to be released exactly in this order, - * otherwise the DS12887 (popular MC146818A clone with integrated - * battery and quartz) will not reset the oscillator and will not - * update precisely 500 ms later. You won't find this mentioned in - * the Dallas Semiconductor data sheets, but who believes data - * sheets anyway ... -- Markus Kuhn - */ - CMOS_MCRTC_WRITE(save_control, MCRTC_CONTROL); - CMOS_MCRTC_WRITE(save_freq_select, MCRTC_FREQ_SELECT); - - return retval; -#endif -} - -/* last time the cmos clock got updated */ -static long last_rtc_update = 0; - -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick - */ -static inline void timer_interrupt(int irq, void *dev, struct pt_regs * regs) -{ - static int timeints = 0; - - do_timer(regs); - - /* - * If we have an externally synchronized Linux clock, then update - * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be - * called as close as possible to 500 ms before the new second starts. - */ - if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) - if (set_rtc_mmss(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ - - - /* use hard disk LED as a heartbeat instead -- much more useful - -- Cort */ - switch(timeints) - { - /* act like an actual heart beat -- ie thump-thump-pause... */ - case 0: - case 20: - hard_disk_LED(1); - break; - case 7: - case 27: - hard_disk_LED(0); - break; - case 100: - timeints = -1; - break; - } - timeints++; -} - -/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. - * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 - * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. - * - * [For the Julian calendar (which was used in Russia before 1917, - * Britain & colonies before 1752, anywhere else before 1582, - * and is still in use by some communities) leave out the - * -year/100+year/400 terms, and add 10.] - * - * This algorithm was first published by Gauss (I think). - * - * WARNING: this function will overflow on 2106-02-07 06:28:16 on - * machines were long is 32-bit! (However, as time_t is signed, we - * will already get problems at other places on 2038-01-19 03:14:08) - */ -static inline unsigned long mktime(unsigned int year, unsigned int mon, - unsigned int day, unsigned int hour, - unsigned int min, unsigned int sec) -{ - if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ - mon += 12; /* Puts Feb last since it has leap day */ - year -= 1; - } - return ((( - (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + - year*365 - 719499 - )*24 + hour /* now have hours */ - )*60 + min /* now have minutes */ - )*60 + sec; /* finally seconds */ -} - -unsigned long get_cmos_time(void) -{ - unsigned int year, mon, day, hour, min, sec; - int i; - - if (_Processor == _PROC_IBM) - { - do { /* Isn't this overkill ? UIP above should guarantee consistency */ - sec = CMOS_MCRTC_READ(MCRTC_SECONDS); - min = CMOS_MCRTC_READ(MCRTC_MINUTES); - hour = CMOS_MCRTC_READ(MCRTC_HOURS); - day = CMOS_MCRTC_READ(MCRTC_DAY_OF_MONTH); - mon = CMOS_MCRTC_READ(MCRTC_MONTH); - year = CMOS_MCRTC_READ(MCRTC_YEAR); - } while (sec != CMOS_MCRTC_READ(MCRTC_SECONDS)); - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } else - if (_Processor == _PROC_Be) - { - do { /* Isn't this overkill ? UIP above should guarantee consistency */ - sec = CMOS_MCRTC_READ(MCRTC_SECONDS); - min = CMOS_MCRTC_READ(MCRTC_MINUTES); - hour = CMOS_MCRTC_READ(MCRTC_HOURS); - day = CMOS_MCRTC_READ(MCRTC_DAY_OF_MONTH); - mon = CMOS_MCRTC_READ(MCRTC_MONTH); - year = CMOS_MCRTC_READ(MCRTC_YEAR); - } while (sec != CMOS_MCRTC_READ(MCRTC_SECONDS)); - } else - { /* Motorola PowerStack etc. */ - do { /* Isn't this overkill ? UIP above should guarantee consistency */ - sec = CMOS_READ(RTC_SECONDS); - min = CMOS_READ(RTC_MINUTES); - hour = CMOS_READ(RTC_HOURS); - day = CMOS_READ(RTC_DAY_OF_MONTH); - mon = CMOS_READ(RTC_MONTH); - year = CMOS_READ(RTC_YEAR); - } while (sec != CMOS_READ(RTC_SECONDS)); - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } -#if 0 -printk("CMOS TOD - M/D/Y H:M:S = %d/%d/%d %d:%02d:%02d\n", mon, day, year, hour, min, sec); -#endif - if ((year += 1900) < 1970) - year += 100; - return mktime(year, mon, day, hour, min, sec); -} - -void time_init(void) -{ - void (*irq_handler)(int, struct pt_regs *); - xtime.tv_sec = get_cmos_time(); - xtime.tv_usec = 0; - - /* If we have the CPU hardware time counters, use them */ - irq_handler = timer_interrupt; - if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL) != 0) - panic("Could not allocate timer IRQ!"); -} - diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/traps.c linux/arch/ppc/kernel/traps.c --- v2.1.47/linux/arch/ppc/kernel/traps.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/kernel/traps.c Thu Jul 31 13:09:17 1997 @@ -20,14 +20,14 @@ #include #include #include +#include +#include #include #include #include #include -#include - /* * Trap & Exception support */ @@ -40,122 +40,98 @@ void _exception(int signr, struct pt_regs *regs) { - /* dump_regs(regs);*/ - force_sig(signr, current); - if (!user_mode(regs)) - { - printk("Failure in kernel at PC: %x, MSR: %x\n", regs->nip, regs->msr); - while (1) ; - } + if (!user_mode(regs)) + { + show_regs(regs); + print_backtrace(regs->gpr[1]); + panic("Exception in kernel pc %x signal %d",regs->nip,signr); + } + force_sig(signr, current); } MachineCheckException(struct pt_regs *regs) { - printk("Machine check at PC: %x[%x], SR: %x\n", regs->nip, va_to_phys(regs->nip), regs->msr); - _exception(SIGSEGV, regs); -} - -ProgramCheckException(struct pt_regs *regs) -{ -#if 0 - printk("Program check at PC: %x[%x], SR: %x\n", - regs->nip, va_to_phys(regs->nip), regs->msr); - #endif - if (current->flags & PF_PTRACED) - { - _exception(SIGTRAP, regs); - } else - { - _exception(SIGILL, regs); - } + if ( !user_mode(regs) ) + { + printk("Machine check in kernel mode.\n"); + printk("Caused by (from msr): "); + printk("regs %08x ",regs); + switch( regs->msr & 0x0000F000) + { + case (1<<12) : + printk("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (1<<13) : + printk("Transfer error ack signal\n"); + break; + case (1<<14) : + printk("Data parity signal\n"); + break; + case (1<<15) : + printk("Address parity signal\n"); + break; + default: + printk("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace(regs->gpr[1]); + panic(""); + } + _exception(SIGSEGV, regs); } -SingleStepException(struct pt_regs *regs) +void +UnknownException(struct pt_regs *regs) { - regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ + printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); _exception(SIGTRAP, regs); } -FloatingPointCheckException(struct pt_regs *regs) +void +InstructionBreakpoint(struct pt_regs *regs) { - /* if fpu already on -- then exception was generated by an error */ - if ( (unsigned long)(regs->msr) & (unsigned long)MSR_FP ) - { - _exception(SIGFPE, regs); - return 0; - } - -#if 0 - printk("fpu off -- turning on: %s pc %x fpscr %x msr %x ksp %x r1 %x\n", - current->comm,regs->nip,regs->fpcsr,regs->msr,regs,regs->gpr[1]); +#ifdef CONFIG_XMON + if (xmon_iabr_match(regs)) + return; #endif + _exception(SIGTRAP, regs); +} - /* if the fpu is off then turn it on and return */ - regs->msr |= MSR_FP; - current->tss.fp_used++; - /* tells return_from_int to restore fp regs since fp was turned on - see head.S -- Cort */ - return MSR_FP; +void +RunModeException(struct pt_regs *regs) +{ + _exception(SIGTRAP, regs); } -AlignmentException(struct pt_regs *regs) +ProgramCheckException(struct pt_regs *regs) { -/* printk("Alignment error at PC: %x, SR: %x\n", regs->nip, regs->msr); - dump_regs(regs); - printk("Alignment error at PC: %x[%x], SR: %x\n", regs->nip, va_to_phys(regs->nip), regs->msr);*/ - _exception(SIGBUS, regs); + if (current->flags & PF_PTRACED) + _exception(SIGTRAP, regs); + else + _exception(SIGILL, regs); } +SingleStepException(struct pt_regs *regs) +{ + regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ + _exception(SIGTRAP, regs); +} -/* see CHECK_STACK macro in head.S for argument definitions */ -bad_stack(unsigned int r3, unsigned int r4, unsigned int r5, unsigned int r6) +AlignmentException(struct pt_regs *regs) { - /* r6 (was r1) kernel stack pointer */ - /* r5 (was r2) kernel stack page */ - /* r4 kernel stack magic */ - /* r3 stack magic or ksp masked to page boundary */ - printk("bad_stack(): Kernel stack bad.\n"); - printk("ksp %x kpage %x stack magic %x r3 %x\n", - r6,r5,r4,r3); - printk("current: %s/%d\n", - current->comm,current->pid); - while(1); -} - -dump_regs(struct pt_regs *regs) -{ - int i; - printk("NIP: %08X, MSR: %08X, XER: %08X, LR: %08X, FRAME: %08X\n", regs->nip, regs->msr, regs->xer, regs->link, regs); -#if 0 - printk("HASH = %08X/%08X, MISS = %08X/%08X, CMP = %08X/%08X\n", regs->hash1, regs->hash2, regs->imiss, regs->dmiss, regs->icmp, regs->dcmp); -#endif - printk("TASK = %x[%d] '%s'\n", current, current->pid, current->comm); - for (i = 0; i < 32; i++) - { - if ((i % 8) == 0) - { - printk("GPR%02d: ", i); - } - printk("%08X ", regs->gpr[i]); - if ((i % 8) == 7) - { - printk("\n"); - } - } -#if 0 - if (regs->nip >= 0x1000) - dump_buf(regs->nip-32, 64); - dump_buf((regs->nip&0x0FFFFFFF)|KERNELBASE, 32); -#endif + _exception(SIGBUS, regs); } trace_syscall(struct pt_regs *regs) { static int count; - printk("Task: %08X(%d), PC: %08X/%08X, Syscall: %3d, Result: %s%d\n", current, current->pid, regs->nip, regs->link, regs->gpr[0], regs->ccr&0x10000000?"Error=":"", regs->gpr[3]); + printk("Task: %08X(%d), PC: %08X/%08X, Syscall: %3d, Result: %s%d\n", + current, current->pid, regs->nip, regs->link, regs->gpr[0], + regs->ccr&0x10000000?"Error=":"", regs->gpr[3]); if (++count == 20) { count = 0; } } - diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/usercpy.c linux/arch/ppc/kernel/usercpy.c --- v2.1.47/linux/arch/ppc/kernel/usercpy.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/kernel/usercpy.c Wed Dec 31 16:00:00 1969 @@ -1,116 +0,0 @@ -#include -#include -#include -#include - -/* - * bad data accesses from these functions should be handled specially - * since they are to user areas and may or may not be valid. - * on error -EFAULT should be returned. -- Cort - */ -int __copy_tofrom_user_failure(void) -{ - current->tss.excount = 0; - return -EFAULT; -} - -int __copy_tofrom_user(unsigned long to, unsigned long from, int size) -{ - /* setup exception handling stuff */ - current->tss.excount++; - current->tss.expc = (unsigned long )__copy_tofrom_user_failure; - - if (memcpy( (void *)to, (void *)from, (size_t) size) == -EFAULT ) - { - /* take down exception handler stuff */ - current->tss.excount = 0; - return -EFAULT; - } - current->tss.excount = 0; - return 0; /* successful return */ -} - -/* Just like strncpy except in the return value: - * - * -EFAULT if an exception occurs before the terminator is copied. - * N if the buffer filled. - * - * Otherwise the length of the string is returned. - */ -asmlinkage int __strncpy_from_user_failure(void) -{ - current->tss.excount = 0; - return -EFAULT; -} - -int __strncpy_from_user(unsigned long dest, unsigned long src, int count) -{ - int i = 0; - /* setup exception handling stuff */ - current->tss.excount++; - current->tss.expc = (unsigned long )__strncpy_from_user_failure; - - while ( i != count ) - { - *(char *)(dest+i) = *(char *)(src+i); - if ( *(char *)(src+i) == 0 ) - { - return i; - } - i++; - } - *(char *)(dest+i) = (char)0; - /* take down exception handler stuff */ - current->tss.excount = 0; - return i; -} - -int __clear_user_failure(void) -{ - current->tss.excount = 0; - return -EFAULT; -} -int __clear_user(unsigned long addr, int size) -{ - /* setup exception handling stuff */ - current->tss.excount++; - current->tss.expc = (unsigned long )__clear_user_failure; - - if ((int)memset((void *)addr,(int)0, (__kernel_size_t)size) == -EFAULT ) - { - /* take down exception handler stuff */ - current->tss.excount = 0; - return -EFAULT; - } - /* take down exception handler stuff */ - current->tss.excount = 0; - return size; -} - -/* - * Return the length of the string including the NUL terminator - * (strlen+1) or zero if an error occured. - */ -size_t strlen_user_failure(void) -{ - current->tss.excount = 0; - return -EFAULT; -} -size_t strlen_user(char * s) -{ - size_t i; - /* setup exception handling stuff */ - current->tss.excount++; - current->tss.expc = (unsigned long )strlen_user_failure; - - i = strlen(s)+1; - - if ( i == -EFAULT) - return -EFAULT; - - /* take down exception handler stuff */ - current->tss.excount = 0; - - return(i); -} - diff -u --recursive --new-file v2.1.47/linux/arch/ppc/ld.script linux/arch/ppc/ld.script --- v2.1.47/linux/arch/ppc/ld.script Mon May 27 02:00:58 1996 +++ linux/arch/ppc/ld.script Thu Jul 31 13:09:17 1997 @@ -31,12 +31,16 @@ .text : { *(.text) - *(.rodata) - *(.rodata1) + *(.fixup) *(.got1) } _etext = .; PROVIDE (etext = .); + .rodata : + { + *(.rodata) + *(.rodata1) + } .fini : { *(.fini) } =0 .ctors : { *(.ctors) } .dtors : { *(.dtors) } @@ -45,31 +49,33 @@ .data : { *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) CONSTRUCTORS } - .data1 : { *(.data1) } - .got : { *(.got.plt) *(.got) } - .dynamic : { *(.dynamic) } - /* We want the small data sections together, so single-instruction offsets - can access them all, and initialized data all before uninitialized, so - we can shorten the on-disk segment size. */ - .sdata : { *(.sdata) } _edata = .; +/* + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; +*/ + __bss_start = .; /* BSS */ PROVIDE (edata = .); __bss_start = .; - .sbss : { *(.sbss) *(.scommon) } .bss : { + *(.sbss) *(.scommon) *(.dynbss) *(.bss) *(COMMON) } _end = . ; PROVIDE (end = .); - /* These are needed for ELF backends which have not yet been - converted to the new style linker. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - /* These must appear regardless of . */ } diff -u --recursive --new-file v2.1.47/linux/arch/ppc/lib/Makefile linux/arch/ppc/lib/Makefile --- v2.1.47/linux/arch/ppc/lib/Makefile Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/lib/Makefile Thu Jul 31 13:09:17 1997 @@ -1,12 +1,38 @@ -# -# Makefile for i386-specific library files.. -# +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) $(ASFLAGS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s +.S.o: + $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s + $(AS) $(ASFLAGS) -o $*.o $*.s + rm $*.s + +HOST_CC = gcc L_TARGET = lib.o -L_OBJS = checksum.o cksum_support.o -CC = gcc -I$(TOPDIR)/include +L_OBJS = checksum.o cksum_support.o string.o ${L_TARGET}: $(L_OBJS) $(LD) -r -o ${L_TARGET} $(L_OBJS) + fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend + +dep: + $(CPP) -M *.S *.c > .depend + +modules: + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff -u --recursive --new-file v2.1.47/linux/arch/ppc/lib/string.S linux/arch/ppc/lib/string.S --- v2.1.47/linux/arch/ppc/lib/string.S Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/lib/string.S Thu Jul 31 13:09:17 1997 @@ -0,0 +1,328 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include "../kernel/ppc_asm.tmpl" +#include + + .globl strcpy +strcpy: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strncpy +strncpy: + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + + .globl strcat +strcat: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strcmp +strcmp: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + .globl bcopy +bcopy: + mr r6,r3 + mr r3,r4 + mr r4,r6 + b memcpy + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + blelr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr + .globl __copy_tofrom_user +__copy_tofrom_user: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + li r3,0 /* success return value */ + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) +11: lwzu r8,8(r4) +12: stw r7,4(r6) +13: stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f +14: lwzu r0,4(r4) + addi r5,r5,-4 +15: stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) +16: stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 +17: stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b +99: li r3,-EFAULT + blr +.section __ex_table,"a" + .align 2 + .long 1b,99b + .long 11b,99b + .long 12b,99b + .long 13b,99b + .long 14b,99b + .long 15b,99b + .long 4b,99b + .long 16b,99b + .long 6b,99b + .long 17b,99b +.text + + .globl __clear_user +__clear_user: + addi r6,r3,-4 + li r3,0 + li r5,0 + cmplwi 0,r4,4 + blt 7f +11: stwu r5,4(r6) + beqlr + andi. r0,r6,3 + add r4,r0,r4 + subf r6,r0,r6 + rlwinm r0,r4,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r5,4(r6) + bdnz 1b +6: andi. r4,r4,3 +7: cmpwi 0,r4,0 + beqlr + mtctr r4 + addi r6,r6,3 +8: stbu r5,1(r6) + bdnz 8b + blr +99: li r3,-EFAULT + blr +.section __ex_table,"a" + .align 2 + .long 11b,99b + .long 1b,99b + .long 8b,99b +.text + + .globl __strncpy_from_user +__strncpy_from_user: + addi r6,r3,-1 + addi r4,r4,-1 + cmpwi 0,r5,0 + beq 2f + mtctr r5 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + beq 3f +2: addi r6,r6,1 +3: subf r3,r3,r6 + blr +99: li r3,-EFAULT + blr +.section __ex_table,"a" + .align 2 + .long 1b,99b +.text + + .globl strlen_user +strlen_user: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + addi r3,r3,1 + blr +99: li r3,0 + blr +.section __ex_table,"a" + .align 2 + .long 1b,99b diff -u --recursive --new-file v2.1.47/linux/arch/ppc/mkdiff linux/arch/ppc/mkdiff --- v2.1.47/linux/arch/ppc/mkdiff Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mkdiff Thu Jul 31 13:09:17 1997 @@ -0,0 +1,8 @@ +#!/bin/bash + +N=`basename $PWD` +date=`date +'%y%m%d'` +cd ../ +mkdir -p dist +echo Diff of: $N against $N.ORIG '->' $N-$date-ppc.patch +diff -uNr -X $N/arch/ppc/ignore $N.ORIG $N > dist/$N-$date-ppc.patch diff -u --recursive --new-file v2.1.47/linux/arch/ppc/mkdist linux/arch/ppc/mkdist --- v2.1.47/linux/arch/ppc/mkdist Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mkdist Thu Jul 31 13:09:17 1997 @@ -0,0 +1,9 @@ +#!/bin/bash + +N=`basename $PWD` +V=`echo $N | sed 's/linux-//'` +date=`date +'%y%m%d'` +mkdir -p ../dist +mv zImage ../dist/zImage-$V-$date +mv System.map ../dist/System.map-$V-$date + diff -u --recursive --new-file v2.1.47/linux/arch/ppc/mktar linux/arch/ppc/mktar --- v2.1.47/linux/arch/ppc/mktar Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mktar Thu Jul 31 13:09:17 1997 @@ -0,0 +1,7 @@ +#!/bin/bash + +N=`basename $PWD` +date=`date +'%y%m%d'` +cd ../ +mkdir -p dist +tar -zcf dist/ppc$N-$date.tar.gz -X $N/arch/ppc/ignore $N diff -u --recursive --new-file v2.1.47/linux/arch/ppc/mm/Makefile linux/arch/ppc/mm/Makefile --- v2.1.47/linux/arch/ppc/mm/Makefile Mon May 27 02:00:59 1996 +++ linux/arch/ppc/mm/Makefile Thu Jul 31 13:09:17 1997 @@ -14,7 +14,7 @@ .c.s: $(CC) $(CFLAGS) -S $< -OBJS = fault.o init.o +OBJS = fault.o init.o extable.o mm.o: $(OBJS) $(LD) -r -o mm.o $(OBJS) @@ -22,9 +22,9 @@ modules: dep: - $(CPP) -M *.c > .depend - + fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend # # include a dependency file if one exists diff -u --recursive --new-file v2.1.47/linux/arch/ppc/mm/extable.c linux/arch/ppc/mm/extable.c --- v2.1.47/linux/arch/ppc/mm/extable.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mm/extable.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,57 @@ +/* + * linux/arch/ppc/mm/extable.c + * + * from linux/arch/i386/mm/extable.c + */ + +#include +#include +#include + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + +#if 1 /*ndef CONFIG_MODULES*/ + /* There is only the kernel to search. */ + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ret) return ret; +#else + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->ex_table_start == NULL) + continue; + ret = search_one_table(mp->ex_table_start, + mp->ex_table_end - 1, addr); + if (ret) return ret; + } +#endif + + return 0; +} diff -u --recursive --new-file v2.1.47/linux/arch/ppc/mm/fault.c linux/arch/ppc/mm/fault.c --- v2.1.47/linux/arch/ppc/mm/fault.c Wed Dec 18 00:49:52 1996 +++ linux/arch/ppc/mm/fault.c Thu Jul 31 13:09:17 1997 @@ -1,9 +1,14 @@ /* - * ARCH/ppc/mm/fault.c + * arch/ppc/mm/fault.c * * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * Ported to PPC by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) + * Modified by Cort Dougan and Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ #include @@ -17,132 +22,151 @@ #include #include #include +#include #include #include +#include -extern void die_if_kernel(char *, struct pt_regs *, long); -extern void do_page_fault(struct pt_regs *, unsigned long, unsigned long); -void new_page_fault(unsigned long address, unsigned long code, unsigned long text, - struct pt_regs *regs); - +#ifdef CONFIG_PMAC +extern void (*xmon_fault_handler)(void); +#endif + +/* the linux norm for the function name is show_regs() so + make it call dump_regs() on the mac -- Cort */ +#ifdef CONFIG_PMAC +#define show_regs dump_regs +#endif -#undef SHOW_FAULTS -#undef NOISY_INSTRFAULT -#undef NOISY_DATAFAULT - -unsigned int probingmem = 0; -#define NEWMM 1 +extern void die_if_kernel(char *, struct pt_regs *, long); +void bad_page_fault(struct pt_regs *, unsigned long); +void do_page_fault(struct pt_regs *, unsigned long, unsigned long); +void print_pte(struct _PTE); -void new_page_fault(unsigned long address, unsigned long ppc_code, - unsigned long text, struct pt_regs *regs) +void do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code) { - struct vm_area_struct * vma; - struct mm_struct *mm = current->mm; - - int intel_code = 0; - pgd_t *dir; - pmd_t *pmd; - pte_t *pte; - - /* - * bit 0 == 0 means no page found, 1 means protection fault - * bit 1 == 0 means read, 1 means write - * bit 2 == 0 means kernel, 1 means user-mode - */ - if (user_mode(regs)) intel_code |= 0x04; - if (!text && (ppc_code & 0x02000000)) intel_code |= 0x02; /* Load/store */ - if (!text && (ppc_code & 0x08000000)) - { - intel_code |= 0x01; /* prot viol */ - goto do_page; - } - - dir = pgd_offset(mm, address & PAGE_MASK); - if (dir) - { - pmd = pmd_offset(dir, address & PAGE_MASK); - if (pmd && pmd_present(*pmd)) - { - pte = pte_offset(pmd, address & PAGE_MASK); - if (pte && pte_present(*pte)) - { - MMU_hash_page(¤t->tss, address & PAGE_MASK, pte); - return; - } - } - } - + struct task_struct *tsk = current; + extern unsigned _end[]; + struct vm_area_struct * vma; + struct mm_struct *mm = current->mm; + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + + /*printk("do_page_fault() %s/%d addr %x nip %x regs %x error %x\n", + current->comm,current->pid,address,regs->nip,regs,error_code);*/ +#ifdef CONFIG_PMAC + if (xmon_fault_handler && regs->trap == 0x300) { + xmon_fault_handler(); + return; + } +#endif + if (in_interrupt()) { + static int complained; + if (complained < 20) { + ++complained; + printk("page fault in interrupt handler, addr=%lx\n", + address); + show_regs(regs); + } + } + if (current == NULL) + goto bad_area; + do_page: - down(&mm->mmap_sem); - vma = find_vma(current->mm, address); - if (!vma) - goto bad_area; - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; + down(&mm->mmap_sem); + vma = find_vma(tsk->mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (expand_stack(vma, address)) + goto bad_area; good_area: - /* a write */ - if (intel_code & 2) { - if (!(vma->vm_flags & VM_WRITE)) - { - goto bad_area; - } - /* a read */ - } else { - /* protection fault */ - if (intel_code & 1) - { - printk("prot fault\n"); - goto bad_area; - } - if (!(vma->vm_flags & (VM_READ | VM_EXEC))) - { - printk("no read or exec\n"); - goto bad_area; - } - } - handle_mm_fault(vma, address, intel_code & 2); - up(&mm->mmap_sem); flush_page(address); /* Flush & Invalidate cache - note: address is OK now */ - return; + if (error_code & 0xb5700000) + /* an error such as lwarx to I/O controller space, + address matching DABR, eciwx, etc. */ + goto bad_area; + + /* a write */ + if (error_code & 0x02000000) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + /* a read */ + } else { + /* protection fault */ + if ( error_code & 0x08000000 ) + goto bad_area; + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + handle_mm_fault(current, vma, address, error_code & 0x02000000); + up(&mm->mmap_sem); + /*printk("do_page_fault() return %s/%d addr %x msr %x\n", + current->comm,current->pid,address,regs->msr);*/ + /* not needed since flush_page_to_ram() works */ +#if 0 + flush_page(address); +#endif + return; bad_area: - up(&mm->mmap_sem); + up(¤t->mm->mmap_sem); + bad_page_fault(regs, address); +} + - /* Did we have an exception handler installed? */ - if(current->tss.excount != 0) { - if(user_mode(regs)) { - printk("Exception signalled from user mode!\n"); - } else { -#if 0 - printk("Exception from kernel mode. pc %x expc %x count %d\n", - regs->nip,current->tss.expc,current->tss.excount); -#endif - current->tss.excount = 0; - regs->gpr[3] = -EFAULT; - regs->nip = current->tss.expc; - return; - } - } +void +bad_page_fault(struct pt_regs *regs, unsigned long address) +{ + extern unsigned int probingmem; + struct task_struct *tsk = current; + unsigned long fixup; + + + /* Are we prepared to handle this fault? */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + if ( user_mode(regs) ) + printk("Exception from user mode\n"); +#if 0 + printk(KERN_DEBUG "Exception at %lx (%lx)\n", regs->nip, fixup); +#endif + regs->nip = fixup; + return; + } + + if ( user_mode(regs) ) + { + force_sig(SIGSEGV, tsk); + return; + } - if (user_mode(regs)) - { - force_sig(SIGSEGV, current); - return; - } - panic("KERNEL access of bad area PC %x address %x vm_flags %x\n", - regs->nip,address,vma->vm_flags); +bad_kernel_access: + /* make sure it's not a bootup probe test */ + if ( probingmem ) + { + probingmem = 0; + return; + } + /* kernel has accessed a bad area */ + show_regs(regs); + print_backtrace( regs->gpr[1] ); +#ifdef CONFIG_PMAC + xmon(regs); +#endif + panic("kernel access of bad area\n pc %x address %X tsk %s/%d", + regs->nip,address,tsk->comm,tsk->pid); } -va_to_phys(unsigned long address) +unsigned long va_to_phys(unsigned long address) { pgd_t *dir; pmd_t *pmd; pte_t *pte; + dir = pgd_offset(current->mm, address & PAGE_MASK); if (dir) { @@ -165,8 +189,28 @@ return (0); } -inline void -update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t _pte) +void print_pte(struct _PTE p) +{ + printk( +"%08x %08x vsid: %06x h: %01x api: %02x rpn: %05x rcwimg: %d%d%d%d%d%d pp: %02x\n", + *((unsigned long *)(&p)), *((long *)&p+1), + p.vsid, p.h, p.api, p.rpn, + p.r,p.c,p.w,p.i,p.m,p.g,p.pp); +} + +/* + * Search the hw hash table for a mapping to the given physical + * address. -- Cort + */ +unsigned long htab_phys_to_va(unsigned long address) { - MMU_hash_page(¤t->tss, address & PAGE_MASK, (pte *)&_pte); + extern PTE *Hash, *Hash_end; + PTE *ptr; + + for ( ptr = Hash ; ptr < Hash_end ; ptr++ ) + { + if ( ptr->rpn == (address>>12) ) + printk("phys %08X -> va ???\n", + address); + } } diff -u --recursive --new-file v2.1.47/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c --- v2.1.47/linux/arch/ppc/mm/init.c Tue May 13 22:41:03 1997 +++ linux/arch/ppc/mm/init.c Thu Jul 31 13:09:17 1997 @@ -1,9 +1,21 @@ /* * arch/ppc/mm/init.c * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - * Ported to PPC by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * */ #include @@ -18,57 +30,354 @@ #include #include #include +#include +#ifdef CONFIG_PMAC +#include +#endif +#include +#include #include +#include +#ifdef CONFIG_PREP #include +#endif -extern pgd_t swapper_pg_dir[1024]; -extern unsigned long empty_zero_page[1024]; +int next_mmu_context; +extern pgd_t swapper_pg_dir[]; +extern char _start[], _end[]; +extern char etext[], _stext[]; +/* References to section boundaries */ +extern char __init_begin, __init_end; extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); -void flush_hash_table(void); +extern unsigned long *find_end_of_memory(void); -#undef HASHSTATS +#undef MAP_RAM_WITH_SEGREGS 1 -unsigned long _SDR1; /* Hardware SDR1 image */ -PTE *Hash; -int Hash_size, Hash_mask; +#ifdef CONFIG_PMAC +void *find_mem_piece(unsigned, unsigned); +static void mapin_ram(void); +static void inherit_prom_translations(void); +#endif +#ifdef CONFIG_PREP +inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va); +int inline MMU_hash_page(struct task_struct *,unsigned long,pte *); +#endif + +static void hash_init(void); +static void *MMU_get_page(void); +void map_page(struct thread_struct *, unsigned long va, + unsigned long pa, int flags); + +PTE *Hash, *Hash_end; +unsigned long Hash_size, Hash_mask; unsigned long *end_of_DRAM; -int cache_is_copyback = 1; -int kernel_pages_are_copyback = 1; -/* Note: these need to be in 'data' so they live over the boot */ -unsigned char *BeBox_IO_page = 0; -unsigned long isBeBox[2] = {0, 0}; +int mem_init_done; +#ifdef CONFIG_PREP #ifdef HASHSTATS -extern unsigned long *hashhits; -#endif - +extern unsigned long evicts; +#endif /* HASHSTATS */ +/* + * these are used to setup the initial page tables + * They can waste up to an entire page since the + * I'll fix this shortly -- Cort + */ +#define MAX_MMU_PAGES 16 +unsigned int probingmem = 0; +unsigned int mmu_pages_count = 0; +char mmu_pages[(MAX_MMU_PAGES+1)*PAGE_SIZE]; +unsigned long _TotalMemory; +#endif /* CONFIG_PREP */ +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving a inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +unsigned long empty_bad_page_table; pte_t * __bad_pagetable(void) { - panic("__bad_pagetable"); + memset((void *)empty_bad_page_table, 0, PAGE_SIZE); + return (pte_t *) empty_bad_page_table; } +unsigned long empty_bad_page; + pte_t __bad_page(void) { - panic("__bad_page"); + memset((void *)empty_bad_page, 0, PAGE_SIZE); + return pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED)); } +#ifdef CONFIG_PMAC +#define MAX_MEM_REGIONS 32 +phandle memory_pkg; + +struct mem_pieces { + int n_regions; + struct reg_property regions[MAX_MEM_REGIONS]; +}; + +struct mem_pieces phys_mem; +struct mem_pieces phys_avail; +struct mem_pieces prom_mem; + +static void get_mem_prop(char *, struct mem_pieces *); +static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); +static void print_mem_pieces(struct mem_pieces *); + +unsigned long avail_start; +int prom_trashed; + +/* + * Read in a property describing some pieces of memory. + */ +static void +get_mem_prop(char *name, struct mem_pieces *mp) +{ + int s, i; + + s = (int) call_prom("getprop", 4, 1, memory_pkg, name, + mp->regions, sizeof(mp->regions)); + if (s < sizeof(mp->regions[0])) { + printk("getprop /memory %s returned %d\n", name, s); + abort(); + } + mp->n_regions = s / sizeof(mp->regions[0]); + + /* + * Make sure the pieces are sorted. + */ + for (i = 1; i < mp->n_regions; ++i) { + unsigned long a, s; + int j; + + a = mp->regions[i].address; + s = mp->regions[i].size; + for (j = i - 1; j >= 0; --j) { + if (a >= mp->regions[j].address) + break; + mp->regions[j+1] = mp->regions[j]; + } + mp->regions[j+1].address = a; + mp->regions[j+1].size = s; + } +} + +/* + * Remove some memory from an array of pieces + */ +static void +remove_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size, + int must_exist) +{ + int i, j; + unsigned end, rs, re; + struct reg_property *rp; + + end = start + size; + for (i = 0, rp = mp->regions; i < mp->n_regions; ++i, ++rp) { + if (end > rp->address && start < rp->address + rp->size) + break; + } + if (i >= mp->n_regions) { + if (must_exist) + printk("remove_mem_piece: [%x,%x) not in any region\n", + start, end); + return; + } + for (; i < mp->n_regions && end > rp->address; ++i, ++rp) { + rs = rp->address; + re = rs + rp->size; + if (must_exist && (start < rs || end > re)) { + printk("remove_mem_piece: bad overlap [%x,%x) with", + start, end); + print_mem_pieces(mp); + must_exist = 0; + } + if (start > rs) { + rp->size = start - rs; + if (end < re) { + /* need to split this entry */ + if (mp->n_regions >= MAX_MEM_REGIONS) + panic("eek... mem_pieces overflow"); + for (j = mp->n_regions; j > i + 1; --j) + mp->regions[j] = mp->regions[j-1]; + ++mp->n_regions; + rp[1].address = end; + rp[1].size = re - end; + } + } else { + if (end < re) { + rp->address = end; + rp->size = re - end; + } else { + /* need to delete this entry */ + for (j = i; j < mp->n_regions - 1; ++j) + mp->regions[j] = mp->regions[j+1]; + --mp->n_regions; + --i; + --rp; + } + } + } +} + +static void +print_mem_pieces(struct mem_pieces *mp) +{ + int i; + + for (i = 0; i < mp->n_regions; ++i) + printk(" [%x, %x)", mp->regions[i].address, + mp->regions[i].address + mp->regions[i].size); + printk("\n"); +} + +void * +find_mem_piece(unsigned size, unsigned align) +{ + int i; + unsigned a, e; + struct mem_pieces *mp = &phys_avail; + + for (i = 0; i < mp->n_regions; ++i) { + a = mp->regions[i].address; + e = a + mp->regions[i].size; + a = (a + align - 1) & -align; + if (a + size <= e) { + remove_mem_piece(mp, a, size, 1); + return __va(a); + } + } + printk("Couldn't find %u bytes at %u alignment\n", size, align); + abort(); + return NULL; +} + +/* + * Collect information about RAM and which pieces are already in use. + * At this point, we have the first 8MB mapped with a BAT. + * Our text, data, bss use something over 1MB, starting at 0. + * Open Firmware may be using 1MB at the 4MB point. + */ +unsigned long *find_end_of_memory(void) +{ + unsigned long a, total; + unsigned long h, kstart, ksize; + extern char _stext[], _end[]; + int i; + + memory_pkg = call_prom("finddevice", 1, 1, "/memory"); + if (memory_pkg == (void *) -1) + panic("can't find memory package"); + + /* + * Find out where physical memory is, and check that it + * starts at 0 and is contiguous. It seems that RAM is + * always physically contiguous on Power Macintoshes, + * because MacOS can't cope if it isn't. + */ + get_mem_prop("reg", &phys_mem); + if (phys_mem.n_regions == 0) + panic("No RAM??"); + a = phys_mem.regions[0].address; + if (a != 0) + panic("RAM doesn't start at physical address 0"); + total = phys_mem.regions[0].size; + for (i = 1; i < phys_mem.n_regions; ++i) { + a = phys_mem.regions[i].address; + if (a != total) { + printk("RAM starting at 0x%lx is not contiguous\n", a); + printk("Using RAM from 0 to 0x%lx\n", total-1); + phys_mem.n_regions = i; + break; + } + total += phys_mem.regions[i].size; + } + + /* record which bits the prom is using */ + get_mem_prop("available", &phys_avail); + prom_mem = phys_mem; + for (i = 0; i < phys_avail.n_regions; ++i) + remove_mem_piece(&prom_mem, phys_avail.regions[i].address, + phys_avail.regions[i].size, 1); + + /* + * phys_avail records memory we can use now. + * prom_mem records memory allocated by the prom that we + * don't want to use now, but we'll reclaim later. + * Make sure the kernel text/data/bss is in neither. + */ + kstart = __pa(_stext); /* should be 0 */ + ksize = PAGE_ALIGN(_end - _stext); + remove_mem_piece(&phys_avail, kstart, ksize, 0); + remove_mem_piece(&prom_mem, kstart, ksize, 0); + + /* + * Allow 64k of hash table for every 16MB of memory, + * up to a maximum of 2MB. + */ + for (h = 64<<10; h < total / 256 && h < 2<<20; h *= 2) + ; + Hash_size = h; + Hash_mask = (h >> 6) - 1; + + /* Find some memory for the hash table. */ + Hash = find_mem_piece(Hash_size, Hash_size); + printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", + total >> 20, Hash_size >> 10, Hash); + + return __va(total); +} + +/* + * Find some memory for setup_arch to return. + * We use the last chunk of available memory as the area + * that setup_arch returns, making sure that there are at + * least 32 pages unused before this for MMU_get_page to use. + */ +unsigned long find_available_memory(void) +{ + int i; + unsigned long a, free; + unsigned long start, end; + + free = 0; + for (i = 0; i < phys_avail.n_regions - 1; ++i) { + start = phys_avail.regions[i].address; + end = start + phys_avail.regions[i].size; + free += (end & PAGE_MASK) - PAGE_ALIGN(start); + } + a = PAGE_ALIGN(phys_avail.regions[i].address); + if (free < 32 * PAGE_SIZE) + a += 32 * PAGE_SIZE - free; + avail_start = (unsigned long) __va(a); + return avail_start; +} +#endif /* CONFIG_PMAC */ + void show_mem(void) { + int i,free = 0,total = 0,reserved = 0; + int shared = 0; struct task_struct *p; - unsigned long i,free = 0,total = 0,reserved = 0; - unsigned long shared = 0; - PTE *ptr; - unsigned long full = 0, overflow = 0; - unsigned int ti; printk("Mem-info:\n"); show_free_areas(); printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = MAP_NR(high_memory); + i = max_mapnr; while (i-- > 0) { total++; if (PageReserved(mem_map+i)) @@ -78,105 +387,185 @@ else shared += atomic_read(&mem_map[i].count) - 1; } - printk("%lu pages of RAM\n",total); - printk("%lu free pages\n",free); - printk("%lu reserved pages\n",reserved); - printk("%lu pages shared\n",shared); + 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 -#ifdef HASHSTATS - printk("Hash Hits %u entries (buckets)\n",(Hash_size/sizeof(struct _PTE))/8); - for ( i = 0; i < (Hash_size/sizeof(struct _PTE))/8; i++ ) { - if ( hashhits[i] >= 20 ) - printk("[%lu] \t %lu\n", i,hashhits[i]); - } -#endif - - for ( ptr = Hash ; ptr <= Hash+Hash_size ; ptr++) { - if (ptr->v) { - full++; - if (ptr->h == 1) - overflow++; - } + printk("%-8s %3s %3s %8s %8s %8s %9s %8s\n", "Process", "Pid", "Cnt", + "Ctx", "Ctx<<4", "Last Sys", "pc", "task"); + for_each_task(p) + { + printk("%-8.8s %3d %3d %8d %8d %8d %c%08x %08x", + p->comm,p->pid, + p->mm->count,p->mm->context, + p->mm->context<<4, p->tss.last_syscall, + user_mode(p->tss.regs) ? 'u' : 'k', p->tss.regs->nip, + p); + if ( p == current ) + printk(" current"); + printk("\n"); } - printk("Hash Table: %dkB Buckets: %dk PTEs: %d/%d (%%%d full) %d overflowed\n", - Hash_size>>10, (Hash_size/(sizeof(PTE)*8)) >> 10, - full,Hash_size/sizeof(PTE), - (full*100)/(Hash_size/sizeof(PTE)), - overflow); - printk(" Task context vsid0\n"); - read_lock(&tasklist_lock); - for_each_task(p) { - printk("%5d %8x %8x\n", - p->pid,p->mm->context, - ((SEGREG *)p->tss.segs)[0].vsid); - } - read_unlock(&tasklist_lock); } extern unsigned long free_area_init(unsigned long, unsigned long); +/* + * paging_init() sets up the page tables - in fact we've already done this. + */ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) { - return free_area_init(start_mem, end_mem); + /* + * Grab some memory for bad_page and bad_pagetable to use. + */ + empty_bad_page = start_mem; + empty_bad_page_table = start_mem + PAGE_SIZE; + start_mem += 2 * PAGE_SIZE; + + /* note: free_area_init uses its second argument + to size the mem_map array. */ + start_mem = free_area_init(start_mem, end_mem); + return start_mem; } void mem_init(unsigned long start_mem, unsigned long end_mem) { - int codepages = 0; - int datapages = 0; - unsigned long tmp; - extern int etext; + unsigned long addr; +#ifdef CONFIG_PMAC + int i; + unsigned long lim; +#endif + int codepages = 0; + int datapages = 0; + int initpages = 0; + + end_mem &= PAGE_MASK; + high_memory = (void *) end_mem; + num_physpages = max_mapnr = MAP_NR(high_memory); - end_mem &= PAGE_MASK; - high_memory = (void *)end_mem; - max_mapnr = MAP_NR(end_mem); - /* clear the zero-page */ - memset(empty_zero_page, 0, PAGE_SIZE); + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); /* mark usable pages in the mem_map[] */ - start_mem = PAGE_ALIGN(start_mem); + start_mem = PAGE_ALIGN(start_mem); + +#ifdef CONFIG_PMAC + remove_mem_piece(&phys_avail, __pa(avail_start), + start_mem - avail_start, 1); + + for (a = KERNELBASE ; a < end_mem; a += PAGE_SIZE) + set_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); + + for (i = 0; i < phys_avail.n_regions; ++i) { + a = (unsigned long) __va(phys_avail.regions[i].address); + lim = a + phys_avail.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) { + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); + mem_map[MAP_NR(a)].count = 1; + free_page(a); + } + } + phys_avail.n_regions = 0; - for (tmp = KERNELBASE ; tmp < (long)high_memory ; tmp += PAGE_SIZE) - { - if (tmp < start_mem) - { - set_bit(PG_reserved, &mem_map[MAP_NR(tmp)].flags); - if (tmp < (unsigned long) &etext) - { - codepages++; - } else - { - datapages++; - } - continue; - } - clear_bit(PG_reserved, &mem_map[MAP_NR(tmp)].flags); - atomic_set(&mem_map[MAP_NR(tmp)].count, 1); - free_page(tmp); - } - tmp = nr_free_pages << PAGE_SHIFT; - printk("Memory: %luk/%luk available (%dk kernel code, %dk data)\n", - tmp >> 10, - ((int)high_memory - (int)KERNELBASE) >> 10, - codepages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10)); - /* invalidate();*/ - return; + /* free the prom's memory */ + for (i = 0; i < prom_mem.n_regions; ++i) { + a = (unsigned long) __va(prom_mem.regions[i].address); + lim = a + prom_mem.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) { + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); + mem_map[MAP_NR(a)].count = 1; + free_page(a); + } + } + prom_trashed = 1; +#endif /* CONFIG_PMAC */ + +#ifdef CONFIG_PREP + /* mark mem used by kernel as reserved, mark other unreserved */ + for (addr = PAGE_OFFSET ; addr < end_mem; addr += PAGE_SIZE) + { + /* skip hash table gap */ + if ( (addr > (ulong)_end) && (addr < (ulong)Hash)) + continue; + if ( addr < (ulong) /*Hash_end*/ start_mem ) + set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + else + clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + } + + for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { + if(PageReserved(mem_map + MAP_NR(addr))) { + if (addr < (ulong) etext) + codepages++; + /*else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) + initpages++;*/ + else if (addr < (ulong) start_mem) + datapages++; + continue; + } + atomic_set(&mem_map[MAP_NR(addr)].count, 1); +#ifdef CONFIG_BLK_DEV_INITRD + if (!initrd_start || + (addr < initrd_start || addr >= initrd_end)) +#endif /* CONFIG_BLK_DEV_INITRD */ + free_page(addr); + } + +#endif /* CONFIG_PREP */ + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n", + (unsigned long) nr_free_pages << (PAGE_SHIFT-10), + codepages << (PAGE_SHIFT-10), + datapages << (PAGE_SHIFT-10), + initpages << (PAGE_SHIFT-10), + PAGE_OFFSET, end_mem); + mem_init_done = 1; } +/* + * this should reclaim gap between _end[] and hash table + * as well as unused mmu_pages[] on prep systems. + * When I get around to it, I'll put initialization functions + * (called only at boot) in their own .section and free that -- Cort + */ void free_initmem(void) { - /* To be written */ + unsigned long addr; + unsigned long a; + unsigned long num_freed_pages = 0; + + /* free unused mmu_pages[] */ + a = PAGE_ALIGN( (unsigned long) mmu_pages) + (mmu_pages_count*PAGE_SIZE); + for ( ; a < PAGE_ALIGN((unsigned long)mmu_pages)+(MAX_MMU_PAGES*PAGE_SIZE); a += PAGE_SIZE ) + { + clear_bit( PG_reserved, &mem_map[MAP_NR(a)].flags ); + atomic_set(&mem_map[MAP_NR(a)].count, 1); + free_page(a); + num_freed_pages++; + } + +#if 0 + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + num_freed_pages++; + mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); + mem_map[MAP_NR(addr)].count = 1; + free_page(addr); + } +#endif + printk ("Freeing unused kernel memory: %dk freed\n", + (num_freed_pages * PAGE_SIZE) >> 10); } void si_meminfo(struct sysinfo *val) { int i; - i = ((int)high_memory & 0x00FFFFFF) >> PAGE_SHIFT; + i = max_mapnr; val->totalram = 0; val->sharedram = 0; val->freeram = nr_free_pages << PAGE_SHIFT; @@ -187,526 +576,637 @@ val->totalram++; if (!atomic_read(&mem_map[i].count)) continue; - val->sharedram += atomic_read(&mem_map[i].count) - 1; + val->sharedram += atomic_read(&mem_map[i].count)-1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; return; } +/* Kernel MMU setup & lowest level hardware support */ + +unsigned long _SDR1; /* Hardware SDR1 image */ + +#ifdef CONFIG_PREP + BAT BAT0 = - { - { - 0x80000000>>17, /* bepi */ - BL_256M, /* bl */ - 1, /* vs -- supervisor mode valid */ - 1, /* vp -- user mode valid */ - }, - { - 0x80000000>>17, /* brpn */ - 1, /* write-through */ - 1, /* cache-inhibited */ - 0, /* memory coherence */ - 1, /* guarded */ - BPP_RW /* protection */ - } - }; +{ + { + 0x80000000>>17, /* bepi */ + BL_256M, /* bl */ + 1, /* vs -- supervisor mode valid */ + 1, /* vp -- user mode valid */ + }, + { + 0x80000000>>17, /* brpn */ + 1, /* write-through */ + 1, /* cache-inhibited */ + 0, /* memory coherence */ + 1, /* guarded */ + BPP_RW /* protection */ + } +}; BAT BAT1 = - { - { - 0xC0000000>>17, /* bepi */ - BL_256M, /* bl */ - 1, /* vs */ - 1, /* vp */ - }, - { - 0xC0000000>>17, /* brpn */ - 1, /* w */ - 1, /* i (cache disabled) */ - 0, /* m */ - 1, /* g */ - BPP_RW /* pp */ - } - }; +{ + { + 0xC0000000>>17, /* bepi */ + BL_256M, /* bl */ + 1, /* vs */ + 1, /* vp */ + }, + { + 0xC0000000>>17, /* brpn */ + 1, /* w */ + 1, /* i (cache disabled) */ + 0, /* m */ + 1, /* g */ + BPP_RW /* pp */ + } +}; BAT BAT2 = - { - { - 0x90000000>>17, /* bepi */ - BL_256M, /* this gets set to amount of phys ram */ - 1, /* vs */ - 0, /* vp */ - }, - { - 0x00000000>>17, /* brpn */ - 0, /* w */ - 0, /* i */ - 0, /* m */ - 0, /* g */ - BPP_RW /* pp */ - } - }; +{ + { + 0x90000000>>17, /* bepi */ + BL_256M, /* this gets set to amount of phys ram */ + 1, /* vs */ + 0, /* vp */ + }, + { + 0x00000000>>17, /* brpn */ + 0, /* w */ + 0, /* i */ + 1, /* m */ + 0, /* g */ + BPP_RW /* pp */ + } +}; BAT BAT3 = - { - { - 0x00000000>>17, /* bepi */ - BL_256M, /* bl */ - 0, /* vs */ - 0, /* vp */ - }, - { - 0x00000000>>17, /* brpn */ - 1, /* w */ - 1, /* i (cache disabled) */ - 0, /* m */ - 0, /* g */ - BPP_RW /* pp */ - } - }; -BAT TMP_BAT2 = - { /* 0x9XXXXXXX -> 0x0XXXXXXX */ - { - 0x90000000>>17, /* bepi */ - BL_256M, /* bl */ - 1, /* vs */ - 1, /* vp */ - }, - { - 0x00000000>>17, /* brpn */ - 1, /* w */ - 0, /* i (cache enabled) */ - 0, /* m */ - 0, /* g */ - BPP_RW /* pp */ - } - }; - - -#ifndef NULL -#define NULL 0 -#endif - -/* - * This code is called to create a minimal mapped environment. - * It is called with the MMU on, but with only a BAT register - * set up to cover the code/data. After this routine runs, - * the BAT mapping is withdrawn and all mappings must be complete. - */ +{ + { + 0x00000000>>17, /* bepi */ + BL_256M, /* bl */ + 0, /* vs */ + 0, /* vp */ + }, + { + 0x00000000>>17, /* brpn */ + 0, /* w */ + 0, /* i (cache disabled) */ + 1, /* m */ + 0, /* g */ + BPP_RW /* pp */ + } +}; +P601_BAT BAT0_601 = +{ + { + 0x80000000>>17, /* bepi */ + 1,1,0, /* wim */ + 1, 0, /* vs, vp */ + BPP_RW, /* pp */ + }, + { + 0x80000000>>17, /* brpn */ + 1, /* v */ + BL_8M, /* bl */ + } +}; +P601_BAT BAT1_601 = +{ + { + 0xC0000000>>17, /* bepi */ + 1,1,0, /* wim */ + 1, 0, /* vs, vp */ + BPP_RW, /* pp */ + }, + { + 0xC0000000>>17, /* brpn */ + 1, /* v */ + BL_8M, /* bl */ + } +}; +P601_BAT BAT2_601 = +{ + { + 0x90000000>>17, /* bepi */ + 0,0,0, /* wim */ + 1, 0, /* vs, vp */ + BPP_RW, /* pp */ + }, + { + 0x00000000>>17, /* brpn */ + 1, /* v */ + BL_8M, /* bl */ + } +}; -extern char _start[], _end[]; - -void MMU_init(void) +P601_BAT BAT3_601 = { - extern RESIDUAL res; - extern unsigned long resptr; - int i, p; - SEGREG *segs; - - /* copy residual data */ - if ( resptr ) - memcpy( &res, (void *)(resptr+KERNELBASE), sizeof(RESIDUAL) ); - else - bzero( &res, sizeof(RESIDUAL) ); /* clearing bss probably clears this but... */ - - end_of_DRAM = (unsigned long *)find_end_of_memory(); - _SDR1 = ((unsigned long)Hash - KERNELBASE) | Hash_mask; -#if 0 - printk("Hash %08x\n",(unsigned long)Hash); - printk("Hash_mask %08x\n",Hash_mask); - printk("Hash_size %08x\n",Hash_size); - printk("SDR1 %08x\n",_SDR1); -#endif - /* Segment registers */ - segs = (SEGREG *)init_task.tss.segs; - for (i = 0; i < 16; i++) - { - segs[i].ks = 0; - segs[i].kp = 1; -#if 1 - if ( i < 8 ) - segs[i].vsid = i+10000; - else -#else - if ( i < 8 ) - segs[i].vsid = i<<5; -#endif - segs[i].vsid = i; + { + 0x90800000>>17, /* bepi */ + 0,0,0, /* wim */ + 1, 0, /* vs, vp */ + BPP_RW, /* pp */ + }, + { + 0x00800000>>17, /* brpn */ + 1, /* v */ + BL_8M, /* bl */ } - - - - /* Hard map in any special local resources */ - if (isBeBox[0]) - { - /* Map in one page for the BeBox motherboard I/O */ - end_of_DRAM = (unsigned long *)((unsigned long)end_of_DRAM - PAGE_SIZE); -#if 0 - BeBox_IO_page = (unsigned char *)0x7FFFF000; -#endif - BeBox_IO_page = (unsigned char *)end_of_DRAM; - MMU_disable_cache_for_page(&init_task.tss, BeBox_IO_page); - } -} +}; /* - * Insert(create) a hardware page table entry + * This finds the amount of physical ram and does necessary + * setup for prep. This is pretty architecture specific so + * this will likely stay seperate from the pmac. + * -- Cort */ -int inline MMU_hash_page(struct thread_struct *tss, unsigned long va, pte *pg) +unsigned long *find_end_of_memory(void) { - int hash, page_index, segment, i, h, _h, api, vsid, perms; - PTE *_pte, *empty, *slot; - PTE *slot0, *slot1; - extern char _etext; - page_index = ((int)va & 0x0FFFF000) >> 12; - segment = (unsigned int)va >> 28; - api = page_index >> 10; - vsid = ((SEGREG *)tss->segs)[segment].vsid; - empty = slot = (PTE *)NULL; + extern RESIDUAL res; + extern unsigned long resptr; + int i, p; + unsigned long h; + + /* copy residual data */ + if ( resptr ) + memcpy( &res, (void *)(resptr+KERNELBASE), sizeof(RESIDUAL) ); + else + /* clearing bss probably clears this but... */ + memset( &res, sizeof(RESIDUAL), 0 ); + _TotalMemory = res.TotalMemory; + + /* this really has nothing to do with the mmu_init() but is + necessary for early setup -- Cort */ + if (!strncmp(res.VitalProductData.PrintableModel,"IBM",3)) + { + _machine = _MACH_IBM; + } + else + _machine = _MACH_Motorola; - if ( (va <= _etext) && (va >= KERNELBASE)) - { - printk("MMU_hash_page: called on kernel page mapped with bats va %x\n", - va); - } - - /* check first hash bucket */ - h = 0; - hash = page_index ^ vsid; - hash &= 0x3FF | (Hash_mask << 10); - hash *= 8; /* 8 entries in each bucket */ - _pte = &Hash[hash]; - slot0 = _pte; - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == h && _pte->api == api) + /* setup the hash table */ + if (_TotalMemory == 0 ) { - slot = _pte; - goto found_it; + /* + * I need a way to probe the amount of memory if the residual + * data doesn't contain it. -- Cort + */ + printk("Ramsize from residual data was 0 -- Probing for value\n"); + _TotalMemory = 0x03000000; + printk("Ramsize default to be %dM\n", _TotalMemory>>20); } - if ((empty == NULL) && (!_pte->v)) + +#if 0 + /* linux has trouble with > 64M ram -- Cort */ + if ( _TotalMemory > 0x04000000 /* 64M */ ) { - empty = _pte; - _h = h; + printk("Only using first 64M of ram.\n"); + _TotalMemory = 0x04000000; } - } - - /* check second hash bucket */ - h = 1; - hash = page_index ^ vsid; - hash = ~hash; - hash &= 0x3FF | (Hash_mask << 10); - hash *= 8; /* 8 entries in each bucket */ - _pte = &Hash[hash]; - slot1 = _pte; - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == h && _pte->api == api) +#endif + + /* setup the bat2 mapping to cover physical ram */ + BAT2.batu.bl = 0x1; /* 256k mapping */ + for ( h = 256*1024 /* 256k */ ; (h <= _TotalMemory) && (h <= 256*1024*1024); + h *= 2 ) + BAT2.batu.bl = (BAT2.batu.bl << 1) | BAT2.batu.bl; + /* + * Allow 64k of hash table for every 16MB of memory, + * up to a maximum of 2MB. + */ + for (h = 64<<10; h < _TotalMemory / 256 && h < 2<<20; h *= 2) + ; + Hash_size = h; + Hash_mask = (h >> 6) - 1; + + /* align htab on a Hash_size boundry above _end[] */ + Hash = (PTE *)_ALIGN( (unsigned long)&_end, Hash_size); + memset(Hash, Hash_size, 0 ); + + /* + * if this is a 601, we can only map sizes of 8M with the BAT's + * so we have to map what we can't map with the bats with the segregs + * head.S will copy in the appropriate BAT's according to the processor + * since the 601_BAT{2,3} structures are already setup to map + * the first 16M correctly + * -- Cort + */ +#ifndef MAP_RAM_WITH_SEGREGS /* don't need to do it twice */ + if ( _get_PVR() == 1 ) { - slot = _pte; - goto found_it; + /* map in rest of ram with seg regs */ + if ( _TotalMemory > 0x01000000 /* 16M */) + { + for (i = KERNELBASE+0x01000000; + i < KERNELBASE+_TotalMemory; i += PAGE_SIZE) + map_page(&init_task.tss, i, __pa(i), + _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); + } } - if ((empty == NULL) && (!_pte->v)) +#endif /* MAP_RAM_WITH_SEGREGS */ + +#ifdef MAP_RAM_WITH_SEGREGS + /* turn off bat mapping kernel since being done with segregs */ + memset(&BAT2, sizeof(BAT2), 0); + memset(&BAT2_601, sizeof(BAT2), 0); /* in case we're on a 601 */ + memset(&BAT3_601, sizeof(BAT2), 0); + /* map all of ram for kernel with segregs */ + for (i = KERNELBASE; i < KERNELBASE+_TotalMemory; i += PAGE_SIZE) { - empty = _pte; - _h = h; + if ( i < (unsigned long)etext ) + map_page(&init_task.tss, i, __pa(i), + _PAGE_PRESENT/*| _PAGE_RW*/|_PAGE_DIRTY|_PAGE_ACCESSED); + else + map_page(&init_task.tss, i, __pa(i), + _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); } - } - - if (empty == (PTE *)NULL) - { -#if 1 - printk("Both hash buckets full! va %x vsid %x current %s (%d)\n", - va,vsid,current->comm,current->pid); -#endif - slot = slot1; - h = 1; - } - else - { - slot = empty; - h = _h; - } -found_it: -#ifdef HASHSTATS - hashhits[hash]++; -#endif - _tlbie(va); /* Clear TLB */ - /* Fill in table */ - slot->v = 1; - slot->vsid = vsid; - slot->h = h; - slot->api = api; - if (((pg->page_num << 12) & 0xF0000000) == KERNELBASE) - { - slot->rpn = pg->page_num - (KERNELBASE>>12); - } else - { - slot->rpn = pg->page_num; - } - slot->r = 0; - slot->c = 0; - slot->i = 0; - slot->g = 0; - if (cache_is_copyback) - { - if (kernel_pages_are_copyback || (pg->flags & _PAGE_USER) || (va < (unsigned long)&_etext)) - { /* All User & Kernel TEXT pages are copy-back */ - slot->w = 0; - slot->m = 1; - } else - { /* Kernel DATA pages are write-thru */ - slot->w = 1; - slot->m = 0; - } - } else - { - slot->w = 1; - slot->m = 0; - } - if (pg->flags & _PAGE_USER) - { - if (pg->flags & _PAGE_RW) - { /* Read/write page */ - perms = PP_RWRW; - } else - { /* Read only page */ - perms = PP_RWRX; - perms = PP_RXRX; - } - } else - { /* Kernel pages */ - perms = PP_RWRW; - perms = PP_RWXX; - } - slot->pp = perms; - return (0); +#endif /* MAP_RAM_WITH_SEGREGS */ + + printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", + _TotalMemory >> 20, Hash_size >> 10, Hash); + return ((unsigned long *)_TotalMemory); } +#endif /* CONFIG_PREP */ + +#ifdef CONFIG_PMAC /* - * Disable cache for a particular page + * Map in all of physical memory starting at KERNELBASE. */ -MMU_disable_cache_for_page(struct thread_struct *tss, unsigned long va) +extern int n_mem_regions; +extern struct reg_property mem_regions[]; + +#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED) + +static void mapin_ram() { - int hash, page_index, segment, i, h, _h, api, vsid, perms; - PTE *_pte, *empty, *slot; - PTE *slot0, *slot1; - extern char _etext; - page_index = ((int)va & 0x0FFFF000) >> 12; - segment = (unsigned int)va >> 28; - api = page_index >> 10; - vsid = ((SEGREG *)tss->segs)[segment].vsid; - empty = slot = (PTE *)NULL; - for (_h = 0; _h < 2; _h++) - { - hash = page_index ^ vsid; - if (_h) - { - hash = ~hash; /* Secondary hash uses ones-complement */ - } - hash &= 0x3FF | (Hash_mask << 10); - hash *= 8; /* Eight entries / hash bucket */ - _pte = &Hash[hash]; - /* Save slot addresses in case we have to purge */ - if (_h) - { - slot1 = _pte; - } else - { - slot0 = _pte; - } - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == _h && _pte->api == api) - { /* Found it! */ - h = _h; - slot = _pte; - goto found_it; - } - if ((empty == (PTE *)NULL) && !_pte->v) - { - h = _h; - empty = _pte; - } - } + int i; + unsigned long v, p, s, f; + + v = KERNELBASE; + for (i = 0; i < phys_mem.n_regions; ++i) { + p = phys_mem.regions[i].address; + for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { + f = _PAGE_PRESENT | _PAGE_ACCESSED; + if ((char *) v < _stext || (char *) v >= etext) + f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; + map_page(&init_task.tss, v, p, f); + v += PAGE_SIZE; + p += PAGE_SIZE; } -found_it: - _tlbie(va); /* Clear TLB */ - slot->i = 1; - slot->m = 0; + } } +#define MAX_PROM_TRANSLATIONS 64 + +static struct translation_property prom_translations[MAX_PROM_TRANSLATIONS]; +int n_translations; +phandle mmu_pkg; +extern ihandle prom_chosen; + +static void inherit_prom_translations() +{ + int s, i, f; + unsigned long v, p, n; + struct translation_property *tp; + ihandle mmu_inst; + + if ((int) call_prom("getprop", 4, 1, prom_chosen, "mmu", + &mmu_inst, sizeof(mmu_inst)) != sizeof(mmu_inst)) + panic("couldn't get /chosen mmu property"); + mmu_pkg = call_prom("instance-to-package", 1, 1, mmu_inst); + if (mmu_pkg == (phandle) -1) + panic("couldn't get mmu package"); + s = (int) call_prom("getprop", 4, 1, mmu_pkg, "translations", + &prom_translations, sizeof(prom_translations)); + if (s < sizeof(prom_translations[0])) + panic("couldn't get mmu translations property"); + n_translations = s / sizeof(prom_translations[0]); + + for (tp = prom_translations, i = 0; i < n_translations; ++i, ++tp) { + /* ignore stuff mapped down low */ + if (tp->virt < 0x10000000) + continue; + /* map PPC mmu flags to linux mm flags */ + f = (tp->flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU + | _PAGE_COHERENT | _PAGE_GUARDED)) + | pgprot_val(PAGE_KERNEL); + /* add these pages to the mappings */ + v = tp->virt; + p = tp->phys; + n = tp->size; + for (; n != 0; n -= PAGE_SIZE) { + map_page(&init_task.tss, v, p, f); + v += PAGE_SIZE; + p += PAGE_SIZE; + } + } +} +#endif /* - * invalidate a hardware hash table pte + * Initialize the hash table and patch the instructions in head.S. */ -inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va) +static void hash_init(void) { - int hash, page_index, segment, i, h, _h, api, vsid, perms; - PTE *_pte, *slot; - int flags = 0; - page_index = ((int)va & 0x0FFFF000) >> 12; - segment = (unsigned int)va >> 28; - api = page_index >> 10; - vsid = mm->context | segment; - for (_h = 0; _h < 2; _h++) - { - hash = page_index ^ vsid; - if (_h) - { - hash = ~hash; /* Secondary hash uses ones-complement */ - } - hash &= 0x3FF | (Hash_mask << 10); - hash *= 8; /* Eight entries / hash bucket */ - _pte = &Hash[hash]; - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == _h && _pte->api == api) - { /* Found it! */ - _tlbie(va); /* Clear TLB */ - if (_pte->r) flags |= _PAGE_ACCESSED; - if (_pte->c) flags |= _PAGE_DIRTY; - _pte->v = 0; - return (flags); - } - } - } - _tlbie(va); - return (flags); + int Hash_bits; + + extern unsigned int hash_page_patch_A[], hash_page_patch_B[], + hash_page_patch_C[]; + + memset(Hash, 0, Hash_size); + Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); + + /* + * Patch up the instructions in head.S:hash_page + */ + Hash_bits = ffz(~Hash_size) - 6; + hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff) + | (__pa(Hash) >> 16); + hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0) + | ((26 - Hash_bits) << 6); + if (Hash_bits > 16) + Hash_bits = 16; + hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0) + | ((26 - Hash_bits) << 6); + hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff) + | (Hash_mask >> 10); + hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff) + | (Hash_mask >> 10); + + /* + * Ensure that the locations we've patched have been written + * out from the data cache and invalidated in the instruction + * cache, on those machines with split caches. + */ + store_cache_range((unsigned long) hash_page_patch_A, + (unsigned long) (hash_page_patch_C + 1)); } -inline void -flush_cache_all(void) +/* + * Do very early mm setup such as finding the size of memory + * and setting up the hash table. + * A lot of this is prep/pmac specific but a lot of it could + * still be merged. + * -- Cort + */ +void +MMU_init(void) { + end_of_DRAM = find_end_of_memory(); + hash_init(); + _SDR1 = __pa(Hash) | (Hash_mask >> 10); +#ifdef CONFIG_PMAC + /* Force initial page tables */ + /* this done by INIT_TSS in processor.h on prep -- Cort */ + init_task.tss.pg_tables = (unsigned long *)swapper_pg_dir; + + /* Map in all of RAM starting at KERNELBASE */ + mapin_ram(); + /* Copy mappings from the prom */ + inherit_prom_translations(); +#endif /* CONFIG_PMAC */ } -inline void -flush_cache_mm(struct mm_struct *mm) -{ -} -inline void -flush_cache_page(struct vm_area_struct *vma, long va) -{ -} -inline void -flush_cache_range(struct mm_struct *mm, unsigned long va_start, unsigned long va_end) -{ -} -inline void -cache_mode(char *str, int *ints) +static void * +MMU_get_page() { - cache_is_copyback = ints[0]; + void *p; + + if (mem_init_done) { + p = (void *) __get_free_page(GFP_KERNEL); + if (p == 0) + panic("couldn't get a page in MMU_get_page"); + } else { +#ifdef CONFIG_PREP + mmu_pages_count++; + if ( mmu_pages_count > MAX_MMU_PAGES ) + printk("out of mmu pages!\n"); + p = (pte *)(PAGE_ALIGN((unsigned long)mmu_pages)+ + (mmu_pages_count+PAGE_SIZE)); +#endif +#ifdef CONFIG_PMAC + p = find_mem_piece(PAGE_SIZE, PAGE_SIZE); +#endif + } + memset(p, 0, PAGE_SIZE); + return p; +} + +#ifdef CONFIG_PMAC +void * +ioremap(unsigned long addr, unsigned long size) +{ + unsigned long p, end = addr + size; + + for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE) + map_page(&init_task.tss, p, p, pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); + return (void *) addr; +} +#endif + +void +map_page(struct thread_struct *tss, unsigned long va, + unsigned long pa, int flags) +{ + pmd_t *pd; + pte_t *pg; + + if (tss->pg_tables == NULL) { + /* Allocate upper level page map */ + tss->pg_tables = (unsigned long *) MMU_get_page(); + } + /* Use upper 10 bits of VA to index the first level map */ + pd = (pmd_t *) (tss->pg_tables + (va >> PGDIR_SHIFT)); + if (pmd_none(*pd)) { + /* Need to allocate second-level table */ + pg = (pte_t *) MMU_get_page(); + pmd_val(*pd) = (unsigned long) pg; + } + /* Use middle 10 bits of VA to index the second-level map */ + pg = pte_offset(pd, va); + set_pte(pg, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); + /*flush_hash_page(va >> 28, va);*/ + flush_hash_page(0, va); } /* * TLB flushing: * - * - flush_tlb() flushes the current mm struct TLBs * - flush_tlb_all() flushes all processes TLBs * - flush_tlb_mm(mm) flushes the specified mm context TLB's * - flush_tlb_page(vma, vmaddr) flushes one page * - flush_tlb_range(mm, start, end) flushes a range of pages * * since the hardware hash table functions as an extension of the - * tlb as far as the linux tables are concerned, flush them too. + * tlb as far as the linux tables are concerned, flush it too. * -- Cort */ -inline void -flush_tlb(void) -{ - PTE *ptep; - int context = current->mm->context; - struct vm_area_struct *v; - unsigned int i; - - v = current->mm->mmap; - - /* for every virtual memory address in the current context -- flush - the hash table */ - while ( v != NULL ) - { - for ( i = v->vm_start ; i <= v->vm_end; i += PAGE_SIZE) - { - MMU_invalidate_page(v->vm_mm,i); - } - v = v->vm_next; - } - _tlbia(); -} - -/* flush all tlb/hash table entries except for kernels - - although the kernel is mapped with the bats, it's dynamic areas - obtained via kmalloc are mapped by the seg regs - -- Cort - */ -inline void +/* + * Flush all tlb/hash table entries except for the kernel's. + * We use the fact that only kernel mappings use VSIDs 0 - 15. + */ +void flush_tlb_all(void) { - PTE *ptep; - - /* flush hash table */ - for ( ptep = Hash ; ptep < (PTE *)((unsigned long)Hash+Hash_size) ; ptep++ ) - { - /* if not kernel vsids 0-7 (vsid greater than that for process 0)*/ - if ( (ptep->vsid > 7 ) && (ptep->v)) - { - ptep->v = 0; - } - } + struct task_struct *tsk; - _tlbia(); + read_lock(&tasklist_lock); + for_each_task(tsk) { + if (tsk->mm) + tsk->mm->context = NO_CONTEXT; + } + read_unlock(&tasklist_lock); + get_mmu_context(current); + set_context(current->mm->context); } -inline void + +/* + * Flush all the (user) entries for the address space described + * by mm. We can't rely on mm->mmap describing all the entries + * that might be in the hash table. + */ +void flush_tlb_mm(struct mm_struct *mm) { - PTE *ptep; - int context = mm->context; - struct vm_area_struct *v; - unsigned int i; - - v = mm->mmap; - while ( v != NULL ) - { - for ( i = v->vm_start ; i <= v->vm_end; i += PAGE_SIZE) - { - MMU_invalidate_page(v->vm_mm,i); - } - v = v->vm_next; - } - - _tlbia(); + mm->context = NO_CONTEXT; + if (mm == current->mm) { + get_mmu_context(current); + /* done by get_mmu_context() now -- Cort */ + /*set_context(current->mm->context);*/ + } } -inline void -flush_tlb_page(struct vm_area_struct *vma, long vmaddr) +void +flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) { - MMU_invalidate_page(vma->vm_mm,vmaddr); + unsigned vsid; + + if ( vmaddr < TASK_SIZE) { + /*vsid = (vma->vm_mm->context << 4) | (vmaddr >> 28);*/ + flush_hash_page(vma->vm_mm->context/*vsid*/, vmaddr); + /* this is needed on prep at the moment -- don't know why + -- Cort*/ + MMU_invalidate_page(vma->vm_mm,vmaddr); + } + else + { + /*printk("flush_tlb_page() vmaddr > TASK_SIZE %08x\n", vmaddr);*/ + } } -/* for each page addr in the range, call mmu_invalidat_page() +/* for each page addr in the range, call MMU_invalidate_page() if the range is very large and the hash table is small it might be faster to do a search of the hash table and just invalidate pages that are in the range but that's for study later. -- Cort */ -inline void -flush_tlb_range(struct mm_struct *mm, long start, long end) +void +flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - long i; - for ( i = PAGE_ALIGN(start-PAGE_SIZE) ; i < PAGE_ALIGN(end) ; i += PAGE_SIZE) - { - MMU_invalidate_page(mm,i); - } + start &= PAGE_MASK; + for (; start < end && start < TASK_SIZE; start += PAGE_SIZE) + { + /*flush_hash_page(VSID_FROM_CONTEXT( start>>28, mm->context), + start );*/ + flush_hash_page(mm->context, start); + /* this is needed on prep at the moment -- don't know why + -- Cort*/ + MMU_invalidate_page(mm,start); + } +} + +/* + * The context counter has overflowed. + * We set mm->context to NO_CONTEXT for all mm's in the system. + * We assume we can get to all mm's by looking as tsk->mm for + * all tasks in the system. + */ +void +mmu_context_overflow(void) +{ + struct task_struct *tsk; + int nr; + + printk(KERN_INFO "mmu_context_overflow\n"); + for (nr = 0; nr < NR_TASKS; ++nr) { + tsk = task[nr]; + if (tsk && tsk->mm) + tsk->mm->context = NO_CONTEXT; + } + flush_hash_segments(0x10, 0xffffff); + _tlbia(); + next_mmu_context = 0; +} + +#ifdef CONFIG_PREP +/* + * it's not a simple matter to get rid of these and switch to the + * ones paul is using. it will take some time and thought -- Cort + */ +inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va) +{ + int hash, page_index, segment, i, h, _h, api, vsid, perms; + PTE *_pte, *slot; + int flags = 0; + page_index = ((int)va & 0x0FFFF000) >> 12; + segment = (unsigned int)va >> 28; + api = page_index >> 10; + vsid = VSID_FROM_CONTEXT(segment,mm->context); + for (_h = 0; _h < 2; _h++) + { + hash = page_index ^ vsid; + if (_h) + { + hash = ~hash; /* Secondary hash uses ones-complement */ + } + hash &= 0x3FF | (Hash_mask /*<< 10*/); + hash *= 8; /* Eight entries / hash bucket */ + _pte = &Hash[hash]; + for (i = 0; i < 8; i++, _pte++) + { + if (_pte->v && _pte->vsid == vsid && _pte->h == _h && _pte->api == api) + { /* Found it! */ + _tlbie(va); /* Clear TLB */ + if (_pte->r) flags |= _PAGE_ACCESSED; + if (_pte->c) flags |= _PAGE_DIRTY; + _pte->v = 0; + return /*(flags)*/; + } + } + } + _tlbie(va); + return /*(flags)*/; } +#endif -inline void -flush_page_to_ram(unsigned long page) +#include +void print_mm_info(void) { + struct _SEGREG s; + long a; + struct _BATU bu; + struct _BATL bl; + unsigned long i; + + for ( i = 0x70000000 ; i <= 0x90000000 ; i+= 0x10000000 ) + { + a = get_SR(i); + memcpy(&s,&a,4); + printk("sr %2d t:%1d ks:%d kp:%d n:%d vsid:%x %x\n", + i>>28, s.t, s.ks, s.kp, s.n, s.vsid, a); + } + + asm("mfspr %0,532; mfspr %1, 533\n" : "=r" (bu), "=r" (bl)); + printk("bat2 bepi: %0x vs: %1x vp: %1x wimg: %x%x%x%x pp: %1x\n", + bu.bepi<<17, bu.vs, bu.vp, bl.w, bl.i, bl.m, bl.g, bl.pp); } diff -u --recursive --new-file v2.1.47/linux/arch/ppc/prep_defconfig linux/arch/ppc/prep_defconfig --- v2.1.47/linux/arch/ppc/prep_defconfig Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/prep_defconfig Thu Jul 31 13:09:17 1997 @@ -0,0 +1,239 @@ +# +# Automatically generated by make menuconfig: don't edit +# +CONFIG_NATIVE=y +# CONFIG_PMAC is not set +CONFIG_PREP=y +# CONFIG_HEARTBEAT is not set +# CONFIG_POWERSAVING is not set +CONFIG_MCOMMON=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +# CONFIG_MODULES is not set +CONFIG_PCI=y +CONFIG_PCI_OPTIMIZE=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BINFMT_JAVA is not set +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_EZ is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +# CONFIG_CHR_DEV_SG is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +CONFIG_SCSI_NCR53C7xx=y +# CONFIG_SCSI_NCR53C7xx_sync is not set +# CONFIG_SCSI_NCR53C7xx_FAST is not set +# CONFIG_SCSI_NCR53C7xx_DISCONNECT is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set +# CONFIG_SCSI_QLOGIC_PMAC is not set + +# +# Network device support +# + +# +# Networking options +# +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_INET_PCTCP is not set +# CONFIG_INET_RARP is not set +CONFIG_PATH_MTU_DISCOVERY=y +# CONFIG_IP_NOSR is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_EL1 is not set +# CONFIG_EL2 is not set +# CONFIG_ELPLUS is not set +# CONFIG_EL16 is not set +CONFIG_EL3=y +# CONFIG_VORTEX is not set +CONFIG_LANCE=y +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +CONFIG_PCNET32=y +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_DE4X5=y +# CONFIG_DEC_ELCP is not set +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_ES3210 is not set +# CONFIG_ZNET is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=y +# CONFIG_NET_RADIO is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +CONFIG_BEXT2_FS=y +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_VFAT_FS is not set +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +# CONFIG_RNFS_BOOTP is not set +# CONFIG_RNFS_RARP is not set +CONFIG_NFSD=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_MAC_PARTITION=y +CONFIG_HFS_FS=y + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_SERIAL_EXTENDED=y +# CONFIG_SERIAL_MANY_PORTS is not set +# CONFIG_SERIAL_SHARE_IRQ is not set +# CONFIG_SERIAL_MULTIPORT is not set +# CONFIG_HUB6 is not set +# CONFIG_SERIAL_CONSOLE is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_PRINTER is not set +CONFIG_MOUSE=y +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_FTAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff -u --recursive --new-file v2.1.47/linux/arch/sparc/defconfig linux/arch/sparc/defconfig --- v2.1.47/linux/arch/sparc/defconfig Thu Jul 17 10:06:03 1997 +++ linux/arch/sparc/defconfig Thu Jul 31 13:09:17 1997 @@ -42,7 +42,7 @@ SUN_FB_BWTWO=y SUN_FB_LEO=y TADPOLE_FB_WEITEK=y -#SUN_FB_CREATOR is not set +SUN_FB_CREATOR=y # # Misc Linux/SPARC drivers @@ -180,15 +180,12 @@ # Filesystems # CONFIG_QUOTA=y -# CONFIG_DCACHE_PRELOAD is not set -# CONFIG_OMIRR is not set -# CONFIG_TRANS_NAMES is not set CONFIG_MINIX_FS=m CONFIG_EXT2_FS=y CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m CONFIG_VFAT_FS=m -CONFIG_UMSDOS_FS=m +# CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y diff -u --recursive --new-file v2.1.47/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c --- v2.1.47/linux/arch/sparc/kernel/sys_sunos.c Thu Jul 17 10:06:03 1997 +++ linux/arch/sparc/kernel/sys_sunos.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.80 1997/07/17 02:20:22 davem Exp $ +/* $Id: sys_sunos.c,v 1.81 1997/07/20 05:59:31 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -842,8 +842,9 @@ linux_nfs_mount.acdirmin = sunos_mount->acdirmin; linux_nfs_mount.acdirmax = sunos_mount->acdirmax; - if (getname (sunos_mount->hostname, &the_name)) - return -EFAULT; + the_name = getname(sunos_mount->hostname); + if(IS_ERR(the_name)) + return PTR_ERR(the_name); strncpy (linux_nfs_mount.hostname, the_name, 254); linux_nfs_mount.hostname [255] = 0; diff -u --recursive --new-file v2.1.47/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c --- v2.1.47/linux/arch/sparc/mm/srmmu.c Mon Jul 7 08:18:54 1997 +++ linux/arch/sparc/mm/srmmu.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.148 1997/06/24 15:48:02 jj Exp $ +/* $Id: srmmu.c,v 1.149 1997/07/20 05:59:34 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -2214,7 +2214,8 @@ { if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) { struct vm_area_struct *vmaring; - struct inode *inode; + struct dentry *dentry; + struct inode *inode = NULL; unsigned long flags, offset, vaddr, start; int alias_found = 0; pgd_t *pgdp; @@ -2223,7 +2224,9 @@ save_and_cli(flags); - inode = vma->vm_inode; + dentry = vma->vm_dentry; + if(dentry) + inode = dentry->d_inode; if (!inode) goto done; offset = (address & PAGE_MASK) - vma->vm_start; diff -u --recursive --new-file v2.1.47/linux/arch/sparc/mm/sun4c.c linux/arch/sparc/mm/sun4c.c --- v2.1.47/linux/arch/sparc/mm/sun4c.c Sat May 24 09:10:23 1997 +++ linux/arch/sparc/mm/sun4c.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: sun4c.c,v 1.148 1997/05/18 21:11:19 davem Exp $ +/* $Id: sun4c.c,v 1.149 1997/07/20 05:59:38 davem Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -2435,11 +2435,14 @@ */ static void sun4c_vac_alias_fixup(struct vm_area_struct *vma, unsigned long address, pte_t pte) { - struct inode *inode; + struct dentry *dentry; + struct inode *inode = NULL; pgd_t *pgdp; pte_t *ptep; - inode = vma->vm_inode; + dentry = vma->vm_dentry; + if(dentry) + inode = dentry->d_inode; if(inode) { unsigned long offset = (address & PAGE_MASK) - vma->vm_start; struct vm_area_struct *vmaring = inode->i_mmap; diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig --- v2.1.47/linux/arch/sparc64/defconfig Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/defconfig Thu Jul 31 13:09:17 1997 @@ -160,14 +160,11 @@ # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_DCACHE_PRELOAD is not set -# CONFIG_OMIRR is not set -# CONFIG_TRANS_NAMES is not set CONFIG_MINIX_FS=m CONFIG_EXT2_FS=y CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m -CONFIG_VFAT_FS=m +# CONFIG_VFAT_FS is not set # CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile --- v2.1.47/linux/arch/sparc64/kernel/Makefile Mon Jul 7 08:18:54 1997 +++ linux/arch/sparc64/kernel/Makefile Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.28 1997/07/05 09:52:20 davem Exp $ +# $Id: Makefile,v 1.30 1997/07/24 14:48:04 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -7,12 +7,24 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... +ifdef SMP + +.S.s: + $(CPP) -D__ASSEMBLY__ $(AFLAGS) -ansi $< -o $*.s + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $< -o $*.o + +else + .S.s: $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s .S.o: $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o +endif + all: kernel.o head.o init_task.o O_TARGET := kernel.o @@ -22,6 +34,10 @@ unaligned.o sys_sunos32.o sunos_ioctl32.o OX_OBJS := sparc64_ksyms.o +ifdef SMP +O_OBJS += smp.o trampoline.o +endif + ifdef CONFIG_SPARC32_COMPAT O_OBJS += sys32.o sys_sparc32.o signal32.o ioctl32.o endif @@ -36,7 +52,7 @@ head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S etrap.S rtrap.S \ winfixup.S entry.S - $(CC) -D__ASSEMBLY__ -ansi -c $*.S -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $*.S -o $*.o # # This is just to get the dependencies... diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/auxio.c linux/arch/sparc64/kernel/auxio.c --- v2.1.47/linux/arch/sparc64/kernel/auxio.c Thu Mar 27 14:40:00 1997 +++ linux/arch/sparc64/kernel/auxio.c Thu Jul 31 13:09:17 1997 @@ -4,6 +4,9 @@ */ #include +#include +#include +#include #include #include #include diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/cpu.c linux/arch/sparc64/kernel/cpu.c --- v2.1.47/linux/arch/sparc64/kernel/cpu.c Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/kernel/cpu.c Thu Jul 31 13:09:17 1997 @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include #include @@ -56,12 +58,7 @@ long ver, fpu_vers; long fprs; -#ifndef __SMP__ - cpuid = 0; -#else -#error SMP not supported on sparc64 yet - /* cpuid = get_cpuid(); */ -#endif + cpuid = smp_processor_id(); fprs = fprs_read (); fprs_write (FPRS_FEF); diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/devices.c linux/arch/sparc64/kernel/devices.c --- v2.1.47/linux/arch/sparc64/kernel/devices.c Mon Jul 7 08:18:54 1997 +++ linux/arch/sparc64/kernel/devices.c Thu Jul 31 13:09:17 1997 @@ -13,7 +13,7 @@ #include #include -struct prom_cpuinfo linux_cpus[NCPUS]; +struct prom_cpuinfo linux_cpus[NR_CPUS]; int linux_num_cpus = 0; extern void cpu_probe(void); diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/entry.S linux/arch/sparc64/kernel/entry.S --- v2.1.47/linux/arch/sparc64/kernel/entry.S Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/kernel/entry.S Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.50 1997/07/15 16:53:00 davem Exp $ +/* $Id: entry.S,v 1.51 1997/07/24 12:15:04 davem Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -206,7 +206,7 @@ * flushing very quickly. */ .align 32 - .globl do_ivec + .globl do_ivec, do_ivec_return do_ivec: ldxa [%g0] ASI_INTR_RECEIVE, %g1 andcc %g1, 0x20, %g0 diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c --- v2.1.47/linux/arch/sparc64/kernel/ioctl32.c Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/kernel/ioctl32.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.13 1997/07/17 02:20:38 davem Exp $ +/* $Id: ioctl32.c,v 1.14 1997/07/17 06:21:12 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -30,6 +30,7 @@ #include #include #include +#include /* As gcc will warn about casting u32 to some ptr, we have to cast it to * unsigned long first, and that's what is A() for. @@ -696,6 +697,22 @@ /* Little p (/dev/rtc etc.) */ case RTCGET: case RTCSET: + + /* OPENPROMIO, SunOS/Solaris only, the NetBSD one's have + * embedded pointers in the arg which we'd need to clean up... + */ + case OPROMGETOPT: + case OPROMSETOPT: + case OPROMNXTOPT: + case OPROMSETOPT2: + case OPROMNEXT: + case OPROMCHILD: + case OPROMGETPROP: + case OPROMNXTPROP: + case OPROMU2P: + case OPROMGETCONS: + case OPROMGETFBNAME: + case OPROMGETBOOTARGS: /* Socket level stuff */ case FIOSETOWN: diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/ioport.c linux/arch/sparc64/kernel/ioport.c --- v2.1.47/linux/arch/sparc64/kernel/ioport.c Mon Jul 7 08:18:54 1997 +++ linux/arch/sparc64/kernel/ioport.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: ioport.c,v 1.10 1997/06/30 09:24:02 jj Exp $ +/* $Id: ioport.c,v 1.11 1997/07/22 06:14:04 davem Exp $ * ioport.c: Simple io mapping allocator. * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -83,7 +83,7 @@ unsigned long vaddr = (unsigned long) virtual & PAGE_MASK; unsigned long plen = (((unsigned long)virtual & ~PAGE_MASK) + len + PAGE_SIZE-1) & PAGE_MASK; - if (virtual >= PAGE_OFFSET + 0x10000000000UL) + if (((unsigned long)virtual) >= PAGE_OFFSET + 0x10000000000UL) return; release_region(vaddr, plen); diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/irq.c linux/arch/sparc64/kernel/irq.c --- v2.1.47/linux/arch/sparc64/kernel/irq.c Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/kernel/irq.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.16 1997/07/11 03:03:08 davem Exp $ +/* $Id: irq.c,v 1.19 1997/07/24 12:15:04 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,7 @@ } /* INO number to Sparc PIL level. */ -static unsigned char ino_to_pil[] = { +unsigned char ino_to_pil[] = { 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 0 */ 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 1 */ 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 2 */ @@ -391,7 +392,116 @@ #define irq_exit(cpu, irq) (local_irq_count[cpu]--) #else -#error SMP not supported on sparc64 just yet + +atomic_t __sparc64_bh_counter = ATOMIC_INIT(0); + +/* Who has global_irq_lock. */ +unsigned char global_irq_holder = NO_PROC_ID; + +/* This protects IRQ's. */ +spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED; + +/* This protects BH software state (masks, things like that). */ +spinlock_t global_bh_lock = SPIN_LOCK_UNLOCKED; + +/* Global IRQ locking depth. */ +atomic_t global_irq_count = ATOMIC_INIT(0); + +static inline void wait_on_irq(int cpu) +{ + int local_count = local_irq_count[cpu]; + + while(local_count != atomic_read(&global_irq_count)) { + atomic_sub(local_count, &global_irq_count); + spin_unlock(&global_irq_lock); + for(;;) { + if (atomic_read(&global_irq_count)) + continue; + if (*((unsigned char *)&global_irq_lock)) + continue; + if (spin_trylock(&global_irq_lock)) + break; + } + atomic_add(local_count, &global_irq_count); + } +} + +static inline void get_irqlock(int cpu) +{ + if (!spin_trylock(&global_irq_lock)) { + if ((unsigned char) cpu == global_irq_holder) + return; + do { + barrier(); + } while (!spin_trylock(&global_irq_lock)); + } + wait_on_irq(cpu); + global_irq_holder = cpu; +} + +void __global_cli(void) +{ + int cpu = smp_processor_id(); + + __cli(); + get_irqlock(cpu); +} + +void __global_sti(void) +{ + release_irqlock(smp_processor_id()); + __sti(); +} + +unsigned long __global_save_flags(void) +{ + return global_irq_holder == (unsigned char) smp_processor_id(); +} + +void __global_restore_flags(unsigned long flags) +{ + if (flags & 1) { + __global_cli(); + } else { + if (global_irq_holder == (unsigned char) smp_processor_id()) { + global_irq_holder = NO_PROC_ID; + spin_unlock(&global_irq_lock); + } + if (!(flags & 2)) + __sti(); + } +} + +void irq_enter(int cpu, int irq) +{ + hardirq_enter(cpu); + barrier(); + while (*((unsigned char *)&global_irq_lock)) { + if ((unsigned char) cpu == global_irq_holder) + printk("irq_enter: Frosted Lucky Charms, " + "they're magically delicious!\n"); + barrier(); + } +} + +void irq_exit(int cpu, int irq) +{ + hardirq_exit(cpu); + release_irqlock(cpu); +} + +void synchronize_irq(void) +{ + int cpu = smp_processor_id(); + int local_count = local_irq_count[cpu]; + unsigned long flags; + + if (local_count != atomic_read(&global_irq_count)) { + save_and_cli(flags); + restore_flags(flags); + } +} + #endif /* __SMP__ */ void report_spurious_ivec(struct pt_regs *regs) @@ -432,9 +542,7 @@ struct irqaction *action; int cpu = smp_processor_id(); - /* XXX */ - if(irq != 14) - clear_softint(1 << irq); + clear_softint(1 << irq); irq_enter(cpu, irq); action = *(irq + irq_action); @@ -575,53 +683,46 @@ return 0; } -/* XXX This is a hack, make it per-cpu so that SMP port will work correctly - * XXX with mixed MHZ Ultras in the machine. -DaveM - */ -static unsigned long cpu_cfreq; -static unsigned long tick_offset; +struct sun5_timer *linux_timers = NULL; -/* XXX This doesn't belong here, just do this cruft in the timer.c handler code. */ -static void timer_handler(int irq, void *dev_id, struct pt_regs *regs) +/* This is called from sbus_init() to get the jiffies timer going. + * We need to call this after there exists a valid SBus_chain so + * that the IMAP/ICLR registers can be accessed. + * + * XXX That is because the whole startup sequence is broken. I will + * XXX fix it all up very soon. -DaveM + */ +void init_timers(void (*cfunc)(int, void *, struct pt_regs *)) { - if (!(get_softint () & 1)) { - /* Just to be sure... */ - clear_softint(1 << 14); - printk("Spurious level14 at %016lx\n", regs->tpc); - return; - } else { - unsigned long compare, tick; - - do { - extern void timer_interrupt(int, void *, struct pt_regs *); - - timer_interrupt(irq, dev_id, regs); - - /* Acknowledge INT_TIMER */ - clear_softint(1 << 0); + struct linux_prom64_registers pregs[3]; + u32 pirqs[2]; + int node, err; - /* Set up for next timer tick. */ - __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" - "add %0, %2, %0\n\t" - "wr %0, 0x0, %%tick_cmpr\n\t" - "rd %%tick, %1" - : "=&r" (compare), "=r" (tick) - : "r" (tick_offset)); - } while(tick >= compare); + node = prom_finddevice("/counter-timer"); + if(node == 0) { + prom_printf("init_timers: Cannot find counter-timer PROM node.\n"); + prom_halt(); } -} + err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs)); + if(err == -1) { + prom_printf("init_timers: Cannot obtain 'reg' for counter-timer.\n"); + prom_halt(); + } + err = prom_getproperty(node, "interrupts", (char *)&pirqs[0], sizeof(pirqs)); + if(err == -1) { + prom_printf("init_timers: Cannot obtain 'interrupts' " + "for counter-timer.\n"); + prom_halt(); + } + linux_timers = (struct sun5_timer *) __va(pregs[0].phys_addr); -/* This is called from time_init() to get the jiffies timer going. */ -void init_timers(void (*cfunc)(int, void *, struct pt_regs *)) -{ - int node, err; + /* Shut it up first. */ + linux_timers->limit0 = 0; + + /* Register IRQ handler. */ + err = request_irq(pirqs[0] & 0x3f, /* XXX Fix this for big Enterprise XXX */ + cfunc, (SA_INTERRUPT | SA_STATIC_ALLOC), "timer", NULL); - /* XXX FIX this for SMP -JJ */ - node = linux_cpus [0].prom_node; - cpu_cfreq = prom_getint(node, "clock-frequency"); - tick_offset = cpu_cfreq / HZ; - err = request_irq(14, timer_handler, (SA_INTERRUPT|SA_STATIC_ALLOC), - "timer", NULL); if(err) { prom_printf("Serious problem, cannot register timer interrupt\n"); prom_halt(); @@ -630,27 +731,33 @@ save_and_cli(flags); - __asm__ __volatile__("wr %0, 0x0, %%tick_cmpr\n\t" - "wrpr %%g0, 0x0, %%tick" - : /* No outputs */ - : "r" (tick_offset)); - - clear_softint (get_softint ()); + /* Set things up so user can access tick register for profiling + * purposes. + */ + __asm__ __volatile__(" + sethi %%hi(0x80000000), %%g1 + sllx %%g1, 32, %%g1 + rd %%tick, %%g2 + add %%g2, 6, %%g2 + andn %%g2, %%g1, %%g2 + wrpr %%g2, 0, %%tick +" : /* no outputs */ + : /* no inputs */ + : "g1", "g2"); + + linux_timers->limit0 = + (SUN5_LIMIT_ENABLE | SUN5_LIMIT_ZRESTART | SUN5_LIMIT_TOZERO | + (SUN5_HZ_TO_LIMIT(HZ) & SUN5_LIMIT_CMASK)); restore_flags(flags); } + sti(); } -/* We use this nowhere else, so only define it's layout here. */ -struct sun5_timer { - volatile u32 count0, _unused0; - volatile u32 limit0, _unused1; - volatile u32 count1, _unused2; - volatile u32 limit1, _unused3; -} *prom_timers; +struct sun5_timer *prom_timers; -static u32 prom_limit0, prom_limit1; +static u64 prom_limit0, prom_limit1; static void map_prom_timers(void) { diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c --- v2.1.47/linux/arch/sparc64/kernel/process.c Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/kernel/process.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.29 1997/07/17 02:20:40 davem Exp $ +/* $Id: process.c,v 1.31 1997/07/24 12:15:05 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -50,9 +50,12 @@ return -EPERM; /* endless idle loop with no priority at all */ + current->priority = -100; current->counter = -100; - for (;;) + for (;;) { + run_task_queue(&tq_scheduler); schedule(); + } return 0; } @@ -61,42 +64,27 @@ /* * the idle loop on a UltraMultiPenguin... */ -asmlinkage int sys_idle(void) +asmlinkage int cpu_idle(void) { - if (current->pid != 0) - return -EPERM; - - /* endless idle loop with no priority at all */ - current->counter = -100; - schedule(); - return 0; + current->priority = -100; + while(1) { + if(tq_scheduler) { + lock_kernel(); + run_task_queue(&tq_scheduler); + unlock_kernel(); + } + current->counter = -100; + schedule(); + } } -/* This is being executed in task 0 'user space'. */ -int cpu_idle(void *unused) +asmlinkage int sys_idle(void) { - volatile int *spap = &smp_process_available; - volatile int cval; + if(current->pid != 0) + return -EPERM; - while(1) { - if(0==*spap) - continue; - cli(); - /* Acquire exclusive access. */ - while((cval = smp_swap(spap, -1)) == -1) - while(*spap == -1) - ; - if (0==cval) { - /* ho hum, release it. */ - *spap = 0; - sti(); - continue; - } - /* Something interesting happened, whee... */ - *spap = (cval - 1); - sti(); - idle(); - } + cpu_idle(); + return 0; } #endif @@ -467,7 +455,11 @@ * allocate the task_struct and kernel stack in * do_fork(). */ +#ifdef __SMP__ +extern void ret_from_smpfork(void); +#else extern void ret_from_syscall(void); +#endif int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, struct task_struct *p, struct pt_regs *regs) @@ -485,7 +477,11 @@ child_trap_frame = ((char *)p) + stack_offset; memcpy(child_trap_frame, (((struct reg_window *)regs)-1), tframe_size); p->tss.ksp = ((unsigned long) child_trap_frame) - STACK_BIAS; +#ifdef __SMP__ + p->tss.kpc = ((unsigned long) ret_from_smpfork) - 0x8; +#else p->tss.kpc = ((unsigned long) ret_from_syscall) - 0x8; +#endif p->tss.kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct reg_window)); p->tss.cwp = regs->u_regs[UREG_G0]; if(regs->tstate & TSTATE_PRIV) { @@ -495,6 +491,10 @@ p->tss.ctx = 0; p->tss.kregs->u_regs[UREG_G6] = (unsigned long) p; } else { + if(current->tss.flags & SPARC_FLAG_32BIT) { + sp &= 0x00000000ffffffff; + regs->u_regs[UREG_FP] &= 0x00000000ffffffff; + } p->tss.kregs->u_regs[UREG_FP] = sp; p->tss.flags &= ~SPARC_FLAG_KTHREAD; p->tss.current_ds = USER_DS; diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c --- v2.1.47/linux/arch/sparc64/kernel/setup.c Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/kernel/setup.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.10 1997/07/08 11:07:47 jj Exp $ +/* $Id: setup.c,v 1.11 1997/07/24 12:15:05 davem Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -405,11 +405,7 @@ int get_cpuinfo(char *buffer) { -#ifndef __SMP__ - int cpuid=0; -#else -#error SMP not supported on sparc64 yet -#endif + int cpuid=smp_processor_id(); return sprintf(buffer, "cpu\t\t: %s\n" "fpu\t\t: %s\n" diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c --- v2.1.47/linux/arch/sparc64/kernel/smp.c Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/kernel/smp.c Thu Jul 31 13:09:17 1997 @@ -3,6 +3,26 @@ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + #define __KERNEL_SYSCALLS__ #include @@ -13,8 +33,9 @@ unsigned long cpu_present_map = 0; int smp_num_cpus = 1; int smp_threads_ready = 0; +volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; -struct cpuinfo_sparc64 cpu_data[NR_CPUS]; +struct cpuinfo_sparc cpu_data[NR_CPUS]; static unsigned char boot_cpu_id = 0; static int smp_activated = 0; @@ -52,11 +73,11 @@ void smp_commence(void) { - local_flush_cache_all(); - local_flush_tlb_all(); + flush_cache_all(); + flush_tlb_all(); smp_commenced = 1; - local_flush_cache_all(); - local_flush_tlb_all(); + flush_cache_all(); + flush_tlb_all(); } static void smp_setup_percpu_timer(void); @@ -67,8 +88,8 @@ { int cpuid = hard_smp_processor_id(); - local_flush_cache_all(); - local_flush_tlb_all(); + flush_cache_all(); + flush_tlb_all(); smp_setup_percpu_timer(); @@ -76,7 +97,7 @@ smp_store_cpu_info(cpuid); callin_flag = 1; __asm__ __volatile__("membar #Sync\n\t" - "flush %g6" : : : "memory"); + "flush %%g6" : : : "memory"); while(!task[cpuid]) barrier(); @@ -103,11 +124,18 @@ return cpu_idle(NULL); } +void cpu_panic(void) +{ + printk("CPU[%d]: Returns from cpu_idle!\n", smp_processor_id()); + panic("SMP bolixed\n"); +} + extern struct prom_cpuinfo linux_cpus[NR_CPUS]; +extern unsigned long sparc64_cpu_startup; void smp_boot_cpus(void) { - int cpucount = 0, i, first, prev; + int cpucount = 0, i; printk("Entering UltraSMPenguin Mode...\n"); __sti(); @@ -133,15 +161,15 @@ continue; if(cpu_present_map & (1 << i)) { - extern unsigned long sparc64_cpu_startup; - unsigned long entry = (unsigned long)&sparc_cpu_startup; struct task_struct *p; int timeout; kernel_thread(start_secondary, NULL, CLONE_PID); p = task[++cpucount]; p->processor = i; - prom_startcpu(linux_cpus[i].prom_node, entry, i); + prom_startcpu(linux_cpus[i].prom_node, + ((unsigned long)&sparc64_cpu_startup), + ((unsigned long)p)); for(timeout = 0; timeout < 5000000; timeout++) { if(cpu_callin_map[i]) break; @@ -190,49 +218,46 @@ barrier(); } -/* XXX Make it fast later. */ +static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, int cpu) +{ + u64 result, target = (cpu_number_map[cpu] << 14) | 0x70; + + __asm__ __volatile__(" + wrpr %0, %1, %%pstate + wr %%g0, %2, %%asi + stxa %3, [0x40] %%asi + stxa %4, [0x50] %%asi + stxa %5, [0x60] %%asi + stxa %%g0, [%6] %%asi + membar #Sync" + : /* No outputs */ + : "r" (pstate), "i" (PSTATE_IE), "i" (ASI_UDB_INTR_W), + "r" (data0), "r" (data1), "r" (data2), "r" (target)); + + /* NOTE: PSTATE_IE is still clear. */ + do { + __asm__ __volatile__("ldxa [%%g0] %1, %0" + : "=r" (result) + : "i" (ASI_INTR_DISPATCH_STAT)); + } while(result & 0x1); + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" + : : "r" (pstate)); + if(result & 0x2) + panic("Penguin NACK's master!"); +} + void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2) { if(smp_processors_ready) { - unsigned long mask; - u64 data0 = (((unsigned long)ctx)<<32 | - (((unsigned long)func) & 0xffffffff)); - u64 pstate; + unsigned long mask = (cpu_present_map & ~(1<context & 0x1fff; - if(mm->cpu_vm_mask != (1 << smp_processor_id())) + if(mm->cpu_vm_mask != (1UL << smp_processor_id())) smp_cross_call(&xcall_flush_tlb_mm, ctx, 0, 0); __flush_tlb_mm(ctx); } @@ -265,7 +292,7 @@ unsigned long end) { u32 ctx = mm->context & 0x1fff; - if(mm->cpu_vm_mask != (1 << smp_processor_id())) + if(mm->cpu_vm_mask != (1UL << smp_processor_id())) smp_cross_call(&xcall_flush_tlb_range, ctx, start, end); __flush_tlb_range(ctx, start, end); } @@ -275,7 +302,7 @@ struct mm_struct *mm = vma->vm_mm; u32 ctx = mm->context & 0x1fff; - if(mm->cpu_vm_mask != (1 << smp_processor_id())) + if(mm->cpu_vm_mask != (1UL << smp_processor_id())) smp_cross_call(&xcall_flush_tlb_page, ctx, page, 0); __flush_tlb_page(ctx, page); } @@ -308,12 +335,12 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) { int cpu = smp_processor_id(); + int user = user_mode(regs); - clear_profile_irq(cpu); - if(!user_mode(regs)) - sparc_do_profile(regs->pc); + /* XXX clear_profile_irq(cpu); */ + if(!user) + sparc64_do_profile(regs->tpc); if(!--prof_counter[cpu]) { - int user = user_mode(regs); if(current->pid) { update_one_process(current, 1, user, !user); if(--current->counter < 0) { @@ -344,4 +371,5 @@ int setup_profiling_timer(unsigned int multiplier) { /* XXX implement me */ + return 0; } diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/time.c linux/arch/sparc64/kernel/time.c --- v2.1.47/linux/arch/sparc64/kernel/time.c Mon Jul 7 08:18:54 1997 +++ linux/arch/sparc64/kernel/time.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.3 1997/06/17 13:25:29 jj Exp $ +/* $Id: time.c,v 1.5 1997/07/23 11:32:06 davem Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -35,11 +36,17 @@ * NOTE: On SUN5 systems the ticker interrupt comes in using 2 * interrupts, one at level14 and one with softint bit 0. */ -void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +extern struct sun5_timer *linux_timers; + +static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) { /* last time the cmos clock got updated */ static long last_rtc_update=0; + __asm__ __volatile__("ldx [%0], %%g0" + : /* no outputs */ + : "r" (&((linux_timers)->limit0))); + do_timer(regs); /* Determine when to update the Mostek clock. */ @@ -239,14 +246,12 @@ __initfunc(void time_init(void)) { - extern void init_timers(void (*func)(int, void *, struct pt_regs *)); unsigned int year, mon, day, hour, min, sec; struct mostek48t02 *mregs; do_get_fast_time = do_gettimeofday; clock_probe(); - init_timers(timer_interrupt); mregs = mstk48t02_regs; if(!mregs) { @@ -266,6 +271,13 @@ mregs->creg &= ~MSTK_CREG_READ; } +extern void init_timers(void (*func)(int, void *, struct pt_regs *)); + +__initfunc(void sun4u_start_timers(void)) +{ + init_timers(timer_interrupt); +} + static __inline__ unsigned long do_gettimeoffset(void) { unsigned long offset = 0; @@ -286,16 +298,39 @@ void do_gettimeofday(struct timeval *tv) { - unsigned long flags; - - save_and_cli(flags); - *tv = xtime; - tv->tv_usec += do_gettimeoffset(); - if(tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; - } - restore_flags(flags); + /* Load doubles must be used on xtime so that what we get + * is guarenteed to be atomic, this is why we can run this + * with interrupts on full blast. Don't touch this... -DaveM + */ + __asm__ __volatile__(" + sethi %hi(linux_timers), %o1 + sethi %hi(xtime), %g2 + ldx [%o1 + %lo(linux_timers)], %g3 +1: ldd [%g2 + %lo(xtime)], %o4 + ldx [%g3], %o1 + ldd [%g2 + %lo(xtime)], %o2 + xor %o4, %o2, %o2 + xor %o5, %o3, %o3 + orcc %o2, %o3, %g0 + bne,pn %icc, 1b + cmp %o1, 0 + bge,pt %icc, 1f + sethi %hi(tick), %o3 + ld [%o3 + %lo(tick)], %o3 + sethi %hi(0x1fffff), %o2 + or %o2, %lo(0x1fffff), %o2 + add %o5, %o3, %o5 + and %o1, %o2, %o1 +1: add %o5, %o1, %o5 + sethi %hi(1000000), %o2 + or %o2, %lo(1000000), %o2 + cmp %o5, %o2 + bl,a,pn %icc, 1f + st %o4, [%o0 + 0x0] + add %o4, 0x1, %o4 + sub %o5, %o2, %o5 + st %o4, [%o0 + 0x0] +1: st %o5, [%o0 + 0x4]"); } void do_settimeofday(struct timeval *tv) diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/kernel/trampoline.S linux/arch/sparc64/kernel/trampoline.S --- v2.1.47/linux/arch/sparc64/kernel/trampoline.S Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc64/kernel/trampoline.S Thu Jul 31 13:09:17 1997 @@ -0,0 +1,197 @@ +/* $Id: trampoline.S,v 1.1 1997/07/24 14:47:53 davem Exp $ + * trampoline.S: Jump start slave processors on sparc64. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + .text + .globl sparc64_cpu_startup +sparc64_cpu_startup: + flushw + mov (LSU_CONTROL_IC | LSU_CONTROL_DC | LSU_CONTROL_IM | LSU_CONTROL_DM), %g1 + stxa %g1, [%g0] ASI_LSU_CONTROL + wrpr %g0, (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE), %pstate + wrpr %g0, 15, %pil + + mov %o0, %g6 + + sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5 + sllx %g5, 32, %g5 + or %g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W | _PAGE_G), %g5 + + sethi %uhi(_PAGE_PADDR), %g3 + or %g3, %ulo(_PAGE_PADDR), %g3 + sllx %g3, 32, %g3 + sethi %hi(_PAGE_PADDR), %g7 + or %g7, %lo(_PAGE_PADDR), %g7 + or %g3, %g7, %g7 + + /* Find TLB entry we are executing out of. */ + clr %l0 + set 0x1fff, %l2 + rd %pc, %l3 + andn %l3, %l2, %g2 +1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 + cmp %g1, %g2 + be,a,pn %xcc, 2f + ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1 + cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + +2: nop + nop + nop + and %g1, %g3, %g1 + sub %g1, %g2, %g1 + or %g5, %g1, %g5 + clr %l0 + sethi %hi(KERNBASE), %g3 + sethi %hi(KERNBASE<<1), %g7 + mov TLB_TAG_ACCESS, %l7 +1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_IMMU + stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS +2: cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + nop + nop + nop + clr %l0 +1: ldxa [%l0] ASI_DTLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_DMMU + stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS +2: cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + nop + nop + nop + sethi %hi(KERNBASE), %g3 + mov (63 << 3), %g7 + stxa %g3, [%l7] ASI_DMMU + stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS + membar #Sync + stxa %g3, [%l7] ASI_IMMU + stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS + membar #Sync + flush %g6 + membar #Sync + b,pt %xcc, 1f + nop +1: set bounce, %g2 + jmpl %g2 + %g0, %g0 + nop + +bounce: + mov PRIMARY_CONTEXT, %g7 + stxa %g0, [%g7] ASI_DMMU + membar #Sync + mov SECONDARY_CONTEXT, %g7 + stxa %g0, [%g7] ASI_DMMU + membar #Sync + + sethi %uhi(PAGE_OFFSET), %g4 + sllx %g4, 32, %g4 + + mov TLB_TAG_ACCESS, %g2 + stxa %g3, [%g2] ASI_IMMU + stxa %g3, [%g2] ASI_DMMU + + mov (63 << 3), %g7 + ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1 + andn %g1, (_PAGE_G), %g1 + stxa %g1, [%g7] ASI_ITLB_DATA_ACCESS + membar #Sync + + ldxa [%g7] ASI_DTLB_DATA_ACCESS, %g1 + andn %g1, (_PAGE_G), %g1 + stxa %g1, [%g7] ASI_DTLB_DATA_ACCESS + membar #Sync + + flush %g6 + membar #Sync + + mov 1, %g5 + sllx %g5, (PAGE_SHIFT + 1), %g5 + sub %g5, (REGWIN_SZ + STACK_BIAS), %g5 + add %g6, %g5, %sp + mov 0, %fp + + wrpr %g0, 0, %wstate + wrpr %g0, 0, %tl + + /* Setup the trap globals, then we can resurface. */ + rdpr %pstate, %o1 + mov %g6, %o2 + wrpr %o1, (PSTATE_AG | PSTATE_IE), %pstate + sethi %hi(sparc64_ttable_tl0), %g5 + wrpr %g5, %tba + mov %o2, %g6 + + wrpr %o1, (PSTATE_MG | PSTATE_IE), %pstate + sethi %hi(0x1ff8), %g2 + or %g2, %lo(0x1ff8), %g2 + ldx [%o2 + AOFF_task_mm], %g6 + ldx [%g6 + AOFF_mm_pgd], %g6 + clr %g7 + + wrpr %o1, (PSTATE_IG | PSTATE_IE), %pstate + sethi %hi(ivector_to_mask), %g5 + or %g5, %lo(ivector_to_mask), %g1 + mov 0x40, %g2 + + wrpr %g0, 0, %wstate + wrpr %o1, PSTATE_IE, %pstate + + mov TSB_REG, %o4 + mov 1, %o5 + stxa %o5, [%o4] ASI_DMMU + stxa %o5, [%o4] ASI_IMMU + membar #Sync + + wrpr %g0, 0, %pil + or %o1, PSTATE_IE, %o1 + wrpr %o1, 0, %pstate + + call smp_callin + nop + call cpu_idle + mov 0, %o0 + call cpu_panic + nop +1: b,a,pt %xcc, 1b diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/lib/locks.S linux/arch/sparc64/lib/locks.S --- v2.1.47/linux/arch/sparc64/lib/locks.S Thu Mar 27 14:40:01 1997 +++ linux/arch/sparc64/lib/locks.S Thu Jul 31 13:09:17 1997 @@ -1,77 +1,77 @@ -/* $Id: locks.S,v 1.2 1997/03/10 12:28:02 jj Exp $ +/* $Id: locks.S,v 1.3 1997/07/22 05:51:42 davem Exp $ * locks.S: SMP low-level lock primitives on Sparc64. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ +#include #include +#include .text - .align 4 - - .globl __spinlock_waitfor -__spinlock_waitfor: -1: orcc %g2, 0x0, %g0 - bne 1b - ldub [%g1], %g2 - ldstub [%g1], %g2 - jmpl %o7 - 12, %g0 - mov %g5, %o7 - - .globl ___become_idt -___become_idt: -#if 0 /* Don't know how to do this on the Ultra yet... */ -#endif - jmpl %o7 + 8, %g0 - mov %g5, %o7 + .align 32 ___lk_busy_spin: - orcc %g2, 0, %g0 - bne ___lk_busy_spin - ldub [%g1 + 0], %g2 - b 1f - ldstub [%g1 + 0], %g2 + orcc %g2, 0, %g0 + bne,pt %icc, ___lk_busy_spin + ldub [%g1 + 0], %g2 + b,pt %xcc, 1f + ldstub [%g1 + 0], %g2 - .globl ___lock_kernel + .globl ___lock_kernel ___lock_kernel: - addcc %g2, -1, %g2 - rdpr %pil, %g3 - bcs,a 9f - st %g2, [%g6 + AOFF_task_lock_depth] - wrpr 15, %pil - ldstub [%g1 + 0], %g2 -1: orcc %g2, 0, %g0 - bne,a ___lk_busy_spin - ldub [%g1 + 0], %g2 - ldub [%g1 + 2], %g2 - cmp %g2, %g5 - be 2f - stb %g5, [%g1 + 1] - stb %g5, [%g1 + 2] -#ifdef __SMP__ - /* XXX Figure out how to become interrupt receiver in SMP system. */ -#endif -2: mov -1, %g2 - st %g2, [%g6 + AOFF_task_lock_depth] - wrpr %g3, %pil -9: jmpl %o7 + 0x8, %g0 - mov %g5, %o7 + addcc %g2, -1, %g2 + rdpr %pil, %g3 + bcs,a,pn %icc, 9f + st %g2, [%g6 + AOFF_task_lock_depth] + wrpr %g0, 15, %pil + ldstub [%g1 + 0], %g2 +1: brnz,a,pn %g2, ___lk_busy_spin + ldub [%g1 + 0], %g2 + lduw [%g6 + AOFF_task_processor], %g2 + membar #LoadLoad | #LoadStore + stb %g2, [%g1 + 1] +2: mov -1, %g2 + st %g2, [%g6 + AOFF_task_lock_depth] + wrpr %g3, 0, %pil +9: jmpl %o7 + 0x8, %g0 + mov %g5, %o7 + + .globl ___lock_reacquire_kernel +___lock_reacquire_kernel: + rdpr %pil, %g3 + wrpr %g0, 15, %pil + st %g2, [%g6 + AOFF_task_lock_depth] + ldstub [%g1 + 0], %g2 +1: brz,pt %g2, 3f + ldub [%g1 + 0], %g2 +2: brnz,a,pt %g2, 2b + ldub [%g1 + 0], %g2 + b,pt %xcc, 1b + ldstub [%g1 + 0], %g2 +3: lduw [%g6 + AOFF_task_processor], %g2 + membar #LoadLoad | #LoadStore + stb %g2, [%g1 + 1] + wrpr %g3, 0, %pil + jmpl %o7 + 0x8, %g0 + mov %g5, %o7 #undef NO_PROC_ID #define NO_PROC_ID 0xff - .globl ___unlock_kernel + .globl ___unlock_kernel ___unlock_kernel: - addcc %g2, 1, %g2 - rdpr %pil, %g3 - bne,a 1f - st %g2, [%g6 + AOFF_task_lock_depth] - wrpr 15, %pil - mov NO_PROC_ID, %g2 - stb %g2, [%g1 + 1] - stb %g0, [%g1 + 0] - st %g0, [%g6 + AOFF_task_lock_depth] - wrpr %g3, %pil -1: jmpl %o7 + 0x8, %g0 - mov %g5, %o7 + addcc %g2, 1, %g2 + rdpr %pil, %g3 + bne,a,pn %icc, 1f + stw %g2, [%g6 + AOFF_task_lock_depth] + wrpr 15, %pil + mov NO_PROC_ID, %g2 + stb %g2, [%g1 + 1] + membar #StoreStore | #LoadStore + stb %g0, [%g1 + 0] + stw %g0, [%g6 + AOFF_task_lock_depth] + wrpr %g3, 0, %pil +1: jmpl %o7 + 0x8, %g0 + mov %g5, %o7 diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/mm/Makefile linux/arch/sparc64/mm/Makefile --- v2.1.47/linux/arch/sparc64/mm/Makefile Mon Jul 7 08:18:55 1997 +++ linux/arch/sparc64/mm/Makefile Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.3 1997/06/27 14:53:38 jj Exp $ +# $Id: Makefile,v 1.4 1997/07/24 12:15:08 davem Exp $ # Makefile for the linux Sparc64-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also @@ -7,11 +7,23 @@ # # Note 2! The CFLAGS definition is now in the main makefile... +ifdef SMP + +.S.s: + $(CPP) -D__ASSEMBLY__ $(AFLAGS) -ansi $< -o $*.s + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $< -o $*.o + +else + .S.s: $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s .S.o: $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + +endif O_TARGET := mm.o O_OBJS := ultra.o fault.o init.o generic.o asyncd.o extable.o modutil.o diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c --- v2.1.47/linux/arch/sparc64/mm/init.c Mon Jul 7 08:18:55 1997 +++ linux/arch/sparc64/mm/init.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.39 1997/07/07 02:50:57 davem Exp $ +/* $Id: init.c,v 1.40 1997/07/24 16:48:27 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -431,8 +431,17 @@ membar("#Sync"); } +void __flush_cache_all(void) +{ + unsigned long va; + + flushw_all(); + for(va = 0; va < (PAGE_SIZE << 1); va += 32) + spitfire_put_icache_tag(va, 0x0); +} + /* If not locked, zap it. */ -void flush_tlb_all(void) +void __flush_tlb_all(void) { unsigned long flags; int i; diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/mm/ultra.S linux/arch/sparc64/mm/ultra.S --- v2.1.47/linux/arch/sparc64/mm/ultra.S Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/mm/ultra.S Thu Jul 31 13:09:17 1997 @@ -1,10 +1,11 @@ -/* $Id: ultra.S,v 1.8 1997/07/15 05:35:50 davem Exp $ +/* $Id: ultra.S,v 1.9 1997/07/24 12:15:08 davem Exp $ * ultra.S: Don't expand these all over the place... * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) */ #include +#include #include /* All callers check mm->context != NO_CONTEXT for us. */ diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/prom/console.c linux/arch/sparc64/prom/console.c --- v2.1.47/linux/arch/sparc64/prom/console.c Thu Mar 27 14:40:01 1997 +++ linux/arch/sparc64/prom/console.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: console.c,v 1.6 1997/03/18 17:59:59 jj Exp $ +/* $Id: console.c,v 1.7 1997/07/19 08:28:29 ecd Exp $ * console.c: Routines that deal with sending and receiving IO * to/from the current console device using the PROM. * @@ -88,6 +88,7 @@ if(strncmp(propb, "serial", sizeof("serial"))) return PROMDEV_I_UNK; /* FIXME: Is there any better way how to find out? */ + memset(propb, 0, sizeof(propb)); st_p = prom_finddevice ("/options"); prom_getproperty(st_p, "input-device", propb, sizeof(propb)); if (strncmp (propb, "tty", 3) || !propb[3] || propb[4]) @@ -116,6 +117,7 @@ if(strncmp("serial", propb, sizeof("serial"))) return PROMDEV_O_UNK; /* FIXME: Is there any better way how to find out? */ + memset(propb, 0, sizeof(propb)); st_p = prom_finddevice ("/options"); prom_getproperty(st_p, "output-device", propb, sizeof(propb)); if (strncmp (propb, "tty", 3) || !propb[3] || propb[4]) diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/prom/misc.c linux/arch/sparc64/prom/misc.c --- v2.1.47/linux/arch/sparc64/prom/misc.c Thu Jul 17 10:06:04 1997 +++ linux/arch/sparc64/prom/misc.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.8 1997/07/14 23:45:28 davem Exp $ +/* $Id: misc.c,v 1.9 1997/07/24 12:15:11 davem Exp $ * misc.c: Miscellaneous prom functions that don't belong * anywhere else. * @@ -135,7 +135,7 @@ } #ifdef __SMP__ -void prom_start_cpu(int cpunode, unsigned long pc, unsigned long o0) +void prom_startcpu(int cpunode, unsigned long pc, unsigned long o0) { p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, o0); } diff -u --recursive --new-file v2.1.47/linux/arch/sparc64/prom/p1275.c linux/arch/sparc64/prom/p1275.c --- v2.1.47/linux/arch/sparc64/prom/p1275.c Mon Jul 7 08:18:55 1997 +++ linux/arch/sparc64/prom/p1275.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: p1275.c,v 1.10 1997/06/27 04:18:30 davem Exp $ +/* $Id: p1275.c,v 1.11 1997/07/24 12:15:11 davem Exp $ * p1275.c: Sun IEEE 1275 PROM low level interface routines * * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include diff -u --recursive --new-file v2.1.47/linux/drivers/Makefile linux/drivers/Makefile --- v2.1.47/linux/drivers/Makefile Sun Jul 27 12:11:00 1997 +++ linux/drivers/Makefile Mon Jul 28 13:49:04 1997 @@ -9,7 +9,7 @@ SUB_DIRS := block char net misc #streams MOD_SUB_DIRS := $(SUB_DIRS) sbus -ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn misc pnp +ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn pnp ifdef CONFIG_PCI SUB_DIRS += pci diff -u --recursive --new-file v2.1.47/linux/drivers/block/acsi.c linux/drivers/block/acsi.c --- v2.1.47/linux/drivers/block/acsi.c Wed Apr 23 19:01:17 1997 +++ linux/drivers/block/acsi.c Thu Jul 31 13:09:17 1997 @@ -370,7 +370,7 @@ static int acsi_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg ); static int acsi_open( struct inode * inode, struct file * filp ); -static void acsi_release( struct inode * inode, struct file * file ); +static int acsi_release( struct inode * inode, struct file * file ); static void acsi_prevent_removal( int target, int flag ); static int acsi_change_blk_size( int target, int lun); static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd ); @@ -1200,7 +1200,7 @@ * be forgotten about... */ -static void acsi_release( struct inode * inode, struct file * file ) +static int acsi_release( struct inode * inode, struct file * file ) { int device; @@ -1210,6 +1210,7 @@ if (--access_count[device] == 0 && acsi_info[device].removable) acsi_prevent_removal(device, 0); MOD_DEC_USE_COUNT; + return( 0 ); } /* @@ -1821,7 +1822,7 @@ { del_timer( &acsi_timer ); blk_dev[MAJOR_NR].request_fn = 0; - free_pages( acsi_buffer, ACSI_BUFFER_ORDER ); + free_pages( (unsigned long)acsi_buffer, ACSI_BUFFER_ORDER ); if (unregister_blkdev( MAJOR_NR, "ad" ) != 0) printk( KERN_ERR "acsi: cleanup_module failed\n"); diff -u --recursive --new-file v2.1.47/linux/drivers/block/acsi_slm.c linux/drivers/block/acsi_slm.c --- v2.1.47/linux/drivers/block/acsi_slm.c Sun Jan 26 02:07:10 1997 +++ linux/drivers/block/acsi_slm.c Thu Jul 31 13:09:17 1997 @@ -117,7 +117,9 @@ static char slmprint_cmd[6] = { 0x0a, 0, 0, 0, 0, 0 }; static char slminquiry_cmd[6] = { 0x12, 0, 0, 0, 0, 0x80 }; static char slmmsense_cmd[6] = { 0x1a, 0, 0, 0, 255, 0 }; +#if 0 static char slmmselect_cmd[6] = { 0x15, 0, 0, 0, 0, 0 }; +#endif #define MAX_SLM 2 @@ -262,11 +264,13 @@ static int slm_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg ); static int slm_open( struct inode *inode, struct file *file ); -static void slm_release( struct inode *inode, struct file *file ); +static int slm_release( struct inode *inode, struct file *file ); static int slm_req_sense( int device ); static int slm_mode_sense( int device, char *buffer, int abs_flag ); +#if 0 static int slm_mode_select( int device, char *buffer, int len, int default_flag ); +#endif static int slm_get_pagesize( int device, int *w, int *h ); /************************* End of Prototypes **************************/ @@ -794,7 +798,7 @@ } -static void slm_release( struct inode *inode, struct file *file ) +static int slm_release( struct inode *inode, struct file *file ) { int device; struct slm *sip; @@ -806,6 +810,8 @@ sip->wbusy = 0; if (file->f_mode & 1) sip->rbusy = 0; + + return( 0 ); } @@ -876,6 +882,8 @@ } +#if 0 +/* currently unused */ static int slm_mode_select( int device, char *buffer, int len, int default_flag ) @@ -911,6 +919,7 @@ stdma_release(); return( rv ); } +#endif static int slm_get_pagesize( int device, int *w, int *h ) diff -u --recursive --new-file v2.1.47/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v2.1.47/linux/drivers/block/genhd.c Mon Jun 16 16:35:54 1997 +++ linux/drivers/block/genhd.c Sun Jul 27 12:17:32 1997 @@ -907,14 +907,14 @@ __initfunc(void device_setup(void)) { extern void console_map_init(void); -#ifdef CONFIG_PNP_PARPORT - extern int pnp_parport_init(void); +#ifdef CONFIG_PARPORT + extern int parport_init(void); #endif struct gendisk *p; int nr=0; -#ifdef CONFIG_PNP_PARPORT - pnp_parport_init(); +#ifdef CONFIG_PARPORT + parport_init(); #endif chr_dev_init(); blk_dev_init(); diff -u --recursive --new-file v2.1.47/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.1.47/linux/drivers/char/Config.in Sun Jul 27 12:11:00 1997 +++ linux/drivers/char/Config.in Thu Jul 31 13:09:17 1997 @@ -99,5 +99,6 @@ tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG fi bool 'Enhanced Real Time Clock Support' CONFIG_RTC +tristate '/dev/nvram support' CONFIG_NVRAM tristate 'PC joystick support' CONFIG_JOYSTICK endmenu diff -u --recursive --new-file v2.1.47/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.1.47/linux/drivers/char/Makefile Sun Jul 27 12:11:00 1997 +++ linux/drivers/char/Makefile Thu Jul 31 13:09:17 1997 @@ -261,6 +261,16 @@ L_OBJS += rtc.o endif +ifeq ($(CONFIG_NVRAM),y) +M = y +L_OBJS += nvram.o +else + ifeq ($(CONFIG_NVRAM),m) + MM = m + M_OBJS += nvram.o + endif +endif + ifeq ($(CONFIG_QIC02_TAPE),y) L_OBJS += tpqic02.o else diff -u --recursive --new-file v2.1.47/linux/drivers/char/fbmem.c linux/drivers/char/fbmem.c --- v2.1.47/linux/drivers/char/fbmem.c Mon Jun 16 16:35:55 1997 +++ linux/drivers/char/fbmem.c Thu Jul 31 13:09:17 1997 @@ -221,8 +221,7 @@ if (remap_page_range(vma->vm_start, vma->vm_offset, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + vma->vm_dentry = dget(file->f_dentry); return 0; } diff -u --recursive --new-file v2.1.47/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c --- v2.1.47/linux/drivers/char/keyboard.c Mon Jun 16 16:35:55 1997 +++ linux/drivers/char/keyboard.c Thu Jul 31 13:09:17 1997 @@ -37,6 +37,7 @@ #include #include #include +#include #define SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -148,8 +149,6 @@ struct pt_regs * kbd_pt_regs; #ifdef CONFIG_MAGIC_SYSRQ -#define SYSRQ_KEY 0x54 -extern void handle_sysrq(int, struct pt_regs *, struct kbd_struct *, struct tty_struct *); static int sysrq_pressed; #endif @@ -239,7 +238,7 @@ return; } else if (sysrq_pressed) { if (!up_flag) - handle_sysrq(keycode, kbd_pt_regs, kbd, tty); + handle_sysrq(kbd_sysrq_xlate[keycode], kbd_pt_regs, kbd, tty); return; } #endif diff -u --recursive --new-file v2.1.47/linux/drivers/char/lp.c linux/drivers/char/lp.c --- v2.1.47/linux/drivers/char/lp.c Sun Jul 27 12:11:00 1997 +++ linux/drivers/char/lp.c Sun Aug 3 00:08:51 1997 @@ -500,7 +500,7 @@ unsigned int minor = MINOR(inode->i_rdev); unsigned int irq; - if ((irq = LP_IRQ(minor))) { + if ((irq = LP_IRQ(minor)) != PARPORT_IRQ_NONE) { kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE); lp_table[minor].lp_buffer = NULL; } @@ -642,7 +642,7 @@ parport[0] = -3; } else { if (ints[0] == 0 || ints[1] == 0) { - /* disable driver on "parport=" or "parport=0" */ + /* disable driver on "lp=" or "lp=0" */ parport[0] = -2; } else { printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]); @@ -652,19 +652,18 @@ #endif -int lp_wakeup(void *ref) +void lp_wakeup(void *ref) { struct lp_struct *lp_dev = (struct lp_struct *) ref; if (!lp_dev->lp_wait_q) - return 1; /* Wake up whom? */ + return; /* Wake up whom? */ /* Claim the Parport */ if (parport_claim(lp_dev->dev)) - return 1; /* Shouldn't happen */ + return; /* Shouldn't happen */ wake_up(&lp_dev->lp_wait_q); - return 0; } static int inline lp_searchfor(int list[], int a) diff -u --recursive --new-file v2.1.47/linux/drivers/char/mem.c linux/drivers/char/mem.c --- v2.1.47/linux/drivers/char/mem.c Thu Jul 17 10:06:04 1997 +++ linux/drivers/char/mem.c Thu Jul 31 13:09:17 1997 @@ -133,6 +133,10 @@ if (x86 > 3 && offset >= __pa(high_memory)) pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; #endif +#ifdef __powerpc__ + if (offset >= __pa(high_memory)) + pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE | _PAGE_GUARDED; +#endif if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; vma->vm_dentry = dget(file->f_dentry); diff -u --recursive --new-file v2.1.47/linux/drivers/char/misc.c linux/drivers/char/misc.c --- v2.1.47/linux/drivers/char/misc.c Thu Jul 17 10:06:04 1997 +++ linux/drivers/char/misc.c Thu Jul 31 13:09:17 1997 @@ -74,6 +74,7 @@ extern void pcwatchdog_init(void); extern int rtc_init(void); extern int dsp56k_init(void); +extern int nvram_init(void); #ifdef CONFIG_PROC_FS static int misc_read_proc(char *buf, char **start, off_t offset, @@ -241,6 +242,9 @@ #endif #ifdef CONFIG_ATARI_DSP56K dsp56k_init(); +#endif +#ifdef CONFIG_NVRAM + nvram_init(); #endif #endif /* !MODULE */ if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { diff -u --recursive --new-file v2.1.47/linux/drivers/char/nvram.c linux/drivers/char/nvram.c --- v2.1.47/linux/drivers/char/nvram.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/nvram.c Thu Jul 31 13:09:17 1997 @@ -0,0 +1,696 @@ +/* + * CMOS/NV-RAM driver for Linux + * + * Copyright (C) 1997 Roman Hodek + * idea by and with help from Richard Jelinek + * + * This driver allows you to access the contents of the non-volatile memory in + * the mc146818rtc.h real-time clock. This chip is built into all PCs and into + * many Atari machines. In the former it's called "CMOS-RAM", in the latter + * "NVRAM" (NV stands for non-volatile). + * + * The data are supplied as a (seekable) character device, /dev/nvram. The + * size of this file is 50, the number of freely available bytes in the memory + * (i.e., not used by the RTC itself). + * + * Checksums over the NVRAM contents are managed by this driver. In case of a + * bad checksum, reads and writes return -EIO. The checksum can be initialized + * to a sane state either by ioctl(NVRAM_INIT) (clear whole NVRAM) or + * ioctl(NVRAM_SETCKS) (doesn't change contents, just makes checksum valid + * again; use with care!) + * + * This file also provides some functions for other parts of the kernel that + * want to access the NVRAM: nvram_{read,write,check_checksum,set_checksum}. + * Obviously this can be used only if this driver is always configured into + * the kernel and is not a module. Since the functions are used by some Atari + * drivers, this is the case on the Atari. + * + */ + +#define NVRAM_VERSION "1.0" + +#include +#include + +#define PC 1 +#define ATARI 2 + +/* select machine configuration */ +#if defined(CONFIG_ATARI) +#define MACH ATARI +#elif defined(__i386__) /* and others?? */ +#define MACH PC +#else +#error Cannot build nvram driver for this machine configuration. +#endif + +#if MACH == PC + +/* RTC in a PC */ +#define CHECK_DRIVER_INIT() 1 + +/* On PCs, the checksum is built only over bytes 2..31 */ +#define PC_CKS_RANGE_START 2 +#define PC_CKS_RANGE_END 31 +#define PC_CKS_LOC 32 + +#define mach_check_checksum pc_check_checksum +#define mach_set_checksum pc_set_checksum +#define mach_proc_infos pc_proc_infos + +#endif + +#if MACH == ATARI + +/* Special parameters for RTC in Atari machines */ +#include +#include +#define RTC_PORT(x) (TT_RTC_BAS + 2*(x)) +#define CHECK_DRIVER_INIT() (MACH_IS_ATARI && ATARIHW_PRESENT(TT_CLK)) + +/* On Ataris, the checksum is over all bytes except the checksum bytes + * themselves; these are at the very end */ +#define ATARI_CKS_RANGE_START 0 +#define ATARI_CKS_RANGE_END 47 +#define ATARI_CKS_LOC 48 + +#define mach_check_checksum atari_check_checksum +#define mach_set_checksum atari_set_checksum +#define mach_proc_infos atari_proc_infos + +#endif + +/* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with + * interrupts disabled. Due to the index-port/data-port design of the RTC, we + * don't want two different things trying to get to it at once. (e.g. the + * periodic 11 min sync from time.c vs. this driver.) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PROC_FS +#include +#endif + +#include +#include +#include + + +static int nvram_open_cnt = 0; /* #times opened */ +static int nvram_open_mode; /* special open modes */ +#define NVRAM_WRITE 1 /* opened for writing (exclusive) */ +#define NVRAM_EXCL 2 /* opened with O_EXCL */ + +#define RTC_FIRST_BYTE 14 /* RTC register number of first NVRAM byte */ +#define NVRAM_BYTES 50 /* number of NVRAM bytes */ + + +static int mach_check_checksum( void ); +static void mach_set_checksum( void ); +#ifdef CONFIG_PROC_FS +static int mach_proc_infos( unsigned char *contents, char *buffer, int *len, + off_t *begin, off_t offset, int size ); +#endif + + +/* + * These are the internal NVRAM access functions, which do NOT disable + * interrupts and do not check the checksum. Both tasks are left to higher + * level function, so they need to be done only once per syscall. + */ + +static __inline__ unsigned char nvram_read_int( int i ) +{ + return( CMOS_READ( RTC_FIRST_BYTE+i ) ); +} + +static __inline__ void nvram_write_int( unsigned char c, int i ) +{ + CMOS_WRITE( c, RTC_FIRST_BYTE+i ); +} + +static __inline__ int nvram_check_checksum_int( void ) +{ + return( mach_check_checksum() ); +} + +static __inline__ void nvram_set_checksum_int( void ) +{ + mach_set_checksum(); +} + +#if MACH == ATARI + +/* + * These non-internal functions are provided to be called by other parts of + * the kernel. It's up to the caller to ensure correct checksum before reading + * or after writing (needs to be done only once). + * + * They're only built if CONFIG_ATARI is defined, because Atari drivers use + * them. For other configurations (PC), the rest of the kernel can't rely on + * them being present (this driver couldn't be configured at all, or as a + * module), so they access config information themselves. + */ + +unsigned char nvram_read_byte( int i ) +{ + unsigned long flags; + unsigned char c; + + save_flags(flags); + cli(); + c = nvram_read_int( i ); + restore_flags(flags); + return( c ); +} + +void nvram_write_byte( unsigned char c, int i ) +{ + unsigned long flags; + + save_flags(flags); + cli(); + nvram_write_int( c, i ); + restore_flags(flags); +} + +int nvram_check_checksum( void ) +{ + unsigned long flags; + int rv; + + save_flags(flags); + cli(); + rv = nvram_check_checksum_int(); + restore_flags(flags); + return( rv ); +} + +void nvram_set_checksum( void ) +{ + unsigned long flags; + + save_flags(flags); + cli(); + nvram_set_checksum_int(); + restore_flags(flags); +} + +#endif /* MACH == ATARI */ + + +/* + * The are the file operation function for user access to /dev/nvram + */ + +static long long nvram_llseek( struct inode *inode, struct file *file, + loff_t offset, int origin ) +{ + switch( origin ) { + case 0: + /* nothing to do */ + break; + case 1: + offset += file->f_pos; + break; + case 2: + offset += NVRAM_BYTES; + break; + } + return( (offset >= 0) ? (file->f_pos = offset) : -EINVAL ); +} + +static long nvram_read( struct inode * inode, struct file * file, + char * buf, unsigned long count ) +{ + unsigned long flags; + unsigned i = file->f_pos; + char *tmp = buf; + + save_flags(flags); + cli(); + + if (!nvram_check_checksum_int()) { + restore_flags(flags); + return( -EIO ); + } + + for( ; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp ) + put_user( nvram_read_int(i), tmp ); + file->f_pos = i; + + restore_flags(flags); + return( tmp - buf ); +} + +static long nvram_write( struct inode * inode, struct file * file, + const char * buf, unsigned long count ) +{ + unsigned long flags; + unsigned i = file->f_pos; + const char *tmp = buf; + char c; + + save_flags(flags); + cli(); + + if (!nvram_check_checksum_int()) { + restore_flags(flags); + return( -EIO ); + } + + for( ; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp ) { + get_user( c, tmp ); + nvram_write_int( c, i ); + } + nvram_set_checksum_int(); + file->f_pos = i; + + restore_flags(flags); + return( tmp - buf ); +} + +static int nvram_ioctl( struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg ) +{ + unsigned long flags; + int i; + + switch( cmd ) { + + case NVRAM_INIT: /* initialize NVRAM contents and checksum */ + if (!suser()) + return( -EACCES ); + + save_flags(flags); + cli(); + + for( i = 0; i < NVRAM_BYTES; ++i ) + nvram_write_int( 0, i ); + nvram_set_checksum_int(); + + restore_flags(flags); + return( 0 ); + + case NVRAM_SETCKS: /* just set checksum, contents unchanged + * (maybe useful after checksum garbaged + * somehow...) */ + if (!suser()) + return( -EACCES ); + + save_flags(flags); + cli(); + nvram_set_checksum_int(); + restore_flags(flags); + return( 0 ); + + default: + return( -EINVAL ); + } +} + +static int nvram_open( struct inode *inode, struct file *file ) +{ + if ((nvram_open_cnt && (file->f_flags & O_EXCL)) || + (nvram_open_mode & NVRAM_EXCL) || + ((file->f_mode & 2) && (nvram_open_mode & NVRAM_WRITE))) + return( -EBUSY ); + + if (file->f_flags & O_EXCL) + nvram_open_mode |= NVRAM_EXCL; + if (file->f_mode & 2) + nvram_open_mode |= NVRAM_WRITE; + nvram_open_cnt++; + MOD_INC_USE_COUNT; + return( 0 ); +} + +static int nvram_release( struct inode *inode, struct file *file ) +{ + nvram_open_cnt--; + if (file->f_flags & O_EXCL) + nvram_open_mode &= ~NVRAM_EXCL; + if (file->f_mode & 2) + nvram_open_mode &= ~NVRAM_WRITE; + + MOD_DEC_USE_COUNT; + return( 0 ); +} + + +#ifdef CONFIG_PROC_FS + +struct proc_dir_entry *proc_nvram; + +static int nvram_read_proc( char *buffer, char **start, off_t offset, + int size, int *eof, void *data ) +{ + unsigned long flags; + unsigned char contents[NVRAM_BYTES]; + int i, len = 0; + off_t begin = 0; + + save_flags(flags); + cli(); + for( i = 0; i < NVRAM_BYTES; ++i ) + contents[i] = nvram_read_int( i ); + restore_flags(flags); + + *eof = mach_proc_infos( contents, buffer, &len, &begin, offset, size ); + + if (offset >= begin + len) + return( 0 ); + *start = buffer + (begin - offset); + return( size < begin + len - offset ? size : begin + len - offset ); + +} + +/* This macro frees the machine specific function from bounds checking and + * this like that... */ +#define PRINT_PROC(fmt,args...) \ + do { \ + *len += sprintf( buffer+*len, fmt, ##args ); \ + if (*begin + *len > offset + size) \ + return( 0 ); \ + if (*begin + *len < offset) { \ + *begin += *len; \ + *len = 0; \ + } \ + } while(0) + +#endif + +static struct file_operations nvram_fops = { + nvram_llseek, + nvram_read, + nvram_write, + NULL, /* No readdir */ + NULL, /* No poll */ + nvram_ioctl, + NULL, /* No mmap */ + nvram_open, + nvram_release +}; + +static struct miscdevice nvram_dev = { + NVRAM_MINOR, + "nvram", + &nvram_fops +}; + + +__initfunc(int nvram_init(void)) +{ + /* First test whether the driver should init at all */ + if (!CHECK_DRIVER_INIT()) + return( -ENXIO ); + + printk( "Non-volatile memory driver v%s\n", NVRAM_VERSION ); + misc_register( &nvram_dev ); +#ifdef CONFIG_PROC_FS + if ((proc_nvram = create_proc_entry( "nvram", 0, 0 ))) + proc_nvram->read_proc = nvram_read_proc; +#endif + + return( 0 ); +} + +#ifdef MODULE +int init_module (void) +{ + return( nvram_init() ); +} + +void cleanup_module (void) +{ +#ifdef CONFIG_PROC_FS + if (proc_nvram) + remove_proc_entry( "nvram", 0 ); +#endif + misc_deregister( &nvram_dev ); +} +#endif + + +/* + * Machine specific functions + */ + + +#if MACH == PC + +static int pc_check_checksum( void ) +{ + int i; + unsigned short sum = 0; + + for( i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i ) + sum += nvram_read_int( i ); + return( (sum & 0xffff) == + ((nvram_read_int(PC_CKS_LOC) << 8) | + nvram_read_int(PC_CKS_LOC+1)) ); +} + +static void pc_set_checksum( void ) +{ + int i; + unsigned short sum = 0; + + for( i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i ) + sum += nvram_read_int( i ); + nvram_write_int( sum >> 8, PC_CKS_LOC ); + nvram_write_int( sum & 0xff, PC_CKS_LOC+1 ); +} + +#ifdef CONFIG_PROC_FS + +static char *floppy_types[] = { + "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M" +}; + +static char *gfx_types[] = { + "EGA, VGA, ... (with BIOS)", + "CGA (40 cols)", + "CGA (80 cols)", + "monochrome", +}; + +static int pc_proc_infos( unsigned char *nvram, char *buffer, int *len, + off_t *begin, off_t offset, int size ) +{ + unsigned long flags; + int checksum; + int type; + + save_flags(flags); + cli(); + checksum = nvram_check_checksum_int(); + restore_flags(flags); + + PRINT_PROC( "Checksum status: %svalid\n", checksum ? "" : "not " ); + + PRINT_PROC( "# floppies : %d\n", + (nvram[6] & 1) ? (nvram[6] >> 6) + 1 : 0 ); + PRINT_PROC( "Floppy 0 type : " ); + type = nvram[2] >> 4; + if (type < sizeof(floppy_types)/sizeof(*floppy_types)) + PRINT_PROC( "%s\n", floppy_types[type] ); + else + PRINT_PROC( "%d (unknown)\n", type ); + PRINT_PROC( "Floppy 1 type : " ); + type = nvram[2] & 0x0f; + if (type < sizeof(floppy_types)/sizeof(*floppy_types)) + PRINT_PROC( "%s\n", floppy_types[type] ); + else + PRINT_PROC( "%d (unknown)\n", type ); + + PRINT_PROC( "HD 0 type : " ); + type = nvram[4] >> 4; + if (type) + PRINT_PROC( " %02x\n", type == 0x0f ? nvram[11] : type ); + else + PRINT_PROC( "none\n" ); + + PRINT_PROC( "HD 1 type : " ); + type = nvram[4] & 0x0f; + if (type) + PRINT_PROC( " %02x\n", type == 0x0f ? nvram[12] : type ); + else + PRINT_PROC( "none\n" ); + + PRINT_PROC( "HD type 48 data: %d/%d/%d C/H/S, precomp %d, lz %d\n", + nvram[18] | (nvram[19] << 8), + nvram[20], nvram[25], + nvram[21] | (nvram[22] << 8), + nvram[23] | (nvram[24] << 8) ); + PRINT_PROC( "HD type 49 data: %d/%d/%d C/H/S, precomp %d, lz %d\n", + nvram[39] | (nvram[40] << 8), + nvram[41], nvram[46], + nvram[42] | (nvram[43] << 8), + nvram[44] | (nvram[45] << 8) ); + + PRINT_PROC( "DOS base memory: %d kB\n", nvram[7] | (nvram[8] << 8) ); + PRINT_PROC( "Extended memory: %d kB (configured), %d kB (tested)\n", + nvram[9] | (nvram[10] << 8), + nvram[34] | (nvram[35] << 8) ); + + PRINT_PROC( "Gfx adapter : %s\n", gfx_types[ (nvram[6] >> 4)&3 ] ); + + PRINT_PROC( "FPU : %sinstalled\n", + (nvram[6] & 2) ? "" : "not " ); + + return( 1 ); +} +#endif + +#endif /* MACH == PC */ + +#if MACH == ATARI + +static int atari_check_checksum( void ) +{ + int i; + unsigned char sum = 0; + + for( i = ATARI_CKS_RANGE_START; i <= ATARI_CKS_RANGE_END; ++i ) + sum += nvram_read_int( i ); + return( nvram_read_int( ATARI_CKS_LOC ) == (~sum & 0xff) && + nvram_read_int( ATARI_CKS_LOC+1 ) == (sum & 0xff) ); +} + +static void atari_set_checksum( void ) +{ + int i; + unsigned char sum = 0; + + for( i = ATARI_CKS_RANGE_START; i <= ATARI_CKS_RANGE_END; ++i ) + sum += nvram_read_int( i ); + nvram_write_int( ~sum, ATARI_CKS_LOC ); + nvram_write_int( sum, ATARI_CKS_LOC+1 ); +} + +#ifdef CONFIG_PROC_FS + +static struct { + unsigned char val; + char *name; +} boot_prefs[] = { + { 0x80, "TOS" }, + { 0x40, "ASV" }, + { 0x20, "NetBSD (?)" }, + { 0x10, "Linux" }, + { 0x00, "unspecified" } +}; + +static char *languages[] = { + "English (US)", + "German", + "French", + "English (UK)", + "Spanish", + "Italian", + "6 (undefined)", + "Swiss (French)", + "Swiss (German)" +}; + +static char *dateformat[] = { + "MM%cDD%cYY", + "DD%cMM%cYY", + "YY%cMM%cDD", + "YY%cDD%cMM", + "4 (undefined)", + "5 (undefined)", + "6 (undefined)", + "7 (undefined)" +}; + +static char *colors[] = { + "2", "4", "16", "256", "65536", "??", "??", "??" +}; + +#define fieldsize(a) (sizeof(a)/sizeof(*a)) + +static int atari_proc_infos( unsigned char *nvram, char *buffer, int *len, + off_t *begin, off_t offset, int size ) +{ + int checksum = nvram_check_checksum(); + int i; + unsigned vmode; + + PRINT_PROC( "Checksum status : %svalid\n", checksum ? "" : "not " ); + + PRINT_PROC( "Boot preference : " ); + for( i = fieldsize(boot_prefs)-1; i >= 0; --i ) { + if (nvram[1] == boot_prefs[i].val) { + PRINT_PROC( "%s\n", boot_prefs[i].name ); + break; + } + } + if (i < 0) + PRINT_PROC( "0x%02x (undefined)\n", nvram[1] ); + + PRINT_PROC( "SCSI arbitration : %s\n", (nvram[16] & 0x80) ? "on" : "off" ); + PRINT_PROC( "SCSI host ID : " ); + if (nvram[16] & 0x80) + PRINT_PROC( "%d\n", nvram[16] & 7 ); + else + PRINT_PROC( "n/a\n" ); + + /* the following entries are defined only for the Falcon */ + if ((atari_mch_cookie >> 16) != ATARI_MCH_FALCON) + return; + + PRINT_PROC( "OS language : " ); + if (nvram[6] < fieldsize(languages)) + PRINT_PROC( "%s\n", languages[nvram[6]] ); + else + PRINT_PROC( "%u (undefined)\n", nvram[6] ); + PRINT_PROC( "Keyboard language: " ); + if (nvram[7] < fieldsize(languages)) + PRINT_PROC( "%s\n", languages[nvram[7]] ); + else + PRINT_PROC( "%u (undefined)\n", nvram[7] ); + PRINT_PROC( "Date format : " ); + PRINT_PROC( dateformat[nvram[8]&7], + nvram[9] ? nvram[9] : '/', nvram[9] ? nvram[9] : '/' ); + PRINT_PROC( ", %dh clock\n", nvram[8] & 16 ? 24 : 12 ); + PRINT_PROC( "Boot delay : " ); + if (nvram[10] == 0) + PRINT_PROC( "default" ); + else + PRINT_PROC( "%ds%s\n", nvram[10], + nvram[10] < 8 ? ", no memory test" : "" ); + + vmode = (nvram[14] << 8) || nvram[15]; + PRINT_PROC( "Video mode : %s colors, %d columns, %s %s monitor\n", + colors[vmode & 7], + vmode & 8 ? 80 : 40, + vmode & 16 ? "VGA" : "TV", + vmode & 32 ? "PAL" : "NTSC" ); + PRINT_PROC( " %soverscan, compat. mode %s%s\n", + vmode & 64 ? "" : "no ", + vmode & 128 ? "on" : "off", + vmode & 256 ? + (vmode & 16 ? ", line doubling" : ", half screen") : "" ); + + return( 1 ); +} +#endif + +#endif /* MACH == ATARI */ + +/* + * Local variables: + * c-indent-level: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.1.47/linux/drivers/char/pc110pad.c linux/drivers/char/pc110pad.c --- v2.1.47/linux/drivers/char/pc110pad.c Thu Jul 17 10:06:04 1997 +++ linux/drivers/char/pc110pad.c Thu Jul 31 13:09:17 1997 @@ -492,7 +492,7 @@ { fasync_pad(inode, file, 0); if (--active) - return; + return 0; outb(0x30, current_params.io+2); /* switch off digitiser */ MOD_DEC_USE_COUNT; return 0; @@ -640,7 +640,7 @@ }; -static int pc110pad_init(void) +int pc110pad_init(void) { current_params = default_params; diff -u --recursive --new-file v2.1.47/linux/drivers/char/pc110pad.h linux/drivers/char/pc110pad.h --- v2.1.47/linux/drivers/char/pc110pad.h Thu Jul 17 10:06:04 1997 +++ linux/drivers/char/pc110pad.h Thu Jul 31 13:09:17 1997 @@ -26,6 +26,6 @@ #define PC110PAD_IOCTL_TYPE 0x9a #define PC110PADIOCGETP _IOR(PC110PAD_IOCTL_TYPE, 0, struct pc110pad_params) -#define PC110PADIOCSETP _IOR(PC110PAD_IOCTL_TYPE, 1, struct pc110pad_params) +#define PC110PADIOCSETP _IOW(PC110PAD_IOCTL_TYPE, 1, struct pc110pad_params) #endif /* _PC110PAD_H */ diff -u --recursive --new-file v2.1.47/linux/drivers/char/pc_keyb.c linux/drivers/char/pc_keyb.c --- v2.1.47/linux/drivers/char/pc_keyb.c Mon Jun 16 16:35:55 1997 +++ linux/drivers/char/pc_keyb.c Mon Aug 4 12:52:22 1997 @@ -7,6 +7,8 @@ * Major cleanup by Martin Mares, May 1997 */ +#include + #include #include #include @@ -25,21 +27,38 @@ #include "pc_keyb.h" +/* Simple translation table for the SysRq keys */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char pckbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + /* * In case we run on a non-x86 hardware we need to initialize both the keyboard * controller and the keyboard. On a x86, the BIOS will already have initialized * them. */ +#ifndef __i386__ +#define INIT_KBD +#endif + #ifdef INIT_KBD __initfunc(static int kbd_wait_for_input(void)) { - int n; - int status, data; + int n; + int status, data; + unsigned long start = jiffies; - n = KBD_TIMEOUT; - do { + do { status = inb(KBD_STATUS_REG); /* * Wait for input data to become available. This bit will @@ -62,7 +81,7 @@ continue; } return (data & 0xff); - } while (--n); + } while (jiffies - start < KBD_INIT_TIMEOUT); return -1; /* timed-out if fell through to here... */ } @@ -155,12 +174,11 @@ __initfunc(static void initialize_kbd(void)) { - unsigned long flags; char *msg; - save_flags(flags); cli(); + disable_irq(KEYBOARD_IRQ); msg = initialize_kbd2(); - restore_flags(flags); + enable_irq(KEYBOARD_IRQ); if (msg) printk(KERN_WARNING "initialize_kbd: %s\n", msg); @@ -181,12 +199,15 @@ static inline void kb_wait(void) { - int i; + unsigned long start = jiffies; - for (i=0; i= KBD_TIMEOUT) { +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING "Keyboard timeout\n"); +#endif + return 0; + } + } while (!resend); } while (retries-- > 0); +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING "keyboard: Too many NACKs -- noisy kbd cable?\n"); +#endif return 0; } diff -u --recursive --new-file v2.1.47/linux/drivers/char/pc_keyb.h linux/drivers/char/pc_keyb.h --- v2.1.47/linux/drivers/char/pc_keyb.h Mon Jun 16 16:35:55 1997 +++ linux/drivers/char/pc_keyb.h Thu Jul 31 13:09:17 1997 @@ -12,8 +12,12 @@ #define KBD_REPORT_ERR /* Report keyboard errors */ #define KBD_REPORT_UNKN /* Report unknown scan codes */ +#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ #undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */ -#define KBD_TIMEOUT 0x100000 /* Timeout for sending of commands */ + +#define KBD_INIT_TIMEOUT HZ /* Timeout for initializing the keyboard */ +#define KBC_TIMEOUT (HZ/4) /* Timeout for sending to keyboard controller */ +#define KBD_TIMEOUT (HZ/4) /* Timeout for keyboard command acknowledge */ /* * Internal variables of the driver diff -u --recursive --new-file v2.1.47/linux/drivers/char/psaux.c linux/drivers/char/psaux.c --- v2.1.47/linux/drivers/char/psaux.c Thu Jul 17 10:06:04 1997 +++ linux/drivers/char/psaux.c Thu Jul 31 13:09:17 1997 @@ -53,6 +53,7 @@ #include #include #include +#include #include @@ -141,19 +142,7 @@ schedule(); retries++; } - return !(retries==MAX_RETRIES); -} - -static int poll_aux_status_nosleep(void) -{ - int retries = 0; - - while ((inb(KBD_STATUS_REG) & (KBD_STAT_IBF | KBD_STAT_OBF)) && retries < 1000000) { - if ((inb_p(KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) - inb_p(KBD_DATA_REG); - retries++; - } - return !(retries == 1000000); + return (retries < MAX_RETRIES); } /* @@ -173,18 +162,10 @@ */ #ifdef INITIALIZE_DEVICE -__initfunc(static void aux_write_dev_nosleep(int val)) -{ - poll_aux_status_nosleep(); - outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG); - poll_aux_status_nosleep(); - outb_p(val, KBD_DATA_REG); -} - __initfunc(static int aux_write_ack(int val)) { - aux_write_dev_nosleep(val); - poll_aux_status_nosleep(); + aux_write_dev(val); + poll_aux_status(); if ((inb(KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) { @@ -207,6 +188,31 @@ } /* + * AUX handler critical section start and end. + * + * Only one process can be in the critical section and all keyboard sends are + * deferred as long as we're inside. This is necessary as we may sleep when + * waiting for the keyboard controller and other processes / BH's can + * preempt us. Please note that the input buffer must be flushed when + * aux_end_atomic() is called and the interrupt is no longer enabled as not + * doing so might cause the keyboard driver to ignore all incoming keystrokes. + */ + +static struct semaphore aux_sema4 = MUTEX; + +static inline void aux_start_atomic(void) +{ + down(&aux_sema4); + disable_bh(KEYBOARD_BH); +} + +static inline void aux_end_atomic(void) +{ + enable_bh(KEYBOARD_BH); + up(&aux_sema4); +} + +/* * Interrupt from the auxiliary device: a character * is waiting in the keyboard/aux controller. */ @@ -236,14 +242,12 @@ fasync_aux(inode, file, 0); if (--aux_count) return 0; - /* disable kbd bh to avoid mixing of cmd bytes */ - disable_bh(KEYBOARD_BH); + aux_start_atomic(); aux_write_cmd(AUX_INTS_OFF); /* Disable controller ints */ poll_aux_status(); outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */ poll_aux_status(); - /* reenable kbd bh */ - enable_bh(KEYBOARD_BH); + aux_end_atomic(); #ifdef CONFIG_MCA free_irq(AUX_IRQ, inode); #else @@ -262,10 +266,14 @@ { if (!aux_present) return -ENODEV; - if (aux_count++) + aux_start_atomic(); + if (aux_count++) { + aux_end_atomic(); return 0; - if (!poll_aux_status()) { + } + if (!poll_aux_status()) { /* FIXME: Race condition */ aux_count--; + aux_end_atomic(); return -EBUSY; } queue->head = queue->tail = 0; /* Flush input queue */ @@ -275,18 +283,16 @@ if (request_irq(AUX_IRQ, aux_interrupt, 0, "PS/2 Mouse", NULL)) { #endif aux_count--; + aux_end_atomic(); return -EBUSY; } MOD_INC_USE_COUNT; - /* disable kbd bh to avoid mixing of cmd bytes */ - disable_bh(KEYBOARD_BH); poll_aux_status(); outb_p(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable Aux */ aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */ aux_write_cmd(AUX_INTS_ON); /* Enable controller ints */ poll_aux_status(); - /* reenable kbd bh */ - enable_bh(KEYBOARD_BH); + aux_end_atomic(); aux_ready = 0; return 0; @@ -304,9 +310,7 @@ if (count) { int written = 0; - /* disable kbd bh to avoid mixing of cmd bytes */ - disable_bh(KEYBOARD_BH); - + aux_start_atomic(); do { char c; if (!poll_aux_status()) @@ -318,8 +322,7 @@ outb_p(c, KBD_DATA_REG); written++; } while (--count); - /* reenable kbd bh */ - enable_bh(KEYBOARD_BH); + aux_end_atomic(); retval = -EIO; if (written) { retval = written; @@ -618,6 +621,7 @@ queue->head = queue->tail = 0; queue->proc_list = NULL; if (!qp_found) { + aux_start_atomic(); #ifdef INITIALIZE_DEVICE outb_p(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable Aux */ aux_write_ack(AUX_SET_SAMPLE); @@ -625,13 +629,15 @@ aux_write_ack(AUX_SET_RES); aux_write_ack(3); /* 8 counts per mm */ aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ - poll_aux_status_nosleep(); + poll_aux_status(); #endif /* INITIALIZE_DEVICE */ outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */ - poll_aux_status_nosleep(); - outb_p(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG); - poll_aux_status_nosleep(); - outb_p(AUX_INTS_OFF, KBD_DATA_REG); + poll_aux_status(); + outb_p(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG); /* Disable controller interrupts */ + poll_aux_status(); + outb_p(AUX_INTS_OFF, KBD_DATA_REG); + poll_aux_status(); + aux_end_atomic(); } return 0; } diff -u --recursive --new-file v2.1.47/linux/drivers/char/sysrq.c linux/drivers/char/sysrq.c --- v2.1.47/linux/drivers/char/sysrq.c Mon Jul 7 08:24:28 1997 +++ linux/drivers/char/sysrq.c Thu Jul 31 13:09:17 1997 @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * $Id: sysrq.c,v 1.3 1997/06/18 09:42:12 mj Exp $ + * $Id: sysrq.c,v 1.4 1997/07/17 11:54:15 mj Exp $ * * Linux Magic System Request Key Hacks * @@ -63,72 +63,72 @@ console_loglevel = 7; printk(KERN_INFO "SysRq: "); switch (key) { - case 19: /* R -- Reset raw mode */ - kbd->kbdmode = VC_XLATE; - printk("Keyboard mode set to XLATE\n"); + case 'r': /* R -- Reset raw mode */ + if (kbd) { + kbd->kbdmode = VC_XLATE; + printk("Keyboard mode set to XLATE\n"); + } break; - case 30: /* A -- SAK */ + case 'a': /* A -- SAK */ printk("SAK\n"); - do_SAK(tty); + if (tty) + do_SAK(tty); reset_vc(fg_console); break; - case 48: /* B -- boot immediately */ + case 'b': /* B -- boot immediately */ printk("Resetting\n"); machine_restart(NULL); break; #ifdef __sparc__ - case 35: /* H -- halt immediately */ + case 'h': /* H -- halt immediately */ printk("Halting\n"); halt_now(); break; #endif #ifdef CONFIG_APM - case 24: /* O -- power off */ + case 'o': /* O -- power off */ printk("Power off\n"); apm_set_power_state(APM_STATE_OFF); break; #endif - case 31: /* S -- emergency sync */ + case 's': /* S -- emergency sync */ printk("Emergency Sync\n"); emergency_sync_scheduled = EMERG_SYNC; wakeup_bdflush(0); break; - case 22: /* U -- emergency remount R/O */ + case 'u': /* U -- emergency remount R/O */ printk("Emergency Remount R/O\n"); emergency_sync_scheduled = EMERG_REMOUNT; wakeup_bdflush(0); break; - case 25: /* P -- show PC */ + case 'p': /* P -- show PC */ printk("Show Regs\n"); if (pt_regs) show_regs(pt_regs); break; - case 20: /* T -- show task info */ + case 't': /* T -- show task info */ printk("Show State\n"); show_state(); break; - case 50: /* M -- show memory info */ + case 'm': /* M -- show memory info */ printk("Show Memory\n"); show_mem(); break; - case 2 ... 11: /* 0-9 -- set console logging level */ - key--; - if (key == 10) - key = 0; - orig_log_level = key; - printk("Log level set to %d\n", key); + case '0' ... '9': /* 0-9 -- set console logging level */ + orig_log_level = key - '0'; + printk("Log level set to %d\n", orig_log_level); break; - case 18: /* E -- terminate all user processes */ + case 'e': /* E -- terminate all user processes */ printk("Terminate All Tasks\n"); send_sig_all(SIGTERM, 0); orig_log_level = 8; /* We probably have killed syslogd */ break; - case 37: /* K -- kill all user processes */ + case 'k': /* K -- kill all user processes */ printk("Kill All Tasks\n"); send_sig_all(SIGKILL, 0); orig_log_level = 8; break; - case 38: /* L -- kill all processes including init */ + case 'l': /* L -- kill all processes including init */ printk("Kill ALL Tasks (even init)\n"); send_sig_all(SIGKILL, 1); orig_log_level = 8; @@ -154,7 +154,7 @@ struct file *file; for (file = inuse_filps; file; file = file->f_next) - if (file->f_inode && file->f_count && S_ISREG(file->f_inode->i_mode)) + if (file->f_dentry && file->f_count && S_ISREG(file->f_dentry->d_inode->i_mode)) file->f_mode &= ~2; } diff -u --recursive --new-file v2.1.47/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.1.47/linux/drivers/char/tty_io.c Sun Jul 27 12:11:00 1997 +++ linux/drivers/char/tty_io.c Sat Aug 2 17:14:06 1997 @@ -688,7 +688,6 @@ return -ENODEV; idx = MINOR(device) - driver->minor_start; - tty = driver->table[idx]; /* * Check whether we need to acquire the tty semaphore to avoid @@ -697,7 +696,8 @@ down_tty_sem(idx); /* check whether we're reopening an existing tty */ - if(tty) goto fast_track; + tty = driver->table[idx]; + if (tty) goto fast_track; /* * First time open is complex, especially for PTY devices. diff -u --recursive --new-file v2.1.47/linux/drivers/char/vga.c linux/drivers/char/vga.c --- v2.1.47/linux/drivers/char/vga.c Mon Jun 16 16:35:55 1997 +++ linux/drivers/char/vga.c Thu Jul 31 13:09:17 1997 @@ -72,6 +72,12 @@ #define dac_reg (0x3c8) #define dac_val (0x3c9) +#ifdef __powerpc__ +#define VGA_OFFSET 0xC0000000; +#else +#define VGA_OFFSET 0x0 +#endif + /* * By replacing the four outb_p with two back to back outw, we can reduce * the window of opportunity to see text mislocated to the RHS of the @@ -152,20 +158,20 @@ { if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */ { - video_mem_base = 0xb0000; + video_mem_base = 0xb0000 + VGA_OFFSET; video_port_reg = 0x3b4; video_port_val = 0x3b5; if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) { video_type = VIDEO_TYPE_EGAM; - video_mem_term = 0xb8000; + video_mem_term = 0xb8000 + VGA_OFFSET; *display_desc = "EGA+"; request_region(0x3b0,16,"ega"); } else { video_type = VIDEO_TYPE_MDA; - video_mem_term = 0xb2000; + video_mem_term = 0xb2000 + VGA_OFFSET; *display_desc = "*MDA"; request_region(0x3b0,12,"mda"); request_region(0x3bf, 1,"mda"); @@ -174,14 +180,14 @@ else /* If not, it is color. */ { can_do_color = 1; - video_mem_base = 0xb8000; + video_mem_base = 0xb8000 + VGA_OFFSET; video_port_reg = 0x3d4; video_port_val = 0x3d5; if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) { int i ; - video_mem_term = 0xc0000; + video_mem_term = 0xc0000 + VGA_OFFSET; if (!ORIG_VIDEO_ISVGA) { video_type = VIDEO_TYPE_EGAC; @@ -199,8 +205,8 @@ * controllers (it seems like setting MM=01 * and COE=1 isn't necessarily a good idea) */ - video_mem_base = 0xa0000 ; - video_mem_term = 0xb0000 ; + video_mem_base = 0xa0000 + VGA_OFFSET; + video_mem_term = 0xb0000 + VGA_OFFSET; outb_p (6, 0x3ce) ; outb_p (6, 0x3cf) ; #endif @@ -232,7 +238,7 @@ else { video_type = VIDEO_TYPE_CGA; - video_mem_term = 0xba000; + video_mem_term = 0xba000 + VGA_OFFSET; *display_desc = "*CGA"; request_region(0x3d4,2,"cga"); } diff -u --recursive --new-file v2.1.47/linux/drivers/misc/parport_arc.c linux/drivers/misc/parport_arc.c --- v2.1.47/linux/drivers/misc/parport_arc.c Sun Jul 27 12:11:00 1997 +++ linux/drivers/misc/parport_arc.c Mon Jul 28 13:46:29 1997 @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff -u --recursive --new-file v2.1.47/linux/drivers/misc/parport_init.c linux/drivers/misc/parport_init.c --- v2.1.47/linux/drivers/misc/parport_init.c Sun Jul 27 12:11:00 1997 +++ linux/drivers/misc/parport_init.c Sun Aug 3 00:08:51 1997 @@ -56,6 +56,7 @@ #ifdef MODULE int init_module(void) { + parport_proc_init(); return 0; } @@ -79,7 +80,8 @@ { struct parport *pb; - if (io[0] == PARPORT_DISABLE) return 1; + if (io[0] == PARPORT_DISABLE) return 1; + parport_proc_init(); #ifdef CONFIG_PARPORT_PC parport_pc_init(io, irq, dma); #endif @@ -92,12 +94,15 @@ EXPORT_SYMBOL(parport_claim); EXPORT_SYMBOL(parport_release); EXPORT_SYMBOL(parport_register_port); +EXPORT_SYMBOL(parport_unregister_port); EXPORT_SYMBOL(parport_quiesce); EXPORT_SYMBOL(parport_register_device); EXPORT_SYMBOL(parport_unregister_device); EXPORT_SYMBOL(parport_enumerate); EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok); EXPORT_SYMBOL(parport_wait_peripheral); +EXPORT_SYMBOL(parport_proc_register); +EXPORT_SYMBOL(parport_proc_unregister); void inc_parport_count(void) { diff -u --recursive --new-file v2.1.47/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c --- v2.1.47/linux/drivers/misc/parport_pc.c Sun Jul 27 12:11:00 1997 +++ linux/drivers/misc/parport_pc.c Sun Aug 3 00:08:51 1997 @@ -10,6 +10,7 @@ * and Philip Blundell */ +#include #include #include @@ -140,7 +141,7 @@ static void pc_release_resources(struct parport *p) { if (p->irq != PARPORT_IRQ_NONE) - free_irq(p->irq, NULL); + free_irq(p->irq, p); release_region(p->base, p->size); if (p->modes & PARPORT_MODE_PCECR) release_region(p->base+0x400, 3); @@ -439,9 +440,10 @@ */ static int parport_ECR_present(struct parport *pb) { - int r, octr = pc_read_control(pb), oecr = pc_read_econtrol(pb); + unsigned int r, octr = pc_read_control(pb), + oecr = pc_read_econtrol(pb); - r= pc_read_control(pb); + r = pc_read_control(pb); if ((pc_read_econtrol(pb) & 0x03) == (r & 0x03)) { pc_write_control(pb, r ^ 0x03 ); /* Toggle bits 0-1 */ @@ -820,7 +822,10 @@ } if (p->irq != PARPORT_IRQ_NONE) printk(", irq %d", p->irq); - if (p->irq != PARPORT_DMA_NONE) + if (p->dma == PARPORT_DMA_AUTO) + p->dma = (p->modes & PARPORT_MODE_PCECP)? + parport_dma_probe(p):PARPORT_DMA_NONE; + if (p->dma != PARPORT_DMA_NONE) printk(", dma %d", p->dma); printk(" ["); #define printmode(x) {if(p->modes&PARPORT_MODE_PC##x){printk("%s%s",f?",":"",#x);f++;}} @@ -835,6 +840,7 @@ } #undef printmode printk("]\n"); + parport_proc_register(p); return 1; } @@ -868,7 +874,7 @@ static int init_module(void) { - return (parport_pc_init(NULL, NULL, NULL)?0:1); + return (parport_pc_init(io, irq, dma)?0:1); } static void cleanup_module(void) @@ -878,6 +884,8 @@ if (p->modes & PARPORT_MODE_PCSPP) { if (!(p->flags & PARPORT_FLAG_COMA)) parport_quiesce(p); + parport_proc_unregister(p); + parport_unregister_port(p); } p = p->next; } diff -u --recursive --new-file v2.1.47/linux/drivers/misc/parport_procfs.c linux/drivers/misc/parport_procfs.c --- v2.1.47/linux/drivers/misc/parport_procfs.c Sun Jul 27 12:11:00 1997 +++ linux/drivers/misc/parport_procfs.c Sun Aug 3 00:08:51 1997 @@ -8,12 +8,12 @@ * and Philip Blundell */ +#include #include #include #include #include -#include #include #include #include @@ -131,7 +131,10 @@ len += sprintf(page+len, "irq:\tnone\n"); else len += sprintf(page+len, "irq:\t%d\n",pp->irq); - len += sprintf(page+len, "dma:\t%d\n",pp->dma); + if (pp->dma == PARPORT_DMA_NONE) + len += sprintf(page+len, "dma:\tnone\n"); + else + len += sprintf(page+len, "dma:\t%d\n",pp->dma); #if 0 diff -u --recursive --new-file v2.1.47/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c --- v2.1.47/linux/drivers/misc/parport_share.c Sun Jul 27 12:11:00 1997 +++ linux/drivers/misc/parport_share.c Sun Aug 3 00:08:51 1997 @@ -88,6 +88,23 @@ return tmp; } +void parport_unregister_port(struct parport *port) +{ + struct parport *p; + kfree(port->name); + if (portlist == port) { + portlist = port->next; + } else { + for (p = portlist; (p != NULL) && (p->next != port); + p=p->next); + if (p) { + if ((p->next = port->next) == NULL) + portlist_tail = p; + } + } + kfree(port); +} + void parport_quiesce(struct parport *port) { if (port->devices) { @@ -106,7 +123,7 @@ } struct pardevice *parport_register_device(struct parport *port, const char *name, - int (*pf)(void *), int (*kf)(void *), + int (*pf)(void *), void (*kf)(void *), void (*irq_func)(int, void *, struct pt_regs *), int flags, void *handle) { @@ -223,7 +240,6 @@ pd1 = dev->port->cad; if (dev->port->cad) { if (dev->port->cad->preempt) { - /* Now try to preempt */ if (dev->port->cad->preempt(dev->port->cad->private)) return -EAGAIN; dev->port->ops->save_state(dev->port, dev->state); @@ -301,19 +317,10 @@ } /* Now give the lurker a chance. - * There should be a wakeup callback because we checked for it + * There must be a wakeup callback because we checked for it * at registration. */ if (dev->port->lurker && (dev->port->lurker != dev)) { - if (dev->port->lurker->wakeup) { - dev->port->lurker->wakeup(dev->port->lurker->private); - } -#ifdef PARPORT_PARANOID - else { /* can't happen */ - printk(KERN_DEBUG - "%s (%s): lurker's wakeup callback went away!\n", - dev->port->name, dev->name); - } -#endif + dev->port->lurker->wakeup(dev->port->lurker->private); } } diff -u --recursive --new-file v2.1.47/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c --- v2.1.47/linux/drivers/net/myri_sbus.c Tue May 13 22:41:09 1997 +++ linux/drivers/net/myri_sbus.c Thu Jul 31 13:09:17 1997 @@ -275,7 +275,7 @@ mp->rx_skbs[i] = skb; skb->dev = dev; skb_put(skb, RX_ALLOC_SIZE); - rxd[i].myri_scatters[0].addr = (unsigned int) skb->data; + rxd[i].myri_scatters[0].addr = (unsigned long) skb->data; rxd[i].myri_scatters[0].len = RX_ALLOC_SIZE; rxd[i].ctx = i; rxd[i].num_sg = 1; @@ -429,7 +429,7 @@ drops++; DRX(("DROP ")); mp->enet_stats.rx_dropped++; - rxd->myri_scatters[0].addr = (unsigned int) skb->data; + rxd->myri_scatters[0].addr = (unsigned long) skb->data; rxd->myri_scatters[0].len = RX_ALLOC_SIZE; rxd->ctx = index; rxd->num_sg = 1; @@ -450,7 +450,7 @@ mp->rx_skbs[index] = new_skb; new_skb->dev = dev; skb_put(new_skb, RX_ALLOC_SIZE); - rxd->myri_scatters[0].addr = (unsigned int) new_skb->data; + rxd->myri_scatters[0].addr = (unsigned long) new_skb->data; rxd->myri_scatters[0].len = RX_ALLOC_SIZE; rxd->ctx = index; rxd->num_sg = 1; @@ -474,7 +474,7 @@ /* Reuse original ring buffer. */ DRX(("reuse ")); - rxd->myri_scatters[0].addr = (unsigned int) skb->data; + rxd->myri_scatters[0].addr = (unsigned long) skb->data; rxd->myri_scatters[0].len = RX_ALLOC_SIZE; rxd->ctx = index; rxd->num_sg = 1; @@ -618,7 +618,7 @@ txd = &sq->myri_txd[entry]; mp->tx_skbs[entry] = skb; - txd->myri_gathers[0].addr = (unsigned int) skb->data; + txd->myri_gathers[0].addr = (unsigned long) skb->data; txd->myri_gathers[0].len = len; txd->num_sg = 1; txd->chan = KERNEL_CHANNEL; diff -u --recursive --new-file v2.1.47/linux/drivers/net/plip.c linux/drivers/net/plip.c --- v2.1.47/linux/drivers/net/plip.c Tue May 13 22:41:09 1997 +++ linux/drivers/net/plip.c Sun Aug 3 00:08:51 1997 @@ -204,7 +204,7 @@ struct tq_struct deferred; struct plip_local snd_data; struct plip_local rcv_data; - struct ppd *pardev; + struct pardevice *pardev; unsigned long trigger; unsigned long nibble; enum plip_connection_state connection; @@ -228,7 +228,7 @@ plip_init_dev(struct device *dev, struct parport *pb)) { struct net_local *nl; - struct ppd *pardev; + struct pardevice *pardev; dev->irq = pb->irq; dev->base_addr = pb->base; @@ -239,8 +239,8 @@ } pardev = parport_register_device(pb, dev->name, plip_preempt, - plip_wakeup, - plip_interrupt, PARPORT_DEV_LURK, dev); + plip_wakeup, plip_interrupt, + PARPORT_DEV_LURK, dev); printk(version); printk("%s: Parallel port at %#3lx, using IRQ %d\n", dev->name, @@ -1045,7 +1045,7 @@ return 0; } -static int +static void plip_wakeup(void *handle) { struct device *dev = (struct device *)handle; @@ -1058,12 +1058,12 @@ /* bus_owner is already set (but why?) */ printk(KERN_DEBUG "%s: I'm broken.\n", dev->name); else - return 1; + return; } if (!(dev->flags & IFF_UP)) /* Don't need the port when the interface is down */ - return 1; + return; if (!parport_claim(nl->pardev)) { nl->port_owner = 1; @@ -1072,7 +1072,7 @@ outb (0x00, PAR_DATA(dev)); } - return 0; + return; } static struct net_device_stats * diff -u --recursive --new-file v2.1.47/linux/drivers/net/soundmodem/sm_sbc.c linux/drivers/net/soundmodem/sm_sbc.c --- v2.1.47/linux/drivers/net/soundmodem/sm_sbc.c Mon Jun 16 16:35:56 1997 +++ linux/drivers/net/soundmodem/sm_sbc.c Thu Jul 31 13:09:17 1997 @@ -339,6 +339,7 @@ sti(); if (sm->dma.ptt_cnt <= 0) { dma_receive(sm, curfrag); + hdlcdrv_arbitrate(dev, &sm->hdrv); if (hdlcdrv_ptt(&sm->hdrv)) { /* starting to transmit */ disable_dma(dev->dma); @@ -352,10 +353,8 @@ sti(); dma_init_receive(sm); setup_dma_dsp(dev, sm, 0); - } else { + } else dma_transmit(sm); - hdlcdrv_arbitrate(dev, &sm->hdrv); - } sm_output_status(sm); hdlcdrv_transmitter(dev, &sm->hdrv); hdlcdrv_receiver(dev, &sm->hdrv); diff -u --recursive --new-file v2.1.47/linux/drivers/net/soundmodem/sm_wss.c linux/drivers/net/soundmodem/sm_wss.c --- v2.1.47/linux/drivers/net/soundmodem/sm_wss.c Mon Jun 16 16:35:56 1997 +++ linux/drivers/net/soundmodem/sm_wss.c Thu Jul 31 13:09:17 1997 @@ -402,6 +402,7 @@ sti(); if (sm->dma.ptt_cnt <= 0) { dma_receive(sm, curfrag); + hdlcdrv_arbitrate(dev, &sm->hdrv); if (hdlcdrv_ptt(&sm->hdrv)) { /* starting to transmit */ disable_dma(dev->dma); @@ -415,10 +416,8 @@ sti(); dma_init_receive(sm); setup_dma_wss(dev, sm, 0); - } else { + } else dma_transmit(sm); - hdlcdrv_arbitrate(dev, &sm->hdrv); - } sm_output_status(sm); hdlcdrv_transmitter(dev, &sm->hdrv); hdlcdrv_receiver(dev, &sm->hdrv); diff -u --recursive --new-file v2.1.47/linux/drivers/pnp/parport_probe.c linux/drivers/pnp/parport_probe.c --- v2.1.47/linux/drivers/pnp/parport_probe.c Sun Jul 27 12:11:00 1997 +++ linux/drivers/pnp/parport_probe.c Sun Aug 3 00:08:52 1997 @@ -86,15 +86,15 @@ static struct wait_queue *wait_q = NULL; -static int wakeup(void *ref) +static void wakeup(void *ref) { struct pardevice **dev = (struct pardevice **)ref; if (!wait_q || parport_claim(*dev)) - return 1; + return; wake_up(&wait_q); - return 0; + return; } int parport_probe(struct parport *port, char *buffer, int len) diff -u --recursive --new-file v2.1.47/linux/drivers/sbus/char/Makefile linux/drivers/sbus/char/Makefile --- v2.1.47/linux/drivers/sbus/char/Makefile Wed Apr 23 19:01:21 1997 +++ linux/drivers/sbus/char/Makefile Thu Jul 31 13:09:17 1997 @@ -32,7 +32,9 @@ FB_OBJS += weitek.o endif ifdef SUN_FB_CREATOR + ifeq ($(ARCH),sparc64) FB_OBJS += creator.o + endif endif #ifdef SUN_FB_FAST_ONE # FB_OBJS += sun_8bit_fast1.o diff -u --recursive --new-file v2.1.47/linux/drivers/sbus/char/creator.c linux/drivers/sbus/char/creator.c --- v2.1.47/linux/drivers/sbus/char/creator.c Thu Jul 17 10:06:05 1997 +++ linux/drivers/sbus/char/creator.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: creator.c,v 1.7 1997/07/17 02:21:47 davem Exp $ +/* $Id: creator.c,v 1.8 1997/07/22 06:14:12 davem Exp $ * creator.c: Creator/Creator3D frame buffer driver * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -235,7 +235,6 @@ struct fb_wid_item wit[30]; char *km = NULL; int i, j; - u32 l; int err; #ifdef CONFIG_SPARC32_COMPAT @@ -287,7 +286,6 @@ struct fb_wid_item wit[30]; char *km = NULL; int i, j; - u32 l; #ifdef CONFIG_SPARC32_COMPAT if (current->tss.flags & SPARC_FLAG_32BIT) { @@ -464,8 +462,6 @@ static int ffb_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long arg, fbinfo_t *fb) { - int i; - switch (cmd) { case FBIO_WID_GET: return ffb_wid_get (fb, (struct fb_wid_list *)arg); diff -u --recursive --new-file v2.1.47/linux/drivers/sbus/char/suncons.c linux/drivers/sbus/char/suncons.c --- v2.1.47/linux/drivers/sbus/char/suncons.c Thu Jul 17 10:06:05 1997 +++ linux/drivers/sbus/char/suncons.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: suncons.c,v 1.66 1997/07/15 09:48:47 jj Exp $ +/* $Id: suncons.c,v 1.67 1997/07/20 05:59:42 davem Exp $ * * suncons.c: Sun SparcStation console support. * @@ -931,7 +931,7 @@ leo_setup (&fbinfo [n], n, base, io); break; #endif -#ifdef SUN_FB_CREATOR +#if defined(SUN_FB_CREATOR) && defined(__sparc_v9__) case FBTYPE_CREATOR: creator_setup (&fbinfo [n], n, con_node, base, io); break; diff -u --recursive --new-file v2.1.47/linux/drivers/sbus/char/tcx.c linux/drivers/sbus/char/tcx.c --- v2.1.47/linux/drivers/sbus/char/tcx.c Thu Jul 17 10:06:05 1997 +++ linux/drivers/sbus/char/tcx.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: tcx.c,v 1.17 1997/07/17 02:21:50 davem Exp $ +/* $Id: tcx.c,v 1.18 1997/07/22 06:14:09 davem Exp $ * tcx.c: SUNW,tcx 24/8bit frame buffer driver * * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -309,9 +309,9 @@ tcxinfo->tec = sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_TEC_OFFSET], 0, sizeof (struct tcx_tec), "tcx_tec", fb->space, 0); if (!fb->base){ - fb->base = (uint) - sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_RAM8BIT_OFFSET], 0, - fb->type.fb_size, "tcx_ram", fb->space, 0); + fb->base = (unsigned long) + sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_RAM8BIT_OFFSET], + 0, fb->type.fb_size, "tcx_ram", fb->space, 0); } if (prom_getbool (node, "hw-cursor")) { diff -u --recursive --new-file v2.1.47/linux/drivers/sbus/char/weitek.c linux/drivers/sbus/char/weitek.c --- v2.1.47/linux/drivers/sbus/char/weitek.c Thu Jul 17 10:06:05 1997 +++ linux/drivers/sbus/char/weitek.c Thu Jul 31 13:09:17 1997 @@ -1,4 +1,4 @@ -/* $Id: weitek.c,v 1.14 1997/07/17 02:21:53 davem Exp $ +/* $Id: weitek.c,v 1.15 1997/07/22 06:14:11 davem Exp $ * weitek.c: Tadpole P9100/P9000 console driver * * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk) @@ -99,8 +99,6 @@ __initfunc(void weitek_setup(fbinfo_t *fb, int slot, u32 addr, int io)) { - extern struct screen_info screen_info; - printk ("weitek%d at 0x%8.8x\n", slot, addr); /* Fill in parameters we left out */ diff -u --recursive --new-file v2.1.47/linux/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c --- v2.1.47/linux/drivers/sbus/sbus.c Wed Apr 23 19:01:21 1997 +++ linux/drivers/sbus/sbus.c Thu Jul 31 13:09:17 1997 @@ -385,5 +385,12 @@ if (sparc_cpu_model == sun4u) auxio_probe (); #endif +#ifdef __sparc_v9__ + if (sparc_cpu_model == sun4u) { + extern void sun4u_start_timers(void); + + sun4u_start_timers(); + } +#endif return memory_start; } diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c --- v2.1.47/linux/drivers/scsi/53c7,8xx.c Tue May 13 22:41:12 1997 +++ linux/drivers/scsi/53c7,8xx.c Thu Jul 31 13:09:17 1997 @@ -560,7 +560,7 @@ * sure what the relationship between the NCR structures * and host structures were going to be. */ - (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (issue[1]) - + (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (le32_to_cpu(issue[1])) - (hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template) - offsetof(struct NCR53c7x0_cmd, dsa)) /* If the IF TRUE bit is not set, it's a NOP */ @@ -795,18 +795,18 @@ for (i = 0, curr = (u32 *) hostdata->schedule; i < host->can_queue; ++i, curr += 2) { curr[0] = hostdata->NOP_insn; - curr[1] = 0xdeadbeef; + curr[1] = le32_to_cpu(0xdeadbeef); } - curr[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE; - curr[1] = (u32) virt_to_bus (hostdata->script) + - hostdata->E_wait_reselect; + curr[0] = le32_to_cpu(((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE); + curr[1] = (u32) le32_to_cpu(virt_to_bus (hostdata->script) + + hostdata->E_wait_reselect); hostdata->reconnect_dsa_head = 0; hostdata->addr_reconnect_dsa_head = (u32) - virt_to_bus((void *) &(hostdata->reconnect_dsa_head)); + le32_to_cpu(virt_to_bus((void *) &(hostdata->reconnect_dsa_head))); hostdata->expecting_iid = 0; hostdata->expecting_sto = 0; if (hostdata->options & OPTION_ALWAYS_SYNCHRONOUS) - hostdata->initiate_sdtr = 0xffff; + hostdata->initiate_sdtr = le32_to_cpu(0xffff); else hostdata->initiate_sdtr = 0; hostdata->talked_to = 0; @@ -877,7 +877,7 @@ * Returns : 0 on success, -1 on failure. */ -static inline int +static int NCR53c7x0_init (struct Scsi_Host *host) { NCR53c7x0_local_declare(); int i, ccf, expected_ccf; @@ -899,6 +899,7 @@ * will differ. */ int expected_mapping = OPTION_IO_MAPPED; + NCR53c7x0_local_setup(host); switch (hostdata->chip) { @@ -929,10 +930,10 @@ /* Assign constants accessed by NCR */ hostdata->NCR53c7xx_zero = 0; - hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT; - hostdata->NCR53c7xx_msg_abort = ABORT; - hostdata->NCR53c7xx_msg_nop = NOP; - hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24; + hostdata->NCR53c7xx_msg_reject = le32_to_cpu(MESSAGE_REJECT); + hostdata->NCR53c7xx_msg_abort = le32_to_cpu(ABORT); + hostdata->NCR53c7xx_msg_nop = le32_to_cpu(NOP); + hostdata->NOP_insn = le32_to_cpu((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24); if (expected_mapping == -1 || (hostdata->options & (OPTION_MEMORY_MAPPED)) != @@ -1131,7 +1132,14 @@ search->irq == host->irq && search != host); search=search->next); if (!search) { - if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx", NULL)) { +#ifdef __powerpc__ + if (request_irq(host->irq, NCR53c7x0_intr, SA_SHIRQ, "53c7,8xx", NULL)) +#else + if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx", NULL)) +#endif + { + + printk("scsi%d : IRQ%d not free, detaching\n" " You have either a configuration problem, or a\n" " broken BIOS. You may wish to manually assign\n" @@ -1453,6 +1461,21 @@ return -1; } +#ifdef __powerpc__ + if ( ! (command & PCI_COMMAND_MASTER)) { + printk("SCSI: PCI Master Bit has not been set. Setting...\n"); + command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO; + pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); + if (io_port >= 0x10000000) { + /* Mapping on PowerPC can't handle this! */ + unsigned long new_io_port; + new_io_port = (io_port & 0x00FFFFFF) | 0x01000000; + printk("SCSI: I/O moved from %08X to %08x\n", io_port, new_io_port); + io_port = new_io_port; + pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port); + } + } +#endif /* * Bit 0 is the address space indicator and must be one for I/O @@ -1613,6 +1636,9 @@ unsigned char tmp; int i, ncr_to_memory, memory_to_ncr; u32 base; +#ifdef __powerpc__ + unsigned long *script_ptr; +#endif NCR53c7x0_local_setup(host); @@ -1801,6 +1827,14 @@ printk("scsi%d : NCR dsa_fields start is %d not %d\n", host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end - Ent_dsa_zero); +#ifdef __powerpc__ +/* The PowerPC is Big Endian - adjust script appropriately */ + script_ptr = hostdata->script; + for (i = 0; i < sizeof(SCRIPT); i += sizeof(long)) + { + *script_ptr++ = le32_to_cpu(*script_ptr); + } +#endif printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no, virt_to_bus(hostdata->script), hostdata->script); @@ -1884,7 +1918,7 @@ " also verify that the board is jumpered to use PCI INTA, since\n" " most PCI motherboards lack support for INTB, INTC, and INTD.\n" : ""); - else if (hostdata->test_completed != 1) + else if (hostdata->test_completed != 1) printk ("scsi%d : test 1 bad interrupt value (%d)\n", host->host_no, hostdata->test_completed); else @@ -1922,16 +1956,17 @@ cmd[1] = cmd[2] = cmd[3] = cmd[5] = 0; cmd[4] = sizeof(data); - dsa[2] = 1; - dsa[3] = virt_to_bus(&identify); - dsa[4] = 6; - dsa[5] = virt_to_bus(&cmd); - dsa[6] = sizeof(data); - dsa[7] = virt_to_bus(&data); - dsa[8] = 1; - dsa[9] = virt_to_bus(&status); - dsa[10] = 1; - dsa[11] = virt_to_bus(&msg); +/* Need to adjust for endian-ness */ + dsa[2] = le32_to_cpu(1); + dsa[3] = le32_to_cpu(virt_to_bus(&identify)); + dsa[4] = le32_to_cpu(6); + dsa[5] = le32_to_cpu(virt_to_bus(&cmd)); + dsa[6] = le32_to_cpu(sizeof(data)); + dsa[7] = le32_to_cpu(virt_to_bus(&data)); + dsa[8] = le32_to_cpu(1); + dsa[9] = le32_to_cpu(virt_to_bus(&status)); + dsa[10] = le32_to_cpu(1); + dsa[11] = le32_to_cpu(virt_to_bus(&msg)); for (i = 0; i < 3; ++i) { cli(); @@ -1942,7 +1977,7 @@ } /* SCNTL3 SDID */ - dsa[0] = (0x33 << 24) | (i << 16) ; + dsa[0] = le32_to_cpu((0x33 << 24) | (i << 16)) ; hostdata->idle = 0; hostdata->test_running = 2; hostdata->test_completed = -1; @@ -2004,9 +2039,22 @@ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) host->hostdata; int i; +#ifdef __powerpc__ + int len; + unsigned long *dsa_ptr; +#endif memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4), hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template); +#ifdef __powerpc__ + /* Note: the script has already been 'endianized' */ + dsa_ptr = cmd->dsa; + len = hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template; + for (i = 0; i < len; i += sizeof(long)) + { + *dsa_ptr++ = le32_to_cpu(*dsa_ptr); + } +#endif /* * Note : within the NCR 'C' code, dsa points to the _start_ @@ -2046,6 +2094,14 @@ /* XXX - new start stuff */ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr)); +#ifdef __powerpc__ + dsa_ptr = cmd->dsa; + len = hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template; + for (i = 0; i < len; i += sizeof(long)) + { + *dsa_ptr++ = le32_to_cpu(*dsa_ptr); + } +#endif } @@ -2125,7 +2181,7 @@ if (issue_to_cmd (host, hostdata, (u32 *) curr) == cmd) { curr[0] = hostdata->NOP_insn; - curr[1] = 0xdeadbeef; + curr[1] = le32_to_cpu(0xdeadbeef); ++found; break; } @@ -2140,13 +2196,13 @@ */ for (left = host->can_queue, - ncr_search = hostdata->reconnect_dsa_head, + ncr_search = le32_to_cpu(hostdata->reconnect_dsa_head), ncr_prev = &hostdata->reconnect_dsa_head; left >= 0 && ncr_search && ((char*)bus_to_virt(ncr_search) + hostdata->dsa_start) != (char *) cmd->dsa; ncr_prev = (u32*) ((char*)bus_to_virt(ncr_search) + - hostdata->dsa_next), ncr_search = *ncr_prev, --left); + hostdata->dsa_next), ncr_search = le32_to_cpu(*ncr_prev), --left); if (left < 0) printk("scsi%d: loop detected in ncr reconnect list\n", @@ -2491,7 +2547,7 @@ dsps = NCR53c7x0_read32(DSPS_REG); dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG)); - if (hostdata->options & OPTION_DEBUG_INTR) + if (hostdata->options & OPTION_DEBUG_INTR) printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps); switch (dsps) { @@ -2584,9 +2640,9 @@ hostdata->msg_buf[4] = 0; /* 0 offset = async */ asynchronous (host, c->target); } - patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5); + patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, le32_to_cpu(5)); patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1, (u32) - virt_to_bus ((void *)&hostdata->msg_buf)); + le32_to_cpu(virt_to_bus ((void *)&hostdata->msg_buf))); hostdata->dsp = hostdata->script + hostdata->E_respond_message / sizeof(u32); hostdata->dsp_changed = 1; @@ -2649,14 +2705,14 @@ * agrees with this being an untagged queue'd command. */ - patch_dsa_32 (cmd->dsa, dsa_msgout, 0, 1); + patch_dsa_32 (cmd->dsa, dsa_msgout, 0, le32_to_cpu(1)); /* * Modify the table indirect for COMMAND OUT phase, since * Request Sense is a six byte command. */ - patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, 6); + patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, le32_to_cpu(6)); c->cmnd[0] = REQUEST_SENSE; c->cmnd[1] &= 0xe0; /* Zero all but LUN */ @@ -2672,17 +2728,17 @@ */ patch_dsa_32 (cmd->dsa, dsa_dataout, 0, - virt_to_bus(hostdata->script) + hostdata->E_other_transfer); + le32_to_cpu(virt_to_bus(hostdata->script) + hostdata->E_other_transfer)); patch_dsa_32 (cmd->dsa, dsa_datain, 0, - virt_to_bus(cmd->data_transfer_start)); - cmd->data_transfer_start[0] = (((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | - DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer); - cmd->data_transfer_start[1] = (u32) virt_to_bus(c->sense_buffer); - - cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) - << 24) | DBC_TCI_TRUE; - cmd->data_transfer_start[3] = (u32) virt_to_bus(hostdata->script) + - hostdata->E_other_transfer; + le32_to_cpu(virt_to_bus(cmd->data_transfer_start))); + cmd->data_transfer_start[0] = le32_to_cpu((((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | + DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer)); + cmd->data_transfer_start[1] = (u32) le32_to_cpu(virt_to_bus(c->sense_buffer)); + + cmd->data_transfer_start[2] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) + << 24) | DBC_TCI_TRUE); + cmd->data_transfer_start[3] = (u32) le32_to_cpu(virt_to_bus(hostdata->script) + + hostdata->E_other_transfer); /* * Currently, this command is flagged as completed, ie @@ -2691,7 +2747,7 @@ * status, etc are used. */ - cmd->cmd->result = 0xffff; + cmd->cmd->result = le32_to_cpu(0xffff); /* * Restart command as a REQUEST SENSE. @@ -2732,7 +2788,7 @@ host->host_no, NCR53c7x0_read32(DSA_REG), dsa); printk("scsi%d : resume address is 0x%x (virt 0x%p)\n", host->host_no, cmd->saved_data_pointer, - bus_to_virt(cmd->saved_data_pointer)); + bus_to_virt(le32_to_cpu(cmd->saved_data_pointer))); print_insn (host, hostdata->script + Ent_reselected_ok / sizeof(u32), "", 1); printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n", @@ -2765,7 +2821,7 @@ if (dsa) { printk("scsi%d : resume address is 0x%x (virt 0x%p)\n", host->host_no, cmd->saved_data_pointer, - bus_to_virt (cmd->saved_data_pointer)); + bus_to_virt (le32_to_cpu(cmd->saved_data_pointer))); #if 0 printk("scsi%d : template code :\n", host->host_no); for (code = dsa + (Ent_dsa_code_check_reselect - Ent_dsa_zero) @@ -2794,7 +2850,7 @@ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n" " (temp was 0x%x (virt 0x%p))\n", host->host_no, cmd->saved_data_pointer, - bus_to_virt (cmd->saved_data_pointer), + bus_to_virt (le32_to_cpu(cmd->saved_data_pointer)), NCR53c7x0_read32 (TEMP_REG), bus_to_virt (NCR53c7x0_read32(TEMP_REG))); } @@ -2888,7 +2944,7 @@ OPTION_DEBUG_DISCONNECT)) { printk ("scsi%d : saved data pointer 0x%x (virt 0x%p)\n", host->host_no, cmd->saved_data_pointer, - bus_to_virt (cmd->saved_data_pointer)); + bus_to_virt (le32_to_cpu(cmd->saved_data_pointer))); print_progress (c); } return SPECIFIC_INT_RESTART; @@ -2901,11 +2957,11 @@ int size; printk ("scsi%d : restored data pointer 0x%x (virt 0x%p)\n", host->host_no, cmd->saved_data_pointer, bus_to_virt ( - cmd->saved_data_pointer)); + le32_to_cpu(cmd->saved_data_pointer))); size = print_insn (host, (u32 *) - bus_to_virt(cmd->saved_data_pointer), "", 1); + bus_to_virt(le32_to_cpu(cmd->saved_data_pointer)), "", 1); size = print_insn (host, (u32 *) - bus_to_virt(cmd->saved_data_pointer) + size, "", 1); + bus_to_virt(le32_to_cpu(cmd->saved_data_pointer)) + size, "", 1); print_progress (c); } #if 0 @@ -2947,8 +3003,8 @@ (int) NCR53c7x0_read8(SCNTL3_REG_800), datapath_residual (host)) ; print_insn (host, dsp, "", 1); - size = print_insn (host, (u32 *) bus_to_virt(dsp[1]), "", 1); - print_insn (host, (u32 *) bus_to_virt(dsp[1]) + size, "", 1); + size = print_insn (host, (u32 *) bus_to_virt(le32_to_cpu(dsp[1])), "", 1); + print_insn (host, (u32 *) bus_to_virt(le32_to_cpu(dsp[1])) + size, "", 1); } return SPECIFIC_INT_RESTART; #endif @@ -3543,7 +3599,6 @@ case MODE_SELECT: case WRITE_6: case WRITE_10: - case START_STOP: /* also SCAN, which may do DATA OUT */ #if 0 printk("scsi%d : command is ", host->host_no); print_command(cmd->cmnd); @@ -3562,6 +3617,7 @@ * These commands do no data transfer, we should force an * interrupt if a data phase is attempted on them. */ + case START_STOP: /* also SCAN, which may do DATA OUT */ case TEST_UNIT_READY: datain = dataout = 0; break; @@ -3612,8 +3668,8 @@ * will start the data transfer over at the beginning. */ - tmp->saved_data_pointer = virt_to_bus (hostdata->script) + - hostdata->E_data_transfer; + tmp->saved_data_pointer = le32_to_cpu(virt_to_bus (hostdata->script) + + hostdata->E_data_transfer); /* * Initialize Linux specific fields. @@ -3622,9 +3678,9 @@ tmp->cmd = cmd; tmp->next = NULL; tmp->flags = 0; - tmp->dsa_next_addr = virt_to_bus(tmp->dsa) + hostdata->dsa_next - - hostdata->dsa_start; - tmp->dsa_addr = virt_to_bus(tmp->dsa) - hostdata->dsa_start; + tmp->dsa_next_addr = le32_to_cpu(virt_to_bus(tmp->dsa) + hostdata->dsa_next - + hostdata->dsa_start); + tmp->dsa_addr = le32_to_cpu(virt_to_bus(tmp->dsa) - hostdata->dsa_start); /* * Calculate addresses of dynamic code to fill in DSA @@ -3650,8 +3706,8 @@ if (hostdata->dsa_fixup) hostdata->dsa_fixup(tmp); - patch_dsa_32(tmp->dsa, dsa_next, 0, 0); - patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd)); + patch_dsa_32(tmp->dsa, dsa_next, 0, le32_to_cpu(0)); + patch_dsa_32(tmp->dsa, dsa_cmnd, 0, le32_to_cpu(virt_to_bus(cmd))); if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) if (hostdata->sync[cmd->target].select_indirect != @@ -3664,8 +3720,8 @@ } - patch_dsa_32(tmp->dsa, dsa_select, 0, hostdata->sync[cmd->target]. - select_indirect); + patch_dsa_32(tmp->dsa, dsa_select, 0, le32_to_cpu(hostdata->sync[cmd->target]. + select_indirect)); /* * Right now, we'll do the WIDE and SYNCHRONOUS negotiations on * different commands; although it should be trivial to do them @@ -3674,7 +3730,7 @@ if (hostdata->initiate_wdtr & (1 << cmd->target)) { memcpy ((void *) (tmp->select + 1), (void *) wdtr_message, sizeof(wdtr_message)); - patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(wdtr_message)); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, le32_to_cpu(1 + sizeof(wdtr_message))); save_flags(flags); cli(); hostdata->initiate_wdtr &= ~(1 << cmd->target); @@ -3682,7 +3738,7 @@ } else if (hostdata->initiate_sdtr & (1 << cmd->target)) { memcpy ((void *) (tmp->select + 1), (void *) sdtr_message, sizeof(sdtr_message)); - patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(sdtr_message)); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, le32_to_cpu(1 + sizeof(sdtr_message))); tmp->flags |= CMD_FLAG_SDTR; save_flags(flags); cli(); @@ -3695,40 +3751,40 @@ !(hostdata->options & OPTION_NO_ASYNC)) { memcpy ((void *) (tmp->select + 1), (void *) async_message, sizeof(async_message)); - patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message)); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, le32_to_cpu(1 + sizeof(async_message))); tmp->flags |= CMD_FLAG_SDTR; } #endif else - patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, le32_to_cpu(1)); hostdata->talked_to |= (1 << cmd->target); tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ? IDENTIFY (1, cmd->lun) : IDENTIFY (0, cmd->lun); - patch_dsa_32(tmp->dsa, dsa_msgout, 1, virt_to_bus(tmp->select)); - patch_dsa_32(tmp->dsa, dsa_cmdout, 0, cmd->cmd_len); - patch_dsa_32(tmp->dsa, dsa_cmdout, 1, virt_to_bus(cmd->cmnd)); - patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ? + patch_dsa_32(tmp->dsa, dsa_msgout, 1, le32_to_cpu(virt_to_bus(tmp->select))); + patch_dsa_32(tmp->dsa, dsa_cmdout, 0, le32_to_cpu(cmd->cmd_len)); + patch_dsa_32(tmp->dsa, dsa_cmdout, 1, le32_to_cpu(virt_to_bus(cmd->cmnd))); + patch_dsa_32(tmp->dsa, dsa_dataout, 0, le32_to_cpu(cmd_dataout ? virt_to_bus (cmd_dataout) - : virt_to_bus (hostdata->script) + hostdata->E_other_transfer); - patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ? + : virt_to_bus (hostdata->script) + hostdata->E_other_transfer)); + patch_dsa_32(tmp->dsa, dsa_datain, 0, le32_to_cpu(cmd_datain ? virt_to_bus (cmd_datain) - : virt_to_bus (hostdata->script) + hostdata->E_other_transfer); + : virt_to_bus (hostdata->script) + hostdata->E_other_transfer)); /* * XXX - need to make endian aware, should use separate variables * for both status and message bytes. */ - patch_dsa_32(tmp->dsa, dsa_msgin, 0, 1); + patch_dsa_32(tmp->dsa, dsa_msgin, 0, le32_to_cpu(1)); /* * FIXME : these only works for little endian. We probably want to * provide message and status fields in the NCR53c7x0_cmd * structure, and assign them to cmd->result when we're done. */ - patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&cmd->result) + 1); - patch_dsa_32(tmp->dsa, dsa_status, 0, 1); - patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&cmd->result)); - patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1); + patch_dsa_32(tmp->dsa, dsa_msgin, 1, le32_to_cpu(virt_to_bus(&cmd->result) + 1)); + patch_dsa_32(tmp->dsa, dsa_status, 0, le32_to_cpu(1)); + patch_dsa_32(tmp->dsa, dsa_status, 1, le32_to_cpu(virt_to_bus(&cmd->result))); + patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, le32_to_cpu(1)); patch_dsa_32(tmp->dsa, dsa_msgout_other, 1, - virt_to_bus(&(hostdata->NCR53c7xx_msg_nop))); + le32_to_cpu(virt_to_bus(&(hostdata->NCR53c7xx_msg_nop)))); /* * Generate code for zero or more of the DATA IN, DATA OUT phases @@ -3779,15 +3835,15 @@ if (datain) { /* CALL other_in, WHEN NOT DATA_IN */ - cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | + cmd_datain[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | DCMD_TCI_IO) << 24) | - DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; - cmd_datain[1] = virt_to_bus (hostdata->script) + - hostdata->E_other_in; + DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE); + cmd_datain[1] = le32_to_cpu(virt_to_bus (hostdata->script) + + hostdata->E_other_in); /* MOVE count, buf, WHEN DATA_IN */ - cmd_datain[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO) - << 24) | count; - cmd_datain[3] = buf; + cmd_datain[2] = le32_to_cpu(((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO) + << 24) | count); + cmd_datain[3] = le32_to_cpu(buf); #if 0 print_insn (host, cmd_datain, "dynamic ", 1); print_insn (host, cmd_datain + 2, "dynamic ", 1); @@ -3795,14 +3851,14 @@ } if (dataout) { /* CALL other_out, WHEN NOT DATA_OUT */ - cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) | - DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; - cmd_dataout[1] = virt_to_bus(hostdata->script) + - hostdata->E_other_out; + cmd_dataout[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) | + DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE); + cmd_dataout[1] = le32_to_cpu(virt_to_bus(hostdata->script) + + hostdata->E_other_out); /* MOVE count, buf, WHEN DATA+OUT */ - cmd_dataout[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24) - | count; - cmd_dataout[3] = buf; + cmd_dataout[2] = le32_to_cpu(((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24) + | count); + cmd_dataout[3] = le32_to_cpu(buf); #if 0 print_insn (host, cmd_dataout, "dynamic ", 1); print_insn (host, cmd_dataout + 2, "dynamic ", 1); @@ -3817,10 +3873,10 @@ if (datain) { - cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | - DBC_TCI_TRUE; - cmd_datain[1] = virt_to_bus(hostdata->script) + - hostdata->E_other_transfer; + cmd_datain[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | + DBC_TCI_TRUE); + cmd_datain[1] = le32_to_cpu(virt_to_bus(hostdata->script) + + hostdata->E_other_transfer); #if 0 print_insn (host, cmd_datain, "dynamic jump ", 1); #endif @@ -3834,10 +3890,10 @@ } #endif if (dataout) { - cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | - DBC_TCI_TRUE; - cmd_dataout[1] = virt_to_bus(hostdata->script) + - hostdata->E_other_transfer; + cmd_dataout[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | + DBC_TCI_TRUE); + cmd_dataout[1] = le32_to_cpu(virt_to_bus(hostdata->script) + + hostdata->E_other_transfer); #if 0 print_insn (host, cmd_dataout, "dynamic jump ", 1); #endif @@ -3894,26 +3950,25 @@ || hostdata->state == STATE_DISABLED) { printk("scsi%d : disabled or bad target %d lun %d\n", host->host_no, cmd->target, cmd->lun); - cmd->result = (DID_BAD_TARGET << 16); + cmd->result = DID_BAD_TARGET << 16; } else if ((hostdata->options & OPTION_DEBUG_NCOMMANDS_LIMIT) && (hostdata->debug_count_limit == 0)) { printk("scsi%d : maximum commands exceeded\n", host->host_no); - cmd->result = (DID_BAD_TARGET << 16); - cmd->result = (DID_BAD_TARGET << 16); + cmd->result = DID_BAD_TARGET << 16; } else if (hostdata->options & OPTION_DEBUG_READ_ONLY) { switch (cmd->cmnd[0]) { case WRITE_6: case WRITE_10: printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n", host->host_no); - cmd->result = (DID_BAD_TARGET << 16); + cmd->result = DID_BAD_TARGET << 16; } } else { if ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) && hostdata->debug_count_limit != -1) --hostdata->debug_count_limit; restore_flags (flags); - cmd->result = 0xffff; /* The NCR will overwrite message + cmd->result = le32_to_cpu(0xffff); /* The NCR will overwrite message and status with valid data */ cmd->host_scribble = (unsigned char *) tmp = create_cmd (cmd); } @@ -3967,7 +4022,7 @@ int i; NCR53c7x0_local_setup(host); -#if 0 +#if 0 printk("scsi%d : new dsa is 0x%lx (virt 0x%p)\n", host->host_no, virt_to_bus(dsa), dsa); #endif @@ -3982,7 +4037,7 @@ if (hostdata->state == STATE_DISABLED) { printk("scsi%d : driver disabled\n", host->host_no); - tmp->result = (DID_BAD_TARGET << 16); + tmp->result = DID_BAD_TARGET << 16; cmd->next = (struct NCR53c7x0_cmd *) hostdata->free; hostdata->free = cmd; tmp->scsi_done(tmp); @@ -4001,18 +4056,18 @@ /* Restore this instruction to a NOP once the command starts */ cmd->dsa [(hostdata->dsa_jump_dest - hostdata->dsa_start) / - sizeof(u32)] = (u32) virt_to_bus ((void *)curr); + sizeof(u32)] = (u32) le32_to_cpu(virt_to_bus ((void *)curr)); /* Replace the current jump operand. */ curr[1] = - virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin - - hostdata->E_dsa_code_template; + le32_to_cpu(virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin - + hostdata->E_dsa_code_template); /* Replace the NOP instruction with a JUMP */ - curr[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | - DBC_TCI_TRUE; + curr[0] = le32_to_cpu(((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | + DBC_TCI_TRUE); } else { printk ("scsi%d: no free slot\n", host->host_no); disable(host); - tmp->result = (DID_ERROR << 16); + tmp->result = DID_ERROR << 16; cmd->next = (struct NCR53c7x0_cmd *) hostdata->free; hostdata->free = cmd; tmp->scsi_done(tmp); @@ -4101,7 +4156,7 @@ if (hostdata->state == STATE_DISABLED) { tmp = (Scsi_Cmnd *) hostdata->issue_queue; hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr; - tmp->result = (DID_BAD_TARGET << 16); + tmp->result = DID_BAD_TARGET << 16; if (tmp->host_scribble) { ((struct NCR53c7x0_cmd *)tmp->host_scribble)->next = hostdata->free; @@ -4133,6 +4188,7 @@ (struct NCR53c7x0_cmd *) tmp->host_scribble); } else { + tmp->result = le32_to_cpu(tmp->result); if (((tmp->result & 0xff) == 0xff) || ((tmp->result & 0xff00) == 0xff00)) { printk ("scsi%d : danger Will Robinson!\n", @@ -4370,7 +4426,6 @@ char buf[80]; /* Debugging sprintf buffer */ size_t buflen; /* Length of same */ #endif - do { done = 1; for (host = first_host; host; host = host->next) @@ -4449,10 +4504,16 @@ printk ("scsi%d : looking at result of 0x%x\n", host->host_no, cmd->cmd->result); #endif - + +#ifdef __powerpc__ + if (tmp->result == le32_to_cpu(0xffff)) + continue; + tmp->result = le32_to_cpu(tmp->result); +#else if (((tmp->result & 0xff) == 0xff) || ((tmp->result & 0xff00) == 0xff00)) continue; +#endif search_found = 1; @@ -4537,7 +4598,6 @@ printk("scsi%d : no active command\n", host->host_no); } } - if (istat & ISTAT_SIP) { if (hostdata->options & OPTION_DEBUG_INTR) printk ("scsi%d : ISTAT_SIP\n", host->host_no); @@ -4872,12 +4932,12 @@ * from normal dynamic code. */ if (dsp != cmd->residual + 2) { - cmd->residual[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | + cmd->residual[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | ((dcmd & DCMD_BMI_IO) ? DCMD_TCI_IO : 0)) << 24) | - DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; - cmd->residual[1] = virt_to_bus(hostdata->script) + DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE); + cmd->residual[1] = le32_to_cpu(virt_to_bus(hostdata->script) + ((dcmd & DCMD_BMI_IO) - ? hostdata->E_other_in : hostdata->E_other_out); + ? hostdata->E_other_in : hostdata->E_other_out)); } /* @@ -4885,17 +4945,17 @@ * move instruction, reflecting the pointer and count at the * time of the phase mismatch. */ - cmd->residual[2] = dbc_dcmd + residual; - cmd->residual[3] = NCR53c7x0_read32(DNAD_REG) - residual; + cmd->residual[2] = le32_to_cpu(dbc_dcmd + residual); + cmd->residual[3] = le32_to_cpu(NCR53c7x0_read32(DNAD_REG) - residual); /* * The third and final instruction is a jump to the instruction * which follows the instruction which had to be 'split' */ if (dsp != cmd->residual + 2) { - cmd->residual[4] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) - << 24) | DBC_TCI_TRUE; - cmd->residual[5] = virt_to_bus(dsp_next); + cmd->residual[4] = le32_to_cpu(((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) + << 24) | DBC_TCI_TRUE); + cmd->residual[5] = le32_to_cpu(virt_to_bus(dsp_next)); } /* @@ -5375,11 +5435,11 @@ */ sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)", (prefix ? prefix : ""), virt_to_bus((void *) insn), insn, - insn[0], insn[1], bus_to_virt (insn[1])); + insn[0], insn[1], bus_to_virt (le32_to_cpu(insn[1]))); tmp = buf + strlen(buf); if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) { sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2], - bus_to_virt(insn[2])); + bus_to_virt(le32_to_cpu(insn[2]))); size = 3; } else { sprintf (tmp, "\n"); @@ -5439,6 +5499,7 @@ struct NCR53c7x0_hostdata *hostdata = host ? (struct NCR53c7x0_hostdata *) host->hostdata : NULL; unsigned long flags; + unsigned long result; struct NCR53c7x0_cmd *curr, **prev; Scsi_Cmnd *me, **last; #if 0 @@ -5530,7 +5591,8 @@ &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next); if (curr) { - if ((cmd->result & 0xff) != 0xff && (cmd->result & 0xff00) != 0xff00) { + result = le32_to_cpu(cmd->result); + if ((result & 0xff) != 0xff && (result & 0xff00) != 0xff00) { if (prev) *prev = (struct NCR53c7x0_cmd *) curr->next; curr->next = (struct NCR53c7x0_cmd *) hostdata->free; @@ -5561,8 +5623,9 @@ cmd->host_scribble = NULL; } - if (((cmd->result & 0xff00) == 0xff00) || - ((cmd->result & 0xff) == 0xff)) { + result = le32_to_cpu(cmd->result); + if (((result & 0xff00) == 0xff00) || + ((result & 0xff) == 0xff)) { printk ("scsi%d : did this command ever run?\n", host->host_no); cmd->result = DID_ABORT << 16; } else { @@ -5709,7 +5772,7 @@ (insn >= ncmd->residual && insn < (ncmd->residual + sizeof(ncmd->residual))))) { - ptr = bus_to_virt(insn[3]); + ptr = bus_to_virt(le32_to_cpu(insn[3])); if ((buffers = cmd->use_sg)) { for (offset = 0, @@ -5764,7 +5827,7 @@ continue; if (!i) { where = "saved"; - ptr = bus_to_virt(ncmd->saved_data_pointer); + ptr = bus_to_virt(le32_to_cpu(ncmd->saved_data_pointer)); } else { where = "active"; ptr = bus_to_virt (NCR53c7x0_read32 (DSP_REG) - @@ -5782,9 +5845,9 @@ cmd->host->host_no, where); if (ncmd) { size = print_insn (cmd->host, - bus_to_virt(ncmd->saved_data_pointer), "", 1); + bus_to_virt(le32_to_cpu(ncmd->saved_data_pointer)), "", 1); print_insn (cmd->host, - bus_to_virt(ncmd->saved_data_pointer) + size * sizeof(u32), + bus_to_virt(le32_to_cpu(ncmd->saved_data_pointer)) + size * sizeof(u32), "", 1); } } @@ -5809,9 +5872,9 @@ " + %d : dsa_msgout length = %u, data = 0x%x (virt 0x%p)\n" , prefix ? prefix : "", host->host_no, virt_to_bus (dsa), dsa, hostdata->dsa_msgout, - dsa[hostdata->dsa_msgout / sizeof(u32)], - dsa[hostdata->dsa_msgout / sizeof(u32) + 1], - bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1])); + le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32)]), + le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32) + 1]), + bus_to_virt (le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32) + 1]))); /* * Only print messages if they're sane in length so we don't @@ -5819,10 +5882,10 @@ * anything. */ - if (dsa[hostdata->dsa_msgout / sizeof(u32)] < + if (le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32)]) < sizeof (hostdata->free->select)) - for (i = dsa[hostdata->dsa_msgout / sizeof(u32)], - ptr = bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]); + for (i = le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32)]), + ptr = bus_to_virt (le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32) + 1])); i > 0 && !check_address ((unsigned long) ptr, 1); ptr += len, i -= len) { printk(" "); @@ -5833,8 +5896,8 @@ } printk(" + %d : select_indirect = 0x%x\n", - hostdata->dsa_select, dsa[hostdata->dsa_select / sizeof(u32)]); - cmd = (Scsi_Cmnd *) bus_to_virt(dsa[hostdata->dsa_cmnd / sizeof(u32)]); + hostdata->dsa_select, le32_to_cpu(dsa[hostdata->dsa_select / sizeof(u32)])); + cmd = (Scsi_Cmnd *) bus_to_virt(le32_to_cpu(dsa[hostdata->dsa_cmnd / sizeof(u32)])); printk(" + %d : dsa_cmnd = 0x%x ", hostdata->dsa_cmnd, (u32) virt_to_bus(cmd)); if (cmd) { @@ -5844,7 +5907,7 @@ } else printk("\n"); printk(" + %d : dsa_next = 0x%x\n", hostdata->dsa_next, - dsa[hostdata->dsa_next / sizeof(u32)]); + le32_to_cpu(dsa[hostdata->dsa_next / sizeof(u32)])); if (cmd) { printk("scsi%d target %d : sxfer_sanity = 0x%x, scntl3_sanity = 0x%x\n" " script : ", @@ -5891,8 +5954,7 @@ host->host_no, cmd->pid); /* print_dsa does sanity check on address, no need to check */ else - print_dsa (host, ((struct NCR53c7x0_cmd *) cmd->host_scribble) - -> dsa, ""); + print_dsa (host, le32_to_cpu(((struct NCR53c7x0_cmd *) cmd->host_scribble)-> dsa), ""); } else printk ("scsi%d : scsi pid %ld for target %d lun %d has no NCR53c7x0_cmd\n", host->host_no, cmd->pid, cmd->target, cmd->lun); @@ -5917,7 +5979,7 @@ left > 0; curr += 2, --left) if (curr[0] != hostdata->NOP_insn) /* FIXME : convert pointer to dsa_begin to pointer to dsa. */ - print_dsa (host, bus_to_virt (curr[1] - + print_dsa (host, bus_to_virt (le32_to_cpu(curr[1]) - (hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template)), ""); printk ("scsi%d : end schedule dsa array\n", host->host_no); @@ -5925,7 +5987,7 @@ printk ("scsi%d : reconnect_dsa_head :\n", host->host_no); for (left = host->can_queue, - dsa = bus_to_virt (hostdata->reconnect_dsa_head); + dsa = bus_to_virt (le32_to_cpu(hostdata->reconnect_dsa_head)); left >= 0 && dsa; dsa = next_dsa) { save_flags (flags); @@ -5937,7 +5999,7 @@ } else { - next_dsa = bus_to_virt(dsa[hostdata->dsa_next / sizeof(u32)]); + next_dsa = bus_to_virt(le32_to_cpu(dsa[hostdata->dsa_next / sizeof(u32)])); print_dsa (host, dsa, ""); } restore_flags(flags); @@ -6131,7 +6193,7 @@ for (i = 0, curr = (u32 *) hostdata->schedule; i < host->can_queue; ++i, curr += 2) { curr[0] = hostdata->NOP_insn; - curr[1] = 0xdeadbeef; + curr[1] = le32_to_cpu(0xdeadbeef); } hostdata->curr = NULL; } diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/53c7,8xx.h linux/drivers/scsi/53c7,8xx.h --- v2.1.47/linux/drivers/scsi/53c7,8xx.h Mon Jul 7 16:02:51 1997 +++ linux/drivers/scsi/53c7,8xx.h Mon Aug 4 14:00:47 1997 @@ -1574,7 +1574,7 @@ if (hostdata->options & OPTION_DEBUG_DSA) \ printk("scsi : dsa %s symbol %s(%d) word %d now 0x%x\n", \ #dsa, #symbol, hostdata->##symbol, \ - (word), (u32) (value)); \ + (word), (u32) le32_to_cpu(value)); \ } /* Paranoid people could use panic() here. */ diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.1.47/linux/drivers/scsi/Config.in Tue May 13 22:41:12 1997 +++ linux/drivers/scsi/Config.in Sun Aug 3 19:39:57 1997 @@ -21,7 +21,14 @@ dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI -dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI +dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI +if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then + bool ' Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y + int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8 + bool ' Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N + bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N + int ' delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15 +fi dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.1.47/linux/drivers/scsi/Makefile Sat May 24 09:10:24 1997 +++ linux/drivers/scsi/Makefile Thu Jul 31 12:36:27 1997 @@ -421,12 +421,8 @@ aha152x.o: aha152x.c $(CC) $(CFLAGS) $(AHA152X) -c aha152x.c -aic7xxx_asm: aic7xxx_asm.c - $(HOSTCC) -o $@ aic7xxx_asm.c - -aic7xxx.c: aic7xxx_seq.h -aic7xxx_seq.h: aic7xxx_asm aic7xxx.seq - ./aic7xxx_asm -o $@ aic7xxx.seq +aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h + $(CC) $(CFLAGS) -c -o $@ aic7xxx.c seagate.o: seagate.c $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY -c seagate.c diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx --- v2.1.47/linux/drivers/scsi/README.aic7xxx Sat Apr 20 10:59:10 1996 +++ linux/drivers/scsi/README.aic7xxx Thu Jul 31 12:37:17 1997 @@ -1,5 +1,5 @@ AIC7xxx Driver for Linux - April 15, 1996 + July 20, 1997 Introduction ------------------------ @@ -31,6 +31,7 @@ ----------------------- AIC-777x AIC-785x + AIC-786x AIC-787x AIC-788x @@ -58,6 +59,7 @@ Dan Eischen deischen@iworks.InterWorks.org (Linux Driver Co-maintainer) Dean Gehnert deang@teleport.com (Linux FTP/patch maintainer) Jess Johnson jester@frenzy.com (AIC7xxx FAQ author) + Doug Ledford dledford@dialnet.net (Stress tester/bug squasher) Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original author of the driver. John has since retired from the project. Thanks @@ -82,10 +84,17 @@ Send regular messages and replies to: AIC7xxx@FreeBSD.ORG - Command line options - ------------------------ + Command line options ("aic7xxx=option[,option...]") + --------------------------------------------------- "aic7xxx=no_reset" - Eliminate the SCSI reset delay during startup. Some SCSI devices need some extra time to reset. + "aic7xxx=extended" - Force extended translation. + "aic7xxx=ultra" - Force Ultra mode + "aic7xxx=irq_trigger:[0,1]" - Edge (0) or Level (1) triggered + interrupts. AFAIK, the driver only works with level triggered + interrupts. This only applies to EISA adapters. + "aic7xxx=verbose" - Enable more bootup messages. PLEASE use this + if you have problems with the driver. /proc support ------------------------ @@ -106,10 +115,37 @@ - US Linux mirror of Teleport site ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/ - European Linux mirror of Teleport site + ftp://ftp.pcnet.com/users/eischen/Linux/ + - Daniel Eischens experimental/development ftp site that is + also home of the Linux aic7xxx sequencer assembler source. + + Sequencer assembler + ------------------------ + The sequencer assembler is no longer being distributed with the + Linux kernel. The sequencer assembler (aic7xxx_asm) is now being + maintained by Justin Gibbs under a BSD copyright (which pretty + much lets you do anything you want with it). I keep a Linux + version of the assembler at my ftp site should you wish to hack + the sequencer code (ftp://ftp.pcnet.com/users/eischen/Linux/). + Please note that you do NOT need the assembler to build a kernel + with aic7xxx support. The assembler generates the code that is + downloaded to the aic7xxx controllers; this code IS part of the + Linux kernel (aic7xxx_seq.h and aic7xxx_reg.h). + + Problems compiling the kernel with aic7xxx support + -------------------------------------------------- + This is probably due to having modified the sequencer files in + some way. If you are not modifying the sequencer source (in + drivers/scsi/aic7xxx/aic7xxx.seq), then you can just re-extract + the necessary files from your kernel tarball. Otherwise, visit + my anonymous ftp site (ftp.pcnet.com) and grab the sequencer + assembler source. Dean W. Gehnert deang@teleport.com -$Revision: 3.0 $ +(Modified by D. Eischen, 7/20/97) + +$Revision: 3.1a $ diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg --- v2.1.47/linux/drivers/scsi/aic7xxx/aic7xxx.reg Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx/aic7xxx.reg Thu Jul 31 12:37:17 1997 @@ -0,0 +1,1120 @@ +/* + * Aic7xxx register and scratch ram definitions. + * + * Copyright (c) 1994-1997 Justin Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: aic7xxx.reg,v 1.4 1997/06/27 19:38:39 gibbs Exp $ + */ + +/* + * This file is processed by the aic7xxx_asm utility for use in assembling + * firmware for the aic7xxx family of SCSI host adapters as well as to generate + * a C header file for use in the kernel portion of the Aic7xxx driver. + * + * All page numbers refer to the Adaptec AIC-7770 Data Book available from + * Adaptec's Technical Documents Department 1-800-934-2766 + */ + +/* + * SCSI Sequence Control (p. 3-11). + * Each bit, when set starts a specific SCSI sequence on the bus + */ +register SCSISEQ { + address 0x000 + access_mode RW + bit TEMODE 0x80 + bit ENSELO 0x40 + bit ENSELI 0x20 + bit ENRSELI 0x10 + bit ENAUTOATNO 0x08 + bit ENAUTOATNI 0x04 + bit ENAUTOATNP 0x02 + bit SCSIRSTO 0x01 +} + +/* + * SCSI Transfer Control 0 Register (pp. 3-13). + * Controls the SCSI module data path. + */ +register SXFRCTL0 { + address 0x001 + access_mode RW + bit DFON 0x80 + bit DFPEXP 0x40 + bit FAST20 0x20 + bit CLRSTCNT 0x10 + bit SPIOEN 0x08 + bit SCAMEN 0x04 + bit CLRCHN 0x02 +} + +/* + * SCSI Transfer Control 1 Register (pp. 3-14,15). + * Controls the SCSI module data path. + */ +register SXFRCTL1 { + address 0x002 + access_mode RW + bit BITBUCKET 0x80 + bit SWRAPEN 0x40 + bit ENSPCHK 0x20 + mask STIMESEL 0x18 + bit ENSTIMER 0x04 + bit ACTNEGEN 0x02 + bit STPWEN 0x01 /* Powered Termination */ +} + +/* + * SCSI Control Signal Read Register (p. 3-15). + * Reads the actual state of the SCSI bus pins + */ +register SCSISIGI { + address 0x003 + access_mode RO + bit CDI 0x80 + bit IOI 0x40 + bit MSGI 0x20 + bit ATNI 0x10 + bit SELI 0x08 + bit BSYI 0x04 + bit REQI 0x02 + bit ACKI 0x01 +/* + * Possible phases in SCSISIGI + */ + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI +} + +/* + * SCSI Control Signal Write Register (p. 3-16). + * Writing to this register modifies the control signals on the bus. Only + * those signals that are allowed in the current mode (Initiator/Target) are + * asserted. + */ +register SCSISIGO { + address 0x003 + access_mode WO + bit CDO 0x80 + bit IOO 0x40 + bit MSGO 0x20 + bit ATNO 0x10 + bit SELO 0x08 + bit BSYO 0x04 + bit REQO 0x02 + bit ACKO 0x01 +/* + * Possible phases to write into SCSISIG0 + */ + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI +} + +/* + * SCSI Rate Control (p. 3-17). + * Contents of this register determine the Synchronous SCSI data transfer + * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the + * SOFS (3:0) bits disables synchronous data transfers. Any offset value + * greater than 0 enables synchronous transfers. + */ +register SCSIRATE { + address 0x004 + access_mode RW + bit WIDEXFER 0x80 /* Wide transfer control */ + mask SXFR 0x70 /* Sync transfer rate */ + mask SOFS 0x0f /* Sync offset */ +} + +/* + * SCSI ID (p. 3-18). + * Contains the ID of the board and the current target on the + * selected channel. + */ +register SCSIID { + address 0x005 + access_mode RW + mask TID 0xf0 /* Target ID mask */ + mask OID 0x0f /* Our ID mask */ +} + +/* + * SCSI Latched Data (p. 3-19). + * Read/Write latches used to transfer data on the SCSI bus during + * Automatic or Manual PIO mode. SCSIDATH can be used for the + * upper byte of a 16bit wide asynchronouse data phase transfer. + */ +register SCSIDATL { + address 0x006 + access_mode RW +} + +register SCSIDATH { + address 0x007 + access_mode RW +} + +/* + * SCSI Transfer Count (pp. 3-19,20) + * These registers count down the number of bytes transferred + * across the SCSI bus. The counter is decremented only once + * the data has been safely transferred. SDONE in SSTAT0 is + * set when STCNT goes to 0 + */ +register STCNT { + address 0x008 + size 3 + access_mode RW +} + +/* + * Clear SCSI Interrupt 0 (p. 3-20) + * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0. + */ +register CLRSINT0 { + address 0x00b + access_mode WO + bit CLRSELDO 0x40 + bit CLRSELDI 0x20 + bit CLRSELINGO 0x10 + bit CLRSWRAP 0x08 + bit CLRSPIORDY 0x02 +} + +/* + * SCSI Status 0 (p. 3-21) + * Contains one set of SCSI Interrupt codes + * These are most likely of interest to the sequencer + */ +register SSTAT0 { + address 0x00b + access_mode RO + bit TARGET 0x80 /* Board acting as target */ + bit SELDO 0x40 /* Selection Done */ + bit SELDI 0x20 /* Board has been selected */ + bit SELINGO 0x10 /* Selection In Progress */ + bit SWRAP 0x08 /* 24bit counter wrap */ + bit SDONE 0x04 /* STCNT = 0x000000 */ + bit SPIORDY 0x02 /* SCSI PIO Ready */ + bit DMADONE 0x01 /* DMA transfer completed */ +} + +/* + * Clear SCSI Interrupt 1 (p. 3-23) + * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1. + */ +register CLRSINT1 { + address 0x00c + access_mode WO + bit CLRSELTIMEO 0x80 + bit CLRATNO 0x40 + bit CLRSCSIRSTI 0x20 + bit CLRBUSFREE 0x08 + bit CLRSCSIPERR 0x04 + bit CLRPHASECHG 0x02 + bit CLRREQINIT 0x01 +} + +/* + * SCSI Status 1 (p. 3-24) + */ +register SSTAT1 { + address 0x00c + access_mode RO + bit SELTO 0x80 + bit ATNTARG 0x40 + bit SCSIRSTI 0x20 + bit PHASEMIS 0x10 + bit BUSFREE 0x08 + bit SCSIPERR 0x04 + bit PHASECHG 0x02 + bit REQINIT 0x01 +} + +/* + * SCSI Status 2 (pp. 3-25,26) + */ +register SSTAT2 { + address 0x00d + access_mode RO + bit OVERRUN 0x80 + mask SFCNT 0x1f +} + +/* + * SCSI Status 3 (p. 3-26) + */ +register SSTAT3 { + address 0x00e + access_mode RO + mask SCSICNT 0xf0 + mask OFFCNT 0x0f +} + +/* + * SCSI Test Control (p. 3-27) + */ +register SCSITEST { + address 0x00f + access_mode RW + bit RQAKCNT 0x04 + bit CNTRTEST 0x02 + bit CMODE 0x01 +} + +/* + * SCSI Interrupt Mode 1 (p. 3-28) + * Setting any bit will enable the corresponding function + * in SIMODE0 to interrupt via the IRQ pin. + */ +register SIMODE0 { + address 0x010 + access_mode RW + bit ENSELDO 0x40 + bit ENSELDI 0x20 + bit ENSELINGO 0x10 + bit ENSWRAP 0x08 + bit ENSDONE 0x04 + bit ENSPIORDY 0x02 + bit ENDMADONE 0x01 +} + +/* + * SCSI Interrupt Mode 1 (pp. 3-28,29) + * Setting any bit will enable the corresponding function + * in SIMODE1 to interrupt via the IRQ pin. + */ +register SIMODE1 { + address 0x011 + access_mode RW + bit ENSELTIMO 0x80 + bit ENATNTARG 0x40 + bit ENSCSIRST 0x20 + bit ENPHASEMIS 0x10 + bit ENBUSFREE 0x08 + bit ENSCSIPERR 0x04 + bit ENPHASECHG 0x02 + bit ENREQINIT 0x01 +} + +/* + * SCSI Data Bus (High) (p. 3-29) + * This register reads data on the SCSI Data bus directly. + */ +register SCSIBUSL { + address 0x012 + access_mode RO +} + +register SCSIBUSH { + address 0x013 + access_mode RO +} + +/* + * SCSI/Host Address (p. 3-30) + * These registers hold the host address for the byte about to be + * transferred on the SCSI bus. They are counted up in the same + * manner as STCNT is counted down. SHADDR should always be used + * to determine the address of the last byte transferred since HADDR + * can be skewed by write ahead. + */ +register SHADDR { + address 0x014 + size 4 + access_mode RO +} + +/* + * Selection Timeout Timer (p. 3-30) + */ +register SELTIMER { + address 0x018 + access_mode RW + bit STAGE6 0x20 + bit STAGE5 0x10 + bit STAGE4 0x08 + bit STAGE3 0x04 + bit STAGE2 0x02 + bit STAGE1 0x01 +} + +/* + * Selection/Reselection ID (p. 3-31) + * Upper four bits are the device id. The ONEBIT is set when the re/selecting + * device did not set its own ID. + */ +register SELID { + address 0x019 + access_mode RW + mask SELID_MASK 0xf0 + bit ONEBIT 0x08 +} + +/* + * SCSI Block Control (p. 3-32) + * Controls Bus type and channel selection. In a twin channel configuration + * addresses 0x00-0x1e are gated to the appropriate channel based on this + * register. SELWIDE allows for the coexistence of 8bit and 16bit devices + * on a wide bus. + */ +register SBLKCTL { + address 0x01f + access_mode RW + bit DIAGLEDEN 0x80 /* Aic78X0 only */ + bit DIAGLEDON 0x40 /* Aic78X0 only */ + bit AUTOFLUSHDIS 0x20 + bit SELBUSB 0x08 + bit SELWIDE 0x02 +} + +/* + * Sequencer Control (p. 3-33) + * Error detection mode and speed configuration + */ +register SEQCTL { + address 0x060 + access_mode RW + bit PERRORDIS 0x80 + bit PAUSEDIS 0x40 + bit FAILDIS 0x20 + bit FASTMODE 0x10 + bit BRKADRINTEN 0x08 + bit STEP 0x04 + bit SEQRESET 0x02 + bit LOADRAM 0x01 +} + +/* + * Sequencer RAM Data (p. 3-34) + * Single byte window into the Scratch Ram area starting at the address + * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write + * four bytes in sucessesion. The SEQADDRs will increment after the most + * significant byte is written + */ +register SEQRAM { + address 0x061 + access_mode RW +} + +/* + * Sequencer Address Registers (p. 3-35) + * Only the first bit of SEQADDR1 holds addressing information + */ +register SEQADDR0 { + address 0x062 + access_mode RW +} + +register SEQADDR1 { + address 0x063 + access_mode RW + mask SEQADDR1_MASK 0x01 +} + +/* + * Accumulator + * We cheat by passing arguments in the Accumulator up to the kernel driver + */ +register ACCUM { + address 0x064 + access_mode RW + accumulator +} + +register SINDEX { + address 0x065 + access_mode RW + sindex +} + +register DINDEX { + address 0x066 + access_mode RW +} + +register ALLONES { + address 0x069 + access_mode RO + allones +} + +register ALLZEROS { + address 0x06a + access_mode RO + allzeros +} + +register NONE { + address 0x06a + access_mode WO + none +} + +register FLAGS { + address 0x06b + access_mode RO + bit ZERO 0x02 + bit CARRY 0x01 +} + +register SINDIR { + address 0x06c + access_mode RO +} + +register DINDIR { + address 0x06d + access_mode WO +} + +register FUNCTION1 { + address 0x06e + access_mode RW +} + +register STACK { + address 0x06f + access_mode RO +} + +/* + * Board Control (p. 3-43) + */ +register BCTL { + address 0x084 + access_mode RW + bit ACE 0x08 + bit ENABLE 0x01 +} + +/* + * On the aic78X0 chips, Board Control is replaced by the DSCommand + * register (p. 4-64) + */ +register DSCOMMAND { + address 0x084 + access_mode RW + bit CACHETHEN 0x80 /* Cache Threshold enable */ + bit DPARCKEN 0x40 /* Data Parity Check Enable */ + bit MPARCKEN 0x20 /* Memory Parity Check Enable */ + bit EXTREQLCK 0x10 /* External Request Lock */ +} + +/* + * Bus On/Off Time (p. 3-44) + */ +register BUSTIME { + address 0x085 + access_mode RW + mask BOFF 0xf0 + mask BON 0x0f +} + +/* + * Bus Speed (p. 3-45) + */ +register BUSSPD { + address 0x086 + access_mode RW + mask DFTHRSH 0xc0 + mask STBOFF 0x38 + mask STBON 0x07 + mask DFTHRSH_100 0xc0 +} + +/* + * Host Control (p. 3-47) R/W + * Overall host control of the device. + */ +register HCNTRL { + address 0x087 + access_mode RW + bit POWRDN 0x40 + bit SWINT 0x10 + bit IRQMS 0x08 + bit PAUSE 0x04 + bit INTEN 0x02 + bit CHIPRST 0x01 + bit CHIPRSTACK 0x01 +} + +/* + * Host Address (p. 3-48) + * This register contains the address of the byte about + * to be transferred across the host bus. + */ +register HADDR { + address 0x088 + size 4 + access_mode RW +} + +register HCNT { + address 0x08c + size 3 + access_mode RW +} + +/* + * SCB Pointer (p. 3-49) + * Gate one of the four SCBs into the SCBARRAY window. + */ +register SCBPTR { + address 0x090 + access_mode RW +} + +/* + * Interrupt Status (p. 3-50) + * Status for system interrupts + */ +register INTSTAT { + address 0x091 + access_mode RW + bit BRKADRINT 0x08 + bit SCSIINT 0x04 + bit CMDCMPLT 0x02 + bit SEQINT 0x01 + mask BAD_PHASE SEQINT /* unknown scsi bus phase */ + mask SEND_REJECT 0x10|SEQINT /* sending a message reject */ + mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/ + mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */ + mask EXTENDED_MSG 0x40|SEQINT /* Extended message received */ + mask NO_MATCH_BUSY 0x50|SEQINT /* Couldn't find BUSY SCB */ + mask REJECT_MSG 0x60|SEQINT /* Reject message received */ + mask BAD_STATUS 0x70|SEQINT /* Bad status from target */ + mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */ + mask ABORT_CMDCMPLT 0x91 /* + * Command tagged for abort + * completed successfully. + */ + mask AWAITING_MSG 0xa0|SEQINT /* + * Kernel requested to specify + * a message to this target + * (command was null), so tell + * it that it can fill the + * message buffer. + */ + mask MSG_BUFFER_BUSY 0xc0|SEQINT /* + * Sequencer wants to use the + * message buffer, but it + * already contains a message + */ + mask MSGIN_PHASEMIS 0xd0|SEQINT /* + * Target changed phase on us + * when we were expecting + * another msgin byte. + */ + mask DATA_OVERRUN 0xe0|SEQINT /* + * Target attempted to write + * beyond the bounds of its + * command. + */ + + mask SEQINT_MASK 0xf0|SEQINT /* SEQINT Status Codes */ + mask INT_PEND (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT) +} + +/* + * Hard Error (p. 3-53) + * Reporting of catastrophic errors. You usually cannot recover from + * these without a full board reset. + */ +register ERROR { + address 0x092 + access_mode RO + bit PARERR 0x08 + bit ILLOPCODE 0x04 + bit ILLSADDR 0x02 + bit ILLHADDR 0x01 +} + +/* + * Clear Interrupt Status (p. 3-52) + */ +register CLRINT { + address 0x092 + access_mode WO + bit CLRBRKADRINT 0x08 + bit CLRSCSIINT 0x04 + bit CLRCMDINT 0x02 + bit CLRSEQINT 0x01 +} + +register DFCNTRL { + address 0x093 + access_mode RW + bit WIDEODD 0x40 + bit SCSIEN 0x20 + bit SDMAEN 0x10 + bit SDMAENACK 0x10 + bit HDMAEN 0x08 + bit HDMAENACK 0x08 + bit DIRECTION 0x04 + bit FIFOFLUSH 0x02 + bit FIFORESET 0x01 +} + +register DFSTATUS { + address 0x094 + access_mode RO + bit DWORDEMP 0x20 + bit MREQPEND 0x10 + bit HDONE 0x08 + bit DFTHRESH 0x04 + bit FIFOFULL 0x02 + bit FIFOEMP 0x01 +} + +register DFDAT { + address 0x099 + access_mode RW +} + +/* + * SCB Auto Increment (p. 3-59) + * Byte offset into the SCB Array and an optional bit to allow auto + * incrementing of the address during download and upload operations + */ +register SCBCNT { + address 0x09a + access_mode RW + bit SCBAUTO 0x80 + mask SCBCNT_MASK 0x1f +} + +/* + * Queue In FIFO (p. 3-60) + * Input queue for queued SCBs (commands that the seqencer has yet to start) + */ +register QINFIFO { + address 0x09b + access_mode RW +} + +/* + * Queue In Count (p. 3-60) + * Number of queued SCBs + */ +register QINCNT { + address 0x09c + access_mode RO +} + +/* + * Queue Out FIFO (p. 3-61) + * Queue of SCBs that have completed and await the host + */ +register QOUTFIFO { + address 0x09d + access_mode WO +} + +/* + * Queue Out Count (p. 3-61) + * Number of queued SCBs in the Out FIFO + */ +register QOUTCNT { + address 0x09e + access_mode RO +} + +/* + * SCB Definition (p. 5-4) + */ +scb { + address 0x0a0 + SCB_CONTROL { + size 1 + bit MK_MESSAGE 0x80 + bit DISCENB 0x40 + bit TAG_ENB 0x20 + bit MUST_DMAUP_SCB 0x10 + bit ABORT_SCB 0x08 + bit DISCONNECTED 0x04 + mask SCB_TAG_TYPE 0x03 + } + SCB_TCL { + size 1 + bit SELBUSB 0x08 + mask TID 0xf0 + mask LID 0x07 + } + SCB_TARGET_STATUS { + size 1 + } + SCB_SGCOUNT { + size 1 + } + SCB_SGPTR { + size 4 + } + SCB_RESID_SGCNT { + size 1 + } + SCB_RESID_DCNT { + size 3 + } + SCB_DATAPTR { + size 4 + } + SCB_DATACNT { + size 3 + } + SCB_LINKED_NEXT { + size 1 + } + SCB_CMDPTR { + size 4 + } + SCB_CMDLEN { + size 1 + } + SCB_TAG { + size 1 + } + SCB_NEXT { + size 1 + } + SCB_PREV { + size 1 + } + SCB_BUSYTARGETS { + size 4 + } +} + +const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */ + +/* --------------------- AHA-2840-only definitions -------------------- */ + +register SEECTL_2840 { + address 0x0c0 + access_mode RW + bit CS_2840 0x04 + bit CK_2840 0x02 + bit DO_2840 0x01 +} + +register STATUS_2840 { + address 0x0c1 + access_mode RW + bit EEPROM_TF 0x80 + mask BIOS_SEL 0x60 + mask ADSEL 0x1e + bit DI_2840 0x01 +} + +/* --------------------- AIC-7870-only definitions -------------------- */ + +register DSPCISTATUS { + address 0x086 +} + +register BRDCTL { + address 0x01d + bit BRDDAT7 0x80 + bit BRDDAT6 0x40 + bit BRDDAT5 0x20 + bit BRDSTB 0x10 + bit BRDCS 0x08 + bit BRDRW 0x04 + bit BRDCTL1 0x02 + bit BRDCTL0 0x01 +} + +/* + * Serial EEPROM Control (p. 4-92 in 7870 Databook) + * Controls the reading and writing of an external serial 1-bit + * EEPROM Device. In order to access the serial EEPROM, you must + * first set the SEEMS bit that generates a request to the memory + * port for access to the serial EEPROM device. When the memory + * port is not busy servicing another request, it reconfigures + * to allow access to the serial EEPROM. When this happens, SEERDY + * gets set high to verify that the memory port access has been + * granted. + * + * After successful arbitration for the memory port, the SEECS bit of + * the SEECTL register is connected to the chip select. The SEECK, + * SEEDO, and SEEDI are connected to the clock, data out, and data in + * lines respectively. The SEERDY bit of SEECTL is useful in that it + * gives us an 800 nsec timer. After a write to the SEECTL register, + * the SEERDY goes high 800 nsec later. The one exception to this is + * when we first request access to the memory port. The SEERDY goes + * high to signify that access has been granted and, for this case, has + * no implied timing. + * + * See 93cx6.c for detailed information on the protocol necessary to + * read the serial EEPROM. + */ +register SEECTL { + address 0x01e + bit EXTARBACK 0x80 + bit EXTARBREQ 0x40 + bit SEEMS 0x20 + bit SEERDY 0x10 + bit SEECS 0x08 + bit SEECK 0x04 + bit SEEDO 0x02 + bit SEEDI 0x01 +} +/* ---------------------- Scratch RAM Offsets ------------------------- */ +/* These offsets are either to values that are initialized by the board's + * BIOS or are specified by the sequencer code. + * + * The host adapter card (at least the BIOS) uses 20-2f for SCSI + * device information, 32-33 and 5a-5f as well. As it turns out, the + * BIOS trashes 20-2f, writing the synchronous negotiation results + * on top of the BIOS values, so we re-use those for our per-target + * scratchspace (actually a value that can be copied directly into + * SCSIRATE). The kernel driver will enable synchronous negotiation + * for all targets that have a value other than 0 in the lower four + * bits of the target scratch space. This should work regardless of + * whether the bios has been installed. + */ + +scratch_ram { + address 0x020 + + /* + * 1 byte per target starting at this address for configuration values + */ + TARG_SCRATCH { + size 16 + } + ULTRA_ENB { + size 2 + } + /* + * Bit vector of targets that have disconnection disabled. + */ + DISC_DSB { + size 2 + } + /* + * Length of pending message + */ + MSG_LEN { + size 1 + } + /* We reserve 8bytes to store outgoing messages */ + MSG_OUT { + size 8 + } + /* Parameters for DMA Logic */ + DMAPARAMS { + size 1 + bit WIDEODD 0x40 + bit SCSIEN 0x20 + bit SDMAEN 0x10 + bit SDMAENACK 0x10 + bit HDMAEN 0x08 + bit HDMAENACK 0x08 + bit DIRECTION 0x04 + bit FIFOFLUSH 0x02 + bit FIFORESET 0x01 + } + /* + * Number of SCBs supported by + * this card. + */ + SCBCOUNT { + size 1 + } + /* + * Two's complement of SCBCOUNT + */ + COMP_SCBCOUNT { + size 1 + } + /* + * Mask of bits to test against + * when looking at the Queue Count + * registers. Works around a bug + * on aic7850 chips. + */ + QCNTMASK { + size 1 + } + SEQ_FLAGS { + size 1 + bit RESELECTED 0x80 + bit IDENTIFY_SEEN 0x40 + bit TAGGED_SCB 0x20 + bit DPHASE 0x10 + bit PAGESCBS 0x04 + bit WIDE_BUS 0x02 + bit TWIN_BUS 0x01 + } + /* + * Temporary storage for the + * target/channel/lun of a + * reconnecting target + */ + SAVED_TCL { + size 1 + } + SG_COUNT { + size 1 + } + /* working value of SG pointer */ + SG_NEXT { + size 4 + } + /* + * head of list of SCBs awaiting + * selection + */ + WAITING_SCBH { + size 1 + } + SAVED_LINKPTR { + size 1 + } + SAVED_SCBPTR { + size 1 + } + /* + * The sequencer will stick the frist byte of any rejected message here + * so we can see what is getting thrown away. + */ + REJBYTE { + size 1 + } + /* + * The last bus phase as seen by the sequencer. + */ + LASTPHASE { + size 1 + bit CDI 0x80 + bit IOI 0x40 + bit MSGI 0x20 + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI + mask P_BUSFREE 0x01 + } + MSGIN_EXT_LEN { + size 1 + } + MSGIN_EXT_OPCODE { + size 1 + } + /* + * location 3, stores the last + * byte of an extended message if + * it passes the two bytes of space + * we allow now. This byte isn't + * used for anything, it just makes + * the code shorter for tossing + * extra bytes. + */ + MSGIN_EXT_BYTES { + size 3 + } + /* + * head of list of SCBs that are + * disconnected. Used for SCB + * paging. + */ + DISCONNECTED_SCBH { + size 1 + } + /* + * head of list of SCBs that are + * not in use. Used for SCB paging. + */ + FREE_SCBH { + size 1 + } + HSCB_ADDR { + size 4 + } + CUR_SCBID { + size 1 + } + ARG_1 { + size 1 + mask SEND_MSG 0x80 + mask SEND_SENSE 0x40 + mask SEND_REJ 0x20 + alias RETURN_1 + } + /* + * These are reserved registers in the card's scratch ram. Some of + * the values are specified in the AHA2742 technical reference manual + * and are initialized by the BIOS at boot time. + */ + SCSICONF { + address 0x05a + size 1 + bit RESET_SCSI 0x40 + } + HOSTCONF { + address 0x05d + size 1 + } + HA_274_BIOSCTRL { + address 0x05f + size 1 + mask BIOSMODE 0x30 + mask BIOSDISABLED 0x30 + bit CHANNEL_B_PRIMARY 0x08 + } +} + +const SCB_LIST_NULL 0xff + + +/* WDTR Message values */ +const BUS_8_BIT 0x00 +const BUS_16_BIT 0x01 +const BUS_32_BIT 0x02 +const MAX_OFFSET_8BIT 0x0f +const MAX_OFFSET_16BIT 0x08 diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq --- v2.1.47/linux/drivers/scsi/aic7xxx/aic7xxx.seq Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx/aic7xxx.seq Thu Jul 31 12:37:17 1997 @@ -0,0 +1,1147 @@ +/* + * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD. + * + * Copyright (c) 1994-1997 Justin Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $ + */ + +#include +#include + +/* + * A few words on the waiting SCB list: + * After starting the selection hardware, we check for reconnecting targets + * as well as for our selection to complete just in case the reselection wins + * bus arbitration. The problem with this is that we must keep track of the + * SCB that we've already pulled from the QINFIFO and started the selection + * on just in case the reselection wins so that we can retry the selection at + * a later time. This problem cannot be resolved by holding a single entry + * in scratch ram since a reconnecting target can request sense and this will + * create yet another SCB waiting for selection. The solution used here is to + * use byte 27 of the SCB as a psuedo-next pointer and to thread a list + * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, + * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to + * this list everytime a request sense occurs or after completing a non-tagged + * command for which a second SCB has been queued. The sequencer will + * automatically consume the entries. + */ + +/* + * We assume that the kernel driver may reset us at any time, even in the + * middle of a DMA, so clear DFCNTRL too. + */ +reset: + clr SCSISIGO; /* De-assert BSY */ + /* Always allow reselection */ + mvi SCSISEQ, ENRSELI|ENAUTOATNP; + call clear_target_state; +poll_for_work: + test SSTAT0,SELDO jnz select; + test SSTAT0,SELDI jnz reselect; + test SCSISEQ, ENSELO jnz poll_for_work; +.if ( TWIN_CHANNEL ) + /* + * Twin channel devices cannot handle things like SELTO + * interrupts on the "background" channel. So, if we + * are selecting, keep polling the current channel util + * either a selection or reselection occurs. + */ + xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ + test SSTAT0,SELDO jnz select; + test SSTAT0,SELDI jnz reselect; + test SCSISEQ, ENSELO jnz poll_for_work; + xor SBLKCTL,SELBUSB; /* Toggle back */ +.endif + cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; +test_queue: + /* Has the driver posted any work for us? */ + mov A, QCNTMASK; + test QINCNT,A jz poll_for_work; + +/* + * We have at least one queued SCB now and we don't have any + * SCBs in the list of SCBs awaiting selection. If we have + * any SCBs available for use, pull the tag from the QINFIFO + * and get to work on it. + */ +.if ( SCB_PAGING ) + mov ALLZEROS call get_free_or_disc_scb; + cmp SINDEX, SCB_LIST_NULL je poll_for_work; +.endif +dequeue_scb: + mov CUR_SCBID,QINFIFO; +.if !( SCB_PAGING ) + /* In the non-paging case, the SCBID == hardware SCB index */ + mov SCBPTR, CUR_SCBID; +.endif +dma_queued_scb: +/* + * DMA the SCB from host ram into the current SCB location. + */ + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov CUR_SCBID call dma_scb; + +/* + * See if there is not already an active SCB for this target. This code + * locks out on a per target basis instead of target/lun. Although this + * is not ideal for devices that have multiple luns active at the same + * time, it is faster than looping through all SCB's looking for active + * commands. We also don't have enough spare SCB space for us to store the + * SCBID of the currently busy transaction for each target/lun making it + * impossible to link up the SCBs. + */ +test_busy: + test SCB_CONTROL, TAG_ENB|ABORT_SCB jnz start_scb; + mvi SEQCTL, PAUSEDIS|FASTMODE; + mov SAVED_SCBPTR, SCBPTR; + mov SCB_TCL call index_untagged_scb; + mov ARG_1, SINDIR; /* + * ARG_1 should + * now have the SCB ID of + * any active, non-tagged, + * command for this target. + */ + cmp ARG_1, SCB_LIST_NULL je make_busy; +.if ( SCB_PAGING ) + /* + * Put this SCB back onto the free list. It + * may be necessary to satisfy the search for + * the active SCB. + */ + mov SCBPTR, SAVED_SCBPTR; + call add_scb_to_free_list; + /* Find the active SCB */ + mov ALLZEROS call findSCB; + /* + * If we couldn't find it, tell the kernel. This should + * never happen. + */ + cmp SINDEX, SCB_LIST_NULL jne paged_busy_link; + mvi INTSTAT, NO_MATCH_BUSY; +paged_busy_link: + /* Link us in */ + mov SCB_LINKED_NEXT, CUR_SCBID; + /* Put it back on the disconnected list */ + call add_scb_to_disc_list; + mvi SEQCTL, FASTMODE; + jmp poll_for_work; +.else +simple_busy_link: + mov SCBPTR, ARG_1; + mov SCB_LINKED_NEXT, CUR_SCBID; + mvi SEQCTL, FASTMODE; + jmp poll_for_work; +.endif +make_busy: + mov DINDIR, CUR_SCBID; + mov SCBPTR, SAVED_SCBPTR; + mvi SEQCTL, FASTMODE; + +start_scb: + /* + * Place us on the waiting list in case our selection + * doesn't win during bus arbitration. + */ + mov SCB_NEXT,WAITING_SCBH; + mov WAITING_SCBH, SCBPTR; +start_waiting: + /* + * Pull the first entry off of the waiting SCB list + * We don't have to "test_busy" because only transactions that + * have passed that test can be in the WAITING_SCB list. + */ + mov SCBPTR, WAITING_SCBH; + call start_selection; + jmp poll_for_work; + +start_selection: +.if ( TWIN_CHANNEL ) + and SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */ + and A,SELBUSB,SCB_TCL; /* Get new channel bit */ + or SINDEX,A; + mov SBLKCTL,SINDEX; /* select channel */ +.endif +initialize_scsiid: + and A, TID, SCB_TCL; /* Get target ID */ + and SCSIID, OID; /* Clear old target */ + or SCSIID, A; + mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret; +/* + * Reselection has been initiated by a target. Make a note that we've been + * reselected, but haven't seen an IDENTIFY message from the target yet. + */ +reselect: + clr MSG_LEN; /* Don't have anything in the mesg buffer */ + mvi CLRSINT0, CLRSELDI; + /* XXX test for and handle ONE BIT condition */ + and SAVED_TCL, SELID_MASK, SELID; + or SEQ_FLAGS,RESELECTED; + jmp select2; + +/* + * After the selection, remove this SCB from the "waiting SCB" + * list. This is achieved by simply moving our "next" pointer into + * WAITING_SCBH. Our next pointer will be set to null the next time this + * SCB is used, so don't bother with it now. + */ +select: + /* Turn off the selection hardware */ + mvi SCSISEQ, ENRSELI|ENAUTOATNP; /* + * ATN on parity errors + * for "in" phases + */ + mvi CLRSINT0, CLRSELDO; + mov SCBPTR, WAITING_SCBH; + mov WAITING_SCBH,SCB_NEXT; + mov SAVED_TCL, SCB_TCL; +/* + * As soon as we get a successful selection, the target should go + * into the message out phase since we have ATN asserted. Prepare + * the message to send. + * + * Messages are stored in scratch RAM starting with a length byte + * followed by the message itself. + */ + +mk_identify: + and MSG_OUT,0x7,SCB_TCL; /* lun */ + and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */ + or MSG_OUT,A; /* or in disconnect privledge */ + or MSG_OUT,MSG_IDENTIFYFLAG; + mvi MSG_LEN, 1; + +/* + * Send a tag message if TAG_ENB is set in the SCB control block. + * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. + */ +mk_tag: + test SCB_CONTROL,TAG_ENB jz mk_message; + and MSG_OUT[1],TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL; + mov MSG_OUT[2],SCB_TAG; + add MSG_LEN,2; /* update message length */ + +/* + * Interrupt the driver, and allow it to tweak the message buffer + * if it asks. + */ +mk_message: + test SCB_CONTROL,MK_MESSAGE jz select2; + mvi INTSTAT,AWAITING_MSG; + +select2: + mvi CLRSINT1,CLRBUSFREE; + or SIMODE1, ENBUSFREE; /* + * We aren't expecting a + * bus free, so interrupt + * the kernel driver if it + * happens. + */ +/* + * Initialize Ultra mode setting and clear the SCSI channel. + */ + or SXFRCTL0, CLRSTCNT|SPIOEN|CLRCHN; +.if ( ULTRA ) +ultra: + mvi SINDEX, ULTRA_ENB+1; + test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */ + dec SINDEX; +ultra_2: + mov FUNCTION1,SAVED_TCL; + mov A,FUNCTION1; + test SINDIR, A jz ndx_dtr; + or SXFRCTL0, FAST20; +.endif + +/* + * Initialize SCSIRATE with the appropriate value for this target. + * The SCSIRATE settings for each target are stored in an array + * based at TARG_SCRATCH. + */ +ndx_dtr: + shr A,4,SAVED_TCL; + test SBLKCTL,SELBUSB jz ndx_dtr_2; + or SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */ + or A,0x08; /* Channel B entries add 8 */ +ndx_dtr_2: + add SINDEX,TARG_SCRATCH,A; + mov SCSIRATE,SINDIR; + + +/* + * Main loop for information transfer phases. If BSY is false, then + * we have a bus free condition, expected or not. Otherwise, wait + * for the target to assert REQ before checking MSG, C/D and I/O + * for the bus phase. + * + */ +ITloop: + test SSTAT1,REQINIT jz ITloop; + test SSTAT1, SCSIPERR jnz ITloop; + + and A,PHASE_MASK,SCSISIGI; + mov LASTPHASE,A; + mov SCSISIGO,A; + + cmp ALLZEROS,A je p_dataout; + cmp A,P_DATAIN je p_datain; + cmp A,P_COMMAND je p_command; + cmp A,P_MESGOUT je p_mesgout; + cmp A,P_STATUS je p_status; + cmp A,P_MESGIN je p_mesgin; + + mvi INTSTAT,BAD_PHASE; /* unknown phase - signal driver */ + jmp ITloop; /* Try reading the bus again. */ + +await_busfree: + and SIMODE1, ~ENBUSFREE; + call clear_target_state; + mov NONE, SCSIDATL; /* Ack the last byte */ + test SSTAT1,REQINIT|BUSFREE jz .; + test SSTAT1, BUSFREE jnz poll_for_work; + mvi INTSTAT, BAD_PHASE; + +clear_target_state: + clr DFCNTRL; + clr SCSIRATE; /* + * We don't know the target we will + * connect to, so default to narrow + * transfers to avoid parity problems. + */ + and SXFRCTL0, ~FAST20; + mvi LASTPHASE, P_BUSFREE; + /* clear target specific flags */ + and SEQ_FLAGS,~(RESELECTED|IDENTIFY_SEEN|TAGGED_SCB|DPHASE) ret; + +p_dataout: + mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET; + jmp data_phase_init; + +/* + * If we re-enter the data phase after going through another phase, the + * STCNT may have been cleared, so restore it from the residual field. + */ +data_phase_reinit: + mvi DINDEX, STCNT; + mvi SCB_RESID_DCNT call bcopy_3; + jmp data_phase_loop; + +p_datain: + mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; +data_phase_init: + call assert; /* + * Ensure entering a data + * phase is okay - seen identify, etc. + */ + + test SEQ_FLAGS, DPHASE jnz data_phase_reinit; + + /* + * Initialize the DMA address and counter from the SCB. + * Also set SG_COUNT and SG_NEXT in memory since we cannot + * modify the values in the SCB itself until we see a + * save data pointers message. + */ + mvi DINDEX, HADDR; + mvi SCB_DATAPTR call bcopy_7; + + call set_stcnt_from_hcnt; + + mov SG_COUNT,SCB_SGCOUNT; + + mvi DINDEX, SG_NEXT; + mvi SCB_SGPTR call bcopy_4; + +data_phase_loop: +/* Guard against overruns */ + test SG_COUNT, 0xff jnz data_phase_inbounds; +/* + * Turn on 'Bit Bucket' mode, set the transfer count to + * 16meg and let the target run until it changes phase. + * When the transfer completes, notify the host that we + * had an overrun. + */ + or SXFRCTL1,BITBUCKET; + mvi HCNT[0], 0xff; + mvi HCNT[1], 0xff; + mvi HCNT[2], 0xff; + call set_stcnt_from_hcnt; + +data_phase_inbounds: +/* If we are the last SG block, ensure wideodd is off. */ + cmp SG_COUNT,0x01 jne data_phase_wideodd; + and DMAPARAMS, ~WIDEODD; +data_phase_wideodd: + mov DMAPARAMS call dma; + +/* Go tell the host about any overruns */ + test SXFRCTL1,BITBUCKET jnz data_phase_overrun; + +/* Exit if we had an underrun. dma clears SINDEX in this case. */ + test SINDEX,0xff jz data_phase_finish; + +/* + * Advance the scatter-gather pointers if needed + */ +sg_advance: + dec SG_COUNT; /* one less segment to go */ + + test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */ + + clr A; /* add sizeof(struct scatter) */ + add SG_NEXT[0],SG_SIZEOF; + adc SG_NEXT[1],A; + +/* + * Load a struct scatter and set up the data address and length. + * If the working value of the SG count is nonzero, then + * we need to load a new set of values. + * + * This, like all DMA's, assumes little-endian host data storage. + */ +sg_load: + mvi DINDEX, HADDR; + mvi SG_NEXT call bcopy_4; + + mvi HCNT[0],SG_SIZEOF; + clr HCNT[1]; + clr HCNT[2]; + + or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + + call dma_finish; + +/* + * Copy data from FIFO into SCB data pointer and data count. This assumes + * that the SG segments are of the form: + * + * struct ahc_dma_seg { + * u_int32_t addr; four bytes, little-endian order + * u_int32_t len; four bytes, little endian order + * }; + */ + mvi HADDR call dfdat_in_7; + +/* Load STCNT as well. It is a mirror of HCNT */ + call set_stcnt_from_hcnt; + test SSTAT1,PHASEMIS jz data_phase_loop; + +data_phase_finish: +/* + * After a DMA finishes, save the SG and STCNT residuals back into the SCB + * We use STCNT instead of HCNT, since it's a reflection of how many bytes + * were transferred on the SCSI (as opposed to the host) bus. + */ + mov SCB_RESID_DCNT[0],STCNT[0]; + mov SCB_RESID_DCNT[1],STCNT[1]; + mov SCB_RESID_DCNT[2],STCNT[2]; + mov SCB_RESID_SGCNT, SG_COUNT; + + /* We have seen a data phase */ + or SEQ_FLAGS, DPHASE; + + jmp ITloop; + +data_phase_overrun: +/* + * Turn off BITBUCKET mode and notify the host + */ + and SXFRCTL1, ~BITBUCKET; + mvi INTSTAT,DATA_OVERRUN; + jmp ITloop; + +/* + * Command phase. Set up the DMA registers and let 'er rip. + */ +p_command: + call assert; + +/* + * Load HADDR and HCNT. + */ + mvi DINDEX, HADDR; + mvi SCB_CMDPTR call bcopy_5; + clr HCNT[1]; + clr HCNT[2]; + + call set_stcnt_from_hcnt; + + mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma; + jmp ITloop; + +/* + * Status phase. Wait for the data byte to appear, then read it + * and store it into the SCB. + */ +p_status: + call assert; + + mov SCB_TARGET_STATUS, SCSIDATL; + jmp ITloop; + +/* + * Message out phase. If there is not an active message, but the target + * took us into this phase anyway, build a no-op message and send it. + */ +p_mesgout: + test MSG_LEN, 0xff jnz p_mesgout_start; + mvi MSG_NOOP call mk_mesg; /* build NOP message */ +p_mesgout_start: +/* + * Set up automatic PIO transfer from MSG_OUT. Bit 3 in + * SXFRCTL0 (SPIOEN) is already on. + */ + mvi SINDEX,MSG_OUT; + mov DINDEX,MSG_LEN; + +/* + * When target asks for a byte, drop ATN if it's the last one in + * the message. Otherwise, keep going until the message is exhausted. + * ATN must be dropped *at least* 90ns before we ack the last byte, so + * the code is aranged to execute two instructions before the byte is + * transferred to give a good margin of safety + * + * Keep an eye out for a phase change, in case the target issues + * a MESSAGE REJECT. + */ +p_mesgout_loop: + test SSTAT1, REQINIT jz p_mesgout_loop; + test SSTAT1, SCSIPERR jnz p_mesgout_loop; + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; +p_mesgout_testretry: + test DINDEX,0xff jnz p_mesgout_dropatn; + or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */ + jmp p_mesgout_start; +/* + * If the next bus phase after ATN drops is a message out, it means + * that the target is requesting that the last message(s) be resent. + */ +p_mesgout_dropatn: + cmp DINDEX,1 jne p_mesgout_outb; /* last byte? */ + mvi CLRSINT1,CLRATNO; /* drop ATN */ +p_mesgout_outb: + dec DINDEX; + mov SCSIDATL,SINDIR; + jmp p_mesgout_loop; + +p_mesgout_done: + mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */ + clr MSG_LEN; /* no active msg */ + jmp ITloop; + +/* + * Message in phase. Bytes are read using Automatic PIO mode. + */ +p_mesgin: + mvi ACCUM call inb_first; /* read the 1st message byte */ + mov REJBYTE,A; /* save it for the driver */ + + test A,MSG_IDENTIFYFLAG jnz mesgin_identify; + cmp A,MSG_DISCONNECT je mesgin_disconnect; + cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs; + cmp ALLZEROS,A je mesgin_complete; + cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs; + cmp A,MSG_EXTENDED je mesgin_extended; + cmp A,MSG_MESSAGE_REJECT je mesgin_reject; + cmp A,MSG_NOOP je mesgin_done; + +rej_mesgin: +/* + * We have no idea what this message in is, so we issue a message reject + * and hope for the best. In any case, rejection should be a rare + * occurrence - signal the driver when it happens. + */ + mvi INTSTAT,SEND_REJECT; /* let driver know */ + + mvi MSG_MESSAGE_REJECT call mk_mesg; + +mesgin_done: + mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ + jmp ITloop; + + +mesgin_complete: +/* + * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO, + * and trigger a completion interrupt. Before doing so, check to see if there + * is a residual or the status byte is something other than NO_ERROR (0). In + * either of these conditions, we upload the SCB back to the host so it can + * process this information. In the case of a non zero status byte, we + * additionally interrupt the kernel driver synchronously, allowing it to + * decide if sense should be retrieved. If the kernel driver wishes to request + * sense, it will fill the kernel SCB with a request sense command and set + * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload + * the SCB, and process it as the next command by adding it to the waiting list. + * If the kernel driver does not wish to request sense, it need only clear + * RETURN_1, and the command is allowed to complete normally. We don't bother + * to post to the QOUTFIFO in the error cases since it would require extra + * work in the kernel driver to ensure that the entry was removed before the + * command complete code tried processing it. + */ + +/* + * First check for residuals + */ + test SCB_RESID_SGCNT,0xff jnz upload_scb; + test SCB_TARGET_STATUS,0xff jz status_ok; /* Good Status? */ +upload_scb: + mvi DMAPARAMS, FIFORESET; + mov SCB_TAG call dma_scb; +check_status: + test SCB_TARGET_STATUS,0xff jz status_ok; /* Just a residual? */ + mvi INTSTAT,BAD_STATUS; /* let driver know */ + cmp RETURN_1, SEND_SENSE jne status_ok; + /* This SCB becomes the next to execute as it will retrieve sense */ + mov SCB_LINKED_NEXT, SCB_TAG; + jmp dma_next_scb; + +status_ok: +/* First, mark this target as free. */ + test SCB_CONTROL,TAG_ENB jnz complete; /* + * Tagged commands + * don't busy the + * target. + */ + mov SAVED_SCBPTR, SCBPTR; + mov SAVED_LINKPTR, SCB_LINKED_NEXT; + mov SCB_TCL call index_untagged_scb; + mov DINDIR, SAVED_LINKPTR; + mov SCBPTR, SAVED_SCBPTR; + +complete: + /* Post the SCB and issue an interrupt */ + mov QOUTFIFO,SCB_TAG; + mvi INTSTAT,CMDCMPLT; + test SCB_CONTROL, ABORT_SCB jz dma_next_scb; + mvi INTSTAT, ABORT_CMDCMPLT; + +dma_next_scb: + cmp SCB_LINKED_NEXT, SCB_LIST_NULL je add_to_free_list; +.if !( SCB_PAGING ) + /* Only DMA on top of ourselves if we are the SCB to download */ + mov A, SCB_LINKED_NEXT; + cmp SCB_TAG, A je dma_next_scb2; + call add_scb_to_free_list; + mov SCBPTR, A; + jmp add_to_waiting_list; +.endif +dma_next_scb2: + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov SCB_LINKED_NEXT call dma_scb; +add_to_waiting_list: + mov SCB_NEXT,WAITING_SCBH; + mov WAITING_SCBH, SCBPTR; + /* + * Prepare our selection hardware before the busfree so we have a + * high probability of winning arbitration. + */ + call start_selection; + jmp await_busfree; +add_to_free_list: + call add_scb_to_free_list; + jmp await_busfree; + +/* + * Is it an extended message? Copy the message to our message buffer and + * notify the host. The host will tell us whether to reject this message, + * respond to it with the message that the host placed in our message buffer, + * or simply to do nothing. + */ +mesgin_extended: + mvi MSGIN_EXT_LEN call inb_next; + mov A, MSGIN_EXT_LEN; +mesgin_extended_loop: + mov DINDEX call inb_next; + dec A; + cmp DINDEX, MSGIN_EXT_BYTES+3 jne mesgin_extended_loop_test; + dec DINDEX; /* dump by repeatedly filling the last byte */ +mesgin_extended_loop_test: + test A, 0xFF jnz mesgin_extended_loop; +mesgin_extended_intr: + mvi INTSTAT,EXTENDED_MSG; /* let driver know */ + cmp RETURN_1,SEND_REJ je rej_mesgin; + cmp RETURN_1,SEND_MSG jne mesgin_done; +/* The kernel has setup a message to be sent */ + or SCSISIGO,ATNO,LASTPHASE; /* turn on ATNO */ + jmp mesgin_done; + +/* + * Is it a disconnect message? Set a flag in the SCB to remind us + * and await the bus going free. + */ +mesgin_disconnect: + or SCB_CONTROL,DISCONNECTED; +.if ( SCB_PAGING ) + call add_scb_to_disc_list; +.endif + jmp await_busfree; + +/* + * Save data pointers message: + * Copying RAM values back to SCB, for Save Data Pointers message, but + * only if we've actually been into a data phase to change them. This + * protects against bogus data in scratch ram and the residual counts + * since they are only initialized when we go into data_in or data_out. + */ +mesgin_sdptrs: + test SEQ_FLAGS, DPHASE jz mesgin_done; + mov SCB_SGCOUNT,SG_COUNT; + + /* The SCB SGPTR becomes the next one we'll download */ + mvi DINDEX, SCB_SGPTR; + mvi SG_NEXT call bcopy_4; + + /* The SCB DATAPTR0 becomes the current SHADDR */ + mvi DINDEX, SCB_DATAPTR; + mvi SHADDR call bcopy_4; + +/* + * Use the residual number since STCNT is corrupted by any message transfer. + */ + mvi SCB_RESID_DCNT call bcopy_3; + + jmp mesgin_done; + +/* + * Restore pointers message? Data pointers are recopied from the + * SCB anytime we enter a data phase for the first time, so all + * we need to do is clear the DPHASE flag and let the data phase + * code do the rest. + */ +mesgin_rdptrs: + and SEQ_FLAGS, ~DPHASE; /* + * We'll reload them + * the next time through + * the dataphase. + */ + jmp mesgin_done; + +/* + * Identify message? For a reconnecting target, this tells us the lun + * that the reconnection is for - find the correct SCB and switch to it, + * clearing the "disconnected" bit so we don't "find" it by accident later. + */ +mesgin_identify: + test A,0x78 jnz rej_mesgin; /*!DiscPriv|!LUNTAR|!Reserved*/ + and A,0x07; /* lun in lower three bits */ + or SAVED_TCL,A; /* SAVED_TCL should be complete now */ + mov SAVED_TCL call index_untagged_scb; + mov ARG_1, SINDIR; +.if ( SCB_PAGING ) + cmp ARG_1,SCB_LIST_NULL jne use_findSCB; +.else + cmp ARG_1,SCB_LIST_NULL je snoop_tag; + /* Directly index the SCB */ + mov SCBPTR,ARG_1; + test SCB_CONTROL,DISCONNECTED jz not_found; + jmp setup_SCB; +.endif +/* + * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message. + * If we get one, we use the tag returned to find the proper + * SCB. With SCB paging, this requires using findSCB for both tagged + * and non-tagged transactions since the SCB may exist in any slot. + * If we're not using SCB paging, we can use the tag as the direct + * index to the SCB. + */ +snoop_tag: + mov NONE,SCSIDATL; /* ACK Identify MSG */ +snoop_tag_loop: + test SSTAT1,REQINIT jz snoop_tag_loop; + test SSTAT1, SCSIPERR jnz snoop_tag_loop; + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGIN jne not_found; + cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found; +get_tag: + or SEQ_FLAGS, TAGGED_SCB; + mvi ARG_1 call inb_next; /* tag value */ +/* + * See if the tag is in range. The tag is < SCBCOUNT if we add + * the complement of SCBCOUNT to the incomming tag and there is + * no carry. + */ + mov A,COMP_SCBCOUNT; + add SINDEX,A,ARG_1; + jc not_found; + +.if ! ( SCB_PAGING ) +index_by_tag: + mov SCBPTR,ARG_1; + mov A, SAVED_TCL; + cmp SCB_TCL,A jne not_found; + test SCB_CONTROL,TAG_ENB jz not_found; + test SCB_CONTROL,DISCONNECTED jz not_found; +.else +/* + * Ensure that the SCB the tag points to is for an SCB transaction + * to the reconnecting target. + */ +use_findSCB: + mov ALLZEROS call findSCB; /* Have to search */ + cmp SINDEX, SCB_LIST_NULL je not_found; +.endif +setup_SCB: + and SCB_CONTROL,~DISCONNECTED; + or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */ + jmp mesgin_done; + +not_found: + mvi INTSTAT, NO_MATCH; + mvi MSG_BUS_DEV_RESET call mk_mesg; + jmp mesgin_done; + +/* + * Message reject? Let the kernel driver handle this. If we have an + * outstanding WDTR or SDTR negotiation, assume that it's a response from + * the target selecting 8bit or asynchronous transfer, otherwise just ignore + * it since we have no clue what it pertains to. + */ +mesgin_reject: + mvi INTSTAT, REJECT_MSG; + jmp mesgin_done; + +/* + * [ ADD MORE MESSAGE HANDLING HERE ] + */ + +/* + * Locking the driver out, build a one-byte message passed in SINDEX + * if there is no active message already. SINDEX is returned intact. + */ +mk_mesg: + mvi SEQCTL, PAUSEDIS|FASTMODE; + test MSG_LEN,0xff jz mk_mesg1; /* Should always succeed */ + + /* + * Hmmm. For some reason the mesg buffer is in use. + * Tell the driver. It should look at SINDEX to find + * out what we wanted to use the buffer for and resolve + * the conflict. + */ + mvi SEQCTL,FASTMODE; + mvi INTSTAT,MSG_BUFFER_BUSY; + +mk_mesg1: + or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */ + mvi MSG_LEN,1; /* length = 1 */ + mov MSG_OUT,SINDEX; /* 1-byte message */ + mvi SEQCTL,FASTMODE ret; + +/* + * Functions to read data in Automatic PIO mode. + * + * According to Adaptec's documentation, an ACK is not sent on input from + * the target until SCSIDATL is read from. So we wait until SCSIDATL is + * latched (the usual way), then read the data byte directly off the bus + * using SCSIBUSL. When we have pulled the ATN line, or we just want to + * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI + * spec guarantees that the target will hold the data byte on the bus until + * we send our ACK. + * + * The assumption here is that these are called in a particular sequence, + * and that REQ is already set when inb_first is called. inb_{first,next} + * use the same calling convention as inb. + */ + +inb_next: + mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ +inb_next_wait: + /* + * If there is a parity error, wait for the kernel to + * see the interrupt and prepare our message response + * before continuing. + */ + test SSTAT1, REQINIT jz inb_next_wait; + test SSTAT1, SCSIPERR jnz inb_next_wait; + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGIN jne mesgin_phasemis; +inb_first: + mov DINDEX,SINDEX; + mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/ +inb_last: + mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ + +mesgin_phasemis: +/* + * We expected to receive another byte, but the target changed phase + */ + mvi INTSTAT, MSGIN_PHASEMIS; + jmp ITloop; + +/* + * DMA data transfer. HADDR and HCNT must be loaded first, and + * SINDEX should contain the value to load DFCNTRL with - 0x3d for + * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared + * during initialization. + */ +dma: + mov DFCNTRL,SINDEX; +dma_loop: + test SSTAT0,DMADONE jnz dma_dmadone; + test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */ +dma_phasemis: + test SSTAT0,SDONE jnz dma_checkfifo; + mov SINDEX,ALLZEROS; /* Notify caller of phasemiss */ + +/* + * We will be "done" DMAing when the transfer count goes to zero, or + * the target changes the phase (in light of this, it makes sense that + * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are + * doing a SCSI->Host transfer, the data FIFO should be flushed auto- + * magically on STCNT=0 or a phase change, so just wait for FIFO empty + * status. + */ +dma_checkfifo: + test DFCNTRL,DIRECTION jnz dma_fifoempty; +dma_fifoflush: + test DFSTATUS,FIFOEMP jz dma_fifoflush; + +dma_fifoempty: + /* Don't clobber an inprogress host data transfer */ + test DFSTATUS, MREQPEND jnz dma_fifoempty; +/* + * Now shut the DMA enables off and make sure that the DMA enables are + * actually off first lest we get an ILLSADDR. + */ +dma_dmadone: + and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); +dma_halt: + test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; +return: + ret; + +/* + * Assert that if we've been reselected, then we've seen an IDENTIFY + * message. + */ +assert: + test SEQ_FLAGS,RESELECTED jz return; /* reselected? */ + test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */ + + mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */ + +.if ( SCB_PAGING ) +/* + * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL) + * or by the SCBIDn ARG_1. The search begins at the SCB index passed in + * via SINDEX. If the SCB cannot be found, SINDEX will be SCB_LIST_NULL, + * otherwise, SCBPTR is set to the proper SCB. + */ +findSCB: + mov SCBPTR,SINDEX; /* switch to next SCB */ + mov A, ARG_1; /* Tag passed in ARG_1 */ + cmp SCB_TAG,A jne findSCB_loop; + test SCB_CONTROL,DISCONNECTED jnz foundSCB;/*should be disconnected*/ +findSCB_loop: + inc SINDEX; + mov A,SCBCOUNT; + cmp SINDEX,A jne findSCB; +/* + * We didn't find it. If we're paging, pull an SCB and DMA down the + * one we want. If we aren't paging or the SCB we dma down has the + * abort flag set, return not found. + */ + mov ALLZEROS call get_free_or_disc_scb; + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov ARG_1 call dma_scb; + test SCB_RESID_SGCNT, 0xff jz . + 2; + or SCB_CONTROL, MUST_DMAUP_SCB; + test SCB_CONTROL, ABORT_SCB jz return; +find_error: + mvi SINDEX, SCB_LIST_NULL ret; +foundSCB: + test SCB_CONTROL, ABORT_SCB jnz find_error; +rem_scb_from_disc_list: +/* Remove this SCB from the disconnection list */ + cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev; + mov SAVED_LINKPTR, SCB_PREV; + mov SCBPTR, SCB_NEXT; + mov SCB_PREV, SAVED_LINKPTR; + mov SCBPTR, SINDEX; +unlink_prev: + cmp SCB_PREV,SCB_LIST_NULL je rHead;/* At the head of the list */ + mov SAVED_LINKPTR, SCB_NEXT; + mov SCBPTR, SCB_PREV; + mov SCB_NEXT, SAVED_LINKPTR; + mov SCBPTR, SINDEX ret; +rHead: + mov DISCONNECTED_SCBH,SCB_NEXT ret; +.else + ret; +.endif + +set_stcnt_from_hcnt: + mov STCNT[0], HCNT[0]; + mov STCNT[1], HCNT[1]; + mov STCNT[2], HCNT[2] ret; + +bcopy_7: + mov DINDIR, SINDIR; + mov DINDIR, SINDIR; +bcopy_5: + mov DINDIR, SINDIR; +bcopy_4: + mov DINDIR, SINDIR; +bcopy_3: + mov DINDIR, SINDIR; + mov DINDIR, SINDIR; + mov DINDIR, SINDIR ret; + +dma_scb: + /* + * SCB index is in SINDEX. Determine the physical address in + * the host where this SCB is located and load HADDR with it. + */ + shr DINDEX, 3, SINDEX; + shl A, 5, SINDEX; + add HADDR[0], A, HSCB_ADDR[0]; + mov A, DINDEX; + adc HADDR[1], A, HSCB_ADDR[1]; + clr A; + adc HADDR[2], A, HSCB_ADDR[2]; + adc HADDR[3], A, HSCB_ADDR[3]; + /* Setup Count */ + mvi HCNT[0], 28; + clr HCNT[1]; + clr HCNT[2]; + mov DFCNTRL, DMAPARAMS; + test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; + /* Fill it with the SCB data */ +copy_scb_tofifo: + mvi SINDEX, SCB_CONTROL; + add A, 28, SINDEX; +copy_scb_tofifo_loop: + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + cmp SINDEX, A jne copy_scb_tofifo_loop; + or DFCNTRL, HDMAEN|FIFOFLUSH; +dma_scb_fromhost: + call dma_finish; + /* If we were putting the SCB, we are done */ + test DMAPARAMS, DIRECTION jz return; + mvi SCB_CONTROL call dfdat_in_7; + call dfdat_in_7_continued; + call dfdat_in_7_continued; + jmp dfdat_in_7_continued; +dfdat_in_7: + mov DINDEX,SINDEX; +dfdat_in_7_continued: + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT ret; + +/* + * Wait for DMA from host memory to data FIFO to complete, then disable + * DMA and wait for it to acknowledge that it's off. + */ +dma_finish: + test DFSTATUS,HDONE jz dma_finish; + /* Turn off DMA */ + and DFCNTRL, ~HDMAEN; + test DFCNTRL, HDMAEN jnz .; + ret; + +index_untagged_scb: + mov DINDEX, SINDEX; + shr DINDEX, 4; + and DINDEX, 0x03; /* Bottom two bits of tid */ + add DINDEX, SCB_BUSYTARGETS; + shr A, 6, SINDEX; /* Target ID divided by 4 */ + test SINDEX, SELBUSB jz index_untagged_scb2; + add A, 2; /* Add 2 positions */ +index_untagged_scb2: + mov SCBPTR, A; /* + * Select the SCB with this + * target's information. + */ + mov SINDEX, DINDEX ret; + +add_scb_to_free_list: + mov SCB_NEXT, FREE_SCBH; + mvi SCB_TAG, SCB_LIST_NULL; + mov FREE_SCBH, SCBPTR ret; + +.if ( SCB_PAGING ) +get_free_or_disc_scb: + cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb; + cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb; +return_error: + mvi SINDEX, SCB_LIST_NULL ret; +dequeue_disc_scb: + mov SCBPTR, DISCONNECTED_SCBH; +/* + * If we have a residual, then we are in the middle of some I/O + * and we have to send this SCB back up to the kernel so that the + * saved data pointers and residual information isn't lost. + */ + test SCB_CONTROL, MUST_DMAUP_SCB jz . + 3; + and SCB_CONTROL, ~MUST_DMAUP_SCB; + jmp dma_up_scb; + test SCB_RESID_SGCNT,0xff jnz dma_up_scb; + cmp SCB_LINKED_NEXT, SCB_LIST_NULL je unlink_disc_scb; +dma_up_scb: + mvi DMAPARAMS, FIFORESET; + mov SCB_TAG call dma_scb; +unlink_disc_scb: + /* jmp instead of call since we want to return anyway */ + mov SCBPTR jmp rem_scb_from_disc_list; +dequeue_free_scb: + mov SCBPTR, FREE_SCBH; + mov FREE_SCBH, SCB_NEXT ret; + +add_scb_to_disc_list: +/* + * Link this SCB into the DISCONNECTED list. This list holds the + * candidates for paging out an SCB if one is needed for a new command. + * Modifying the disconnected list is a critical(pause dissabled) section. + */ + mvi SCB_PREV, SCB_LIST_NULL; + mov SCB_NEXT, DISCONNECTED_SCBH; + mov DISCONNECTED_SCBH, SCBPTR; + cmp SCB_NEXT,SCB_LIST_NULL je return; + mov SCBPTR,SCB_NEXT; + mov SCB_PREV,DISCONNECTED_SCBH; + mov SCBPTR,DISCONNECTED_SCBH ret; +.endif diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/aic7xxx/scsi_message.h linux/drivers/scsi/aic7xxx/scsi_message.h --- v2.1.47/linux/drivers/scsi/aic7xxx/scsi_message.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx/scsi_message.h Thu Jul 31 12:37:17 1997 @@ -0,0 +1,41 @@ +/* + * SCSI messages definitions. + */ + +/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */ +#define MSG_CMDCOMPLETE 0x00 /* M/M */ +#define MSG_EXTENDED 0x01 /* O/O */ +#define MSG_SAVEDATAPOINTER 0x02 /* O/O */ +#define MSG_RESTOREPOINTERS 0x03 /* O/O */ +#define MSG_DISCONNECT 0x04 /* O/O */ +#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */ +#define MSG_ABORT 0x06 /* O/M */ +#define MSG_MESSAGE_REJECT 0x07 /* M/M */ +#define MSG_NOOP 0x08 /* M/M */ +#define MSG_PARITY_ERROR 0x09 /* M/M */ +#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */ +#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */ +#define MSG_BUS_DEV_RESET 0x0c /* O/M */ +#define MSG_ABORT_TAG 0x0d /* O/O */ +#define MSG_CLEAR_QUEUE 0x0e /* O/O */ +#define MSG_INIT_RECOVERY 0x0f /* O/O */ +#define MSG_REL_RECOVERY 0x10 /* O/O */ +#define MSG_TERM_IO_PROC 0x11 /* O/O */ + +/* Messages (2 byte) */ +#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */ +#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */ +#define MSG_ORDERED_Q_TAG 0x22 /* O/O */ +#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */ + +/* Identify message */ /* M/M */ +#define MSG_IDENTIFYFLAG 0x80 +#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun)) +#define MSG_ISIDENTIFY(m) ((m) & MSG_IDENTIFYFLAG) + +/* Extended messages (opcode and length) */ +#define MSG_EXT_SDTR 0x01 +#define MSG_EXT_SDTR_LEN 0x03 + +#define MSG_EXT_WDTR 0x03 +#define MSG_EXT_WDTR_LEN 0x02 diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/aic7xxx/sequencer.h linux/drivers/scsi/aic7xxx/sequencer.h --- v2.1.47/linux/drivers/scsi/aic7xxx/sequencer.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx/sequencer.h Thu Jul 31 12:37:17 1997 @@ -0,0 +1,102 @@ +/* + * Instruction formats for the sequencer program downloaded to + * Aic7xxx SCSI host adapters + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: sequencer.h,v 1.2 1997/06/27 19:38:52 gibbs Exp $ + */ + +#if defined(__KERNEL__) +typedef unsigned char u_int8_t; +#endif + +struct ins_format1 { + u_int8_t immediate; + u_int8_t source; + u_int8_t destination; + u_int8_t opcode_ret; +}; + +struct ins_format2 { + u_int8_t shift_control; + u_int8_t source; + u_int8_t destination; + u_int8_t opcode_ret; +#define RETURN_BIT 0x01 +}; + +struct ins_format3 { + u_int8_t immediate; + u_int8_t source; + u_int8_t address; + u_int8_t opcode_addr; +#define ADDR_HIGH_BIT 0x01 +}; + +struct instruction { + union { + struct ins_format1 format1; + struct ins_format2 format2; + struct ins_format3 format3; + u_int8_t bytes[4]; + } format; + u_int srcline; + struct symbol *patch_label; + struct { + struct instruction *stqe_next; /* next element */ + } links; +}; + +#define AIC_OP_OR 0x0 +#define AIC_OP_AND 0x1 +#define AIC_OP_XOR 0x2 +#define AIC_OP_ADD 0x3 +#define AIC_OP_ADC 0x4 +#define AIC_OP_ROL 0x5 + +#define AIC_OP_JMP 0x8 +#define AIC_OP_JC 0x9 +#define AIC_OP_JNC 0xa +#define AIC_OP_CALL 0xb +#define AIC_OP_JNE 0xc +#define AIC_OP_JNZ 0xd +#define AIC_OP_JE 0xe +#define AIC_OP_JZ 0xf + +/* Pseudo Ops */ +#define AIC_OP_SHL 0x10 +#define AIC_OP_SHR 0x20 +#define AIC_OP_ROR 0x30 diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c --- v2.1.47/linux/drivers/scsi/aic7xxx.c Wed Apr 23 19:01:22 1997 +++ linux/drivers/scsi/aic7xxx.c Thu Jul 31 12:37:17 1997 @@ -1,5 +1,3 @@ -#define EXPERIMENTAL_FLAGS 0 - /*+M************************************************************************* * Adaptec AIC7xxx device driver for Linux. * @@ -29,21 +27,73 @@ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the * ANSI SCSI-2 specification (draft 10c), ... * - * ---------------------------------------------------------------- - * Modified to include support for wide and twin bus adapters, - * DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes, + * -------------------------------------------------------------------------- + * + * Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org): + * + * Substantially modified to include support for wide and twin bus + * adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes, * SCB paging, and other rework of the code. * - * Parts of this driver are based on the FreeBSD driver by Justin - * T. Gibbs. + * Parts of this driver were also based on the FreeBSD driver by + * Justin T. Gibbs. His copyright follows: + * + * -------------------------------------------------------------------------- + * Copyright (c) 1994-1997 Justin Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $ + *--------------------------------------------------------------------------- + * + * Thanks also go to (in alphabetical order) the following: + * + * Rory Bolt - Sequencer bug fixes + * Jay Estabrook - Initial DEC Alpha support + * Doug Ledford - Much needed abort/reset bug fixes + * Kai Makisara - DMAing of SCBs * * A Boot time option was also added for not resetting the scsi bus. * - * Form: aic7xxx=extended,no_reset + * Form: aic7xxx=extended + * aic7xxx=no_reset + * aic7xxx=ultra + * aic7xxx=irq_trigger:[0,1] # 0 edge, 1 level + * aic7xxx=verbose * - * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96 + * Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97 * - * $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $ + * $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $ *-M*************************************************************************/ #ifdef MODULE @@ -67,7 +117,11 @@ #include "scsi.h" #include "hosts.h" #include "aic7xxx.h" + +#include "aic7xxx/sequencer.h" +#include "aic7xxx/scsi_message.h" #include "aic7xxx_reg.h" +#include "aic7xxx_seq.h" #include #include /* for kmalloc() */ @@ -79,16 +133,20 @@ */ #define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a)) -static struct proc_dir_entry proc_scsi_aic7xxx = { +struct proc_dir_entry proc_scsi_aic7xxx = { PROC_SCSI_AIC7XXX, 7, "aic7xxx", - S_IFDIR | S_IRUGO | S_IXUGO, 2 + S_IFDIR | S_IRUGO | S_IXUGO, 2, + 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "$Revision: 4.0 $" +#define AIC7XXX_C_VERSION "$Revision: 4.1 $" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) -#define MIN(a,b) ((a < b) ? a : b) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define ALL_TARGETS -1 +#define ALL_CHANNELS '\0' +#define ALL_LUNS -1 #ifndef TRUE # define TRUE 1 #endif @@ -107,11 +165,8 @@ * support because all PCI dependent code is bracketed with * "#ifdef CONFIG_PCI ... #endif CONFIG_PCI". * - * o Twin bus support - this has been tested and does work. - * - * o DMAing of SCBs - thanks to Kai Makisara, this now works. - * This define is now taken out and DMAing of SCBs is always - * performed (8/12/95 - DE). + * o Twin bus support - this has been tested and does work. It is + * not an option anymore. * * o Tagged queueing - this driver is capable of tagged queueing * but I am unsure as to how well the higher level driver implements @@ -140,16 +195,16 @@ * LUN using its own heuristic based on the number of available * SCBs. * - * o 3985 support - The 3985 adapter is much like the 3940, but - * has three 7870 controllers as opposed to two for the 3940. - * It will get probed and recognized as three different adapters, - * but all three controllers can share the same external bank of - * 255 SCBs. If you enable AIC7XXX_SHARE_SCBS, then the driver - * will attempt to share the common bank of SCBs between the three - * controllers of the 3985. This is experimental and hasn't - * been tested. By default, we do not share the bank of SCBs, - * and force the controllers to use their own internal bank of - * 16 SCBs. Please let us know if sharing the SCB array works. + * o 3985 support - The 3985 adapter is much like the 3940, but has + * three 7870 controllers as opposed to two for the 3940. It will + * be probed and recognized as three different adapters, but all + * three controllers can share the same external bank of 255 SCBs. + * If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt + * to use and share the common bank of SCBs between the three + * controllers of the 3985. This is experimental and hasn't been + * been tested. By default, we do not use external SCB RAM, and + * force the controllers to use their own internal bank of 16 SCBs. + * Please let us know if using the external SCB array works. * * o SCB paging support - SCB paging is enabled by defining * AIC7XXX_PAGE_ENABLE. Support for this was taken from the @@ -162,43 +217,54 @@ * Note that sharing of IRQs is not an option any longer. Linux supports * it so we support it. * - * Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96 + * Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96 */ -/* Uncomment this for testing twin bus support. */ -#define AIC7XXX_TWIN_SUPPORT - /* Uncomment this for tagged queueing. */ -/* #define AIC7XXX_TAGGED_QUEUEING */ +#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING +#define AIC7XXX_TAGGED_QUEUEING +#endif /* * You can try raising me if tagged queueing is enabled, or lowering * me if you only have 4 SCBs. */ -/* #define AIC7XXX_CMDS_PER_LUN 8 */ +#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN +#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN +#endif /* Set this to the delay in seconds after SCSI bus reset. */ +#ifdef CONFIG_AIC7XXX_RESET_DELAY +#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY +#else #define AIC7XXX_RESET_DELAY 15 +#endif /* - * Uncomment the following define for collection of SCSI transfer statistics - * for the /proc filesystem. + * Control collection of SCSI transfer statistics for the /proc filesystem. * * NOTE: Do NOT enable this when running on kernels version 1.2.x and below. * NOTE: This does affect performance since it has to maintain statistics. */ -/* #define AIC7XXX_PROC_STATS */ +#ifdef CONFIG_AIC7XXX_PROC_STATS +#define AIC7XXX_PROC_STATS +#endif /* - * Uncomment the following to enable SCB paging. + * Enable SCB paging. */ -/* #define AIC7XXX_PAGE_ENABLE */ +#ifdef CONFIG_AIC7XXX_PAGE_ENABLE +#define AIC7XXX_PAGE_ENABLE +#endif /* - * Uncomment the following to enable sharing of the external bank - * of 255 SCBs for the 3985. + * Uncomment the following to enable use of the external bank + * of 255 SCBs. For 3985 adapters, this will also enable sharing + * of the SCB array across all three controllers. */ -#define AIC7XXX_SHARE_SCBS +#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM +#define AIC7XXX_USE_EXT_SCBRAM +#endif /* * For debugging the abort/reset code. @@ -211,6 +277,87 @@ #define AIC7XXX_DEBUG /* + * Set this for defining the number of tagged commands on a device + * by device, and controller by controller basis. The first set + * of tagged commands will be used for the first detected aic7xxx + * controller, the second set will be used for the second detected + * aic7xxx controller, and so on. These values will *only* be used + * for targets that are tagged queueing capable; these values will + * be ignored in all other cases. The tag_commands is an array of + * 16 to allow for wide and twin adapters. Twin adapters will use + * indexes 0-7 for channel 0, and indexes 8-15 for channel 1. + * + * *** Determining commands per LUN *** + * + * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its + * own algorithm to determine the commands/LUN. If SCB paging is + * enabled, the commands/LUN is 8. When SCB paging is not enabled, + * then commands/LUN is 8 for adapters with 16 or more hardware SCBs + * and 4 commands/LUN for adapters with 3 or 4 SCBs. + * + */ +/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */ + +#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE +typedef struct +{ + char tag_commands[16]; /* Allow for wide/twin channel adapters. */ +} adapter_tag_info_t; + +/* + * Make a define that will tell the driver to use it's own algorithm + * for determining commands/LUN (see Determining commands per LUN + * above). + */ +#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +/* + * Modify this as you see fit for your system. By setting tag_commands + * to 0, the driver will use it's own algorithm for determining the + * number of commands to use (see above). When -1, the driver will + * not enable tagged queueing for that particular device. When positive + * (> 0) the values in the array are used for the queue_depth. Note + * that the maximum value for an entry is 127. + * + * In this example, the first line will enable tagged queueing for all + * the devices on the first probed aic7xxx adapter and tells the driver + * to use it's own algorithm for determining commands/LUN. + * + * The second line enables tagged queueing with 4 commands/LUN for IDs + * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the + * driver to use its own algorithm for ID 1. + * + * The third line is the same as the first line. + * + * The fourth line disables tagged queueing for devices 0 and 3. It + * enables tagged queueing for the other IDs, with 16 commands/LUN + * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for + * IDs 2, 5-7, and 9-15. + */ +adapter_tag_info_t aic7xxx_tag_info[] = +{ + {DEFAULT_TAG_COMMANDS}, + {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}}, + {DEFAULT_TAG_COMMANDS}, + {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}} +}; +#endif + +/* + * Don't define this unless you have problems with the driver + * interrupt handler. The old method would register the drivers + * interrupt handler as a "fast" type interrupt handler that would + * lock out other interrupts. Since this driver can spend a lot + * of time in the interrupt handler, this is _not_ a good idea. + * It also conflicts with some of the more common ethernet drivers + * that don't use fast interrupts. Currently, Linux does not allow + * IRQ sharing unless both drivers can agree on the type of interrupt + * handler. + */ +/* #define AIC7XXX_OLD_ISR_TYPE */ + + +/* * Controller type and options */ typedef enum { @@ -232,14 +379,15 @@ AIC_7882, /* PCI aic7882 on 3940 Ultra */ AIC_7883, /* PCI aic7883 on 3985 Ultra */ AIC_7884 /* PCI aic7884 on 294x Ultra Differential */ -} aha_type; +} aha_chip_type; typedef enum { AIC_777x, /* AIC-7770 based */ - AIC_785x, /* AIC-7850 based */ + AIC_785x, /* AIC-7850 based (3 SCBs)*/ + AIC_786x, /* AIC-7860 based (7850 ultra) */ AIC_787x, /* AIC-7870 based */ - AIC_788x /* AIC-7880 based */ -} aha_chip_type; + AIC_788x /* AIC-7880 based (ultra) */ +} aha_chip_class_type; typedef enum { AIC_SINGLE, /* Single Channel */ @@ -269,24 +417,24 @@ * Don't forget to change this when changing the types! */ static const char *board_names[] = { - "", /* AIC_NONE */ - "AIC-7770", /* AIC_7770 */ - "AHA-2740", /* AIC_7771 */ - "AHA-2840", /* AIC_284x */ - "AIC-7850", /* AIC_7850 */ - "AIC-7855", /* AIC_7855 */ - "AIC-7850 Ultra", /* AIC_7860 */ - "AHA-2940A Ultra", /* AIC_7861 */ - "AIC-7870", /* AIC_7870 */ - "AHA-2940", /* AIC_7871 */ - "AHA-3940", /* AIC_7872 */ - "AHA-3985", /* AIC_7873 */ - "AHA-2940 Differential", /* AIC_7874 */ - "AIC-7880 Ultra", /* AIC_7880 */ - "AHA-2940 Ultra", /* AIC_7881 */ - "AHA-3940 Ultra", /* AIC_7882 */ - "AHA-3985 Ultra", /* AIC_7883 */ - "AHA-2940 Ultra Differential" /* AIC_7884 */ + "AIC-7xxx Unknown", /* AIC_NONE */ + "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */ + "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */ + "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */ + "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */ + "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */ + "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */ + "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */ + "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */ + "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */ + "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */ + "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */ + "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */ + "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */ + "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */ + "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */ + "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */ + "Adaptec AHA-2944 Ultra SCSI host adapter" /* AIC_7884 */ }; /* @@ -310,12 +458,17 @@ */ #define DID_RETRY_COMMAND DID_ERROR +#define HSCSIID 0x07 +#define HWSCSIID 0x0F +#define SCSI_RESET 0x040 + /* * EISA/VL-bus stuff */ #define MINSLOT 1 #define MAXSLOT 15 #define SLOTBASE(x) ((x) << 12) +#define BASE_TO_SLOT(x) ((x) >> 12) /* * Standard EISA Host ID regs (Offset from slot base) @@ -334,14 +487,6 @@ #define INTDEF 0x5C /* Interrupt Definition Register */ /* - * Some defines for the HCNTRL register. - */ -#define REQ_PAUSE IRQMS | INTEN | PAUSE -#define UNPAUSE_274X IRQMS | INTEN -#define UNPAUSE_284X INTEN -#define UNPAUSE_294X IRQMS | INTEN - -/* * AIC-78X0 PCI registers */ #define CLASS_PROGIF_REVID 0x08 @@ -377,7 +522,7 @@ * each word, while the C56 and C66 (4096 bits) use 8 bits to * address each word. */ -typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type; +typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type; /* * @@ -417,15 +562,15 @@ /* * Host Adapter Control Bits */ -/* UNUSED 0x0001 */ +#define CFAUTOTERM 0x0001 /* Perform Auto termination */ #define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */ #define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */ #define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */ -#define CFSTERM 0x0004 /* SCSI low byte termination (non-wide cards) */ +#define CFSTERM 0x0004 /* SCSI low byte termination */ #define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */ #define CFSPARITY 0x0010 /* SCSI parity */ #define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */ -#define CFRESETB 0x0040 /* reset SCSI bus at IC initialization */ +#define CFRESETB 0x0040 /* reset SCSI bus at boot */ /* UNUSED 0xFF80 */ unsigned short adapter_control; /* word 17 */ @@ -448,36 +593,17 @@ unsigned short checksum; /* word 31 */ }; - -#define SCSI_RESET 0x040 - -/* - * Pause the sequencer and wait for it to actually stop - this - * is important since the sequencer can disable pausing for critical - * sections. - */ -#define PAUSE_SEQUENCER(p) \ - synchronize_irq(); \ - outb(p->pause, HCNTRL + p->base); \ - while ((inb(HCNTRL + p->base) & PAUSE) == 0) \ - ; \ - -/* - * Unpause the sequencer. Unremarkable, yet done often enough to - * warrant an easy way to do it. - */ -#define UNPAUSE_SEQUENCER(p) \ - outb(p->unpause, HCNTRL + p->base) - -/* - * Restart the sequencer program from address zero - */ -#define RESTART_SEQUENCER(p) \ - do { \ - outb(SEQRESET | FASTMODE, SEQCTL + p->base); \ - } while (inb(SEQADDR0 + p->base) != 0 && \ - inb(SEQADDR1 + p->base) != 0); \ - UNPAUSE_SEQUENCER(p); +#define SELBUS_MASK 0x0a +#define SELNARROW 0x00 +#define SELBUSB 0x08 +#define SINGLE_BUS 0x00 + +#define SCB_TARGET(scb) \ + (((scb)->hscb->target_channel_lun & TID) >> 4) +#define SCB_LUN(scb) \ + ((scb)->hscb->target_channel_lun & LID) +#define SCB_IS_SCSIBUS_B(scb) \ + (((scb)->hscb->target_channel_lun & SELBUSB) != 0) /* * If an error occurs during a data transfer phase, run the command @@ -541,12 +667,6 @@ static int aic7xxx_spurious_count; /* - * The driver keeps up to four scb structures per card in memory. Only the - * first 25 bytes of the structure are valid for the hardware, the rest used - * for driver level bookkeeping. - */ - -/* * As of Linux 2.1, the mid-level SCSI code uses virtual addresses * in the scatter-gather lists. We need to convert the virtual * addresses to physical addresses. @@ -559,20 +679,28 @@ /* * Maximum number of SG segments these cards can support. */ -#define MAX_SG 256 +#define AIC7XXX_MAX_SG 27 -struct aic7xxx_scb { +/* + * The maximum number of SCBs we could have for ANY type + * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE + * SEQUENCER CODE IF THIS IS MODIFIED! + */ +#define AIC7XXX_MAXSCB 255 + + +struct aic7xxx_hwscb { /* ------------ Begin hardware supported fields ---------------- */ /* 0*/ unsigned char control; /* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */ /* 2*/ unsigned char target_status; /* 3*/ unsigned char SG_segment_count; -/* 4*/ unsigned char SG_list_pointer[4] __attribute__ ((packed)); +/* 4*/ unsigned int SG_list_pointer; /* 8*/ unsigned char residual_SG_segment_count; -/* 9*/ unsigned char residual_data_count[3] __attribute__ ((packed)); -/*12*/ unsigned char data_pointer[4] __attribute__ ((packed)); -/*16*/ unsigned int data_count __attribute__ ((packed)); /* must be 32 bits */ -/*20*/ unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed)); +/* 9*/ unsigned char residual_data_count[3]; +/*12*/ unsigned int data_pointer; +/*16*/ unsigned int data_count; +/*20*/ unsigned int SCSI_cmd_pointer; /*24*/ unsigned char SCSI_cmd_length; /*25*/ u_char tag; /* Index into our kernel SCB array. * Also used as the tag for tagged I/O @@ -580,29 +708,47 @@ #define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download * via PIO to initialize a transaction. */ -/*26*/ u_char next; /* Used to thread SCBs awaiting selection +/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection * or disconnected down in the sequencer. */ - /*-----------------end of hardware supported fields----------------*/ - Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ - struct aic7xxx_scb *q_next; /* next scb in queue */ -#define SCB_FREE 0x00 -#define SCB_ACTIVE 0x01 -#define SCB_ABORTED 0x02 -#define SCB_DEVICE_RESET 0x04 -#define SCB_IMMED 0x08 -#define SCB_SENSE 0x10 -#define SCB_QUEUED_FOR_DONE 0x40 -#define SCB_PAGED_OUT 0x80 -#define SCB_WAITINGQ 0x100 -#define SCB_ASSIGNEDQ 0x200 -#define SCB_SENTORDEREDTAG 0x400 -#define SCB_IN_PROGRESS (SCB_ACTIVE | SCB_PAGED_OUT | \ - SCB_WAITINGQ | SCB_ASSIGNEDQ) - int state; /* current state of scb */ - unsigned int position; /* Position in scb array */ - struct hw_scatterlist sg_list[MAX_SG]; /* SG list in adapter format */ - unsigned char sense_cmd[6]; /* Allocate 6 characters for sense command */ +/*27*/ unsigned char prev; +/*28*/ unsigned int pad; /* + * Unused by the kernel, but we require + * the padding so that the array of + * hardware SCBs is alligned on 32 byte + * boundaries so the sequencer can index + */ +}; + +typedef enum { + SCB_FREE = 0x0000, + SCB_ACTIVE = 0x0001, + SCB_ABORTED = 0x0002, + SCB_DEVICE_RESET = 0x0004, + SCB_SENSE = 0x0008, + SCB_TIMEDOUT = 0x0010, + SCB_QUEUED_FOR_DONE = 0x0020, + SCB_RECOVERY_SCB = 0x0040, + SCB_WAITINGQ = 0x0080, + SCB_ASSIGNEDQ = 0x0100, + SCB_SENTORDEREDTAG = 0x0200, + SCB_MSGOUT_SDTR = 0x0400, + SCB_MSGOUT_WDTR = 0x0800, + SCB_ABORT = 0x1000, + SCB_QUEUED_ABORT = 0x2000 +} scb_flag_type; + +struct aic7xxx_scb { + struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */ + Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ + struct aic7xxx_scb *q_next; /* next scb in queue */ + scb_flag_type flags; /* current state of scb */ + struct hw_scatterlist *sg_list; /* SG list in adapter format */ + unsigned char sg_count; + unsigned char sense_cmd[6]; /* + * Allocate 6 characters for + * sense command. + */ }; /* @@ -627,20 +773,17 @@ generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 }; typedef struct { + struct aic7xxx_hwscb *hscbs; scb_queue_type free_scbs; /* * SCBs assigned to free slot on * card (no paging required) */ - int numscbs; /* current number of scbs */ - int activescbs; /* active scbs */ -} scb_usage_type; - -/* - * The maximum number of SCBs we could have for ANY type - * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE - * SEQUENCER CODE IF THIS IS MODIFIED! - */ -#define AIC7XXX_MAXSCB 255 + unsigned char numscbs; /* current number of scbs */ + unsigned char maxhscbs; /* hardware scbs */ + unsigned char maxscbs; /* max scbs including pageable scbs */ + struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; + unsigned int reserve[100]; +} scb_data_type; /* * Define a structure used for each host adapter, only one per IRQ. @@ -648,17 +791,26 @@ struct aic7xxx_host { struct Scsi_Host *host; /* pointer to scsi host */ int host_no; /* SCSI host number */ + int instance; /* aic7xxx instance number */ + int scsi_id; /* host adapter SCSI ID */ + int scsi_id_b; /* channel B for twin adapters */ + int irq; /* IRQ for this adapter */ int base; /* card base address */ - int maxhscbs; /* hardware SCBs */ - int maxscbs; /* max SCBs (including pageable) */ -#define A_SCANNED 0x0001 -#define B_SCANNED 0x0002 -#define EXTENDED_TRANSLATION 0x0004 -#define HAVE_SEEPROM 0x0008 -#define ULTRA_ENABLED 0x0010 -#define PAGE_ENABLED 0x0020 -#define IN_ISR 0x0040 -#define USE_DEFAULTS 0x0080 + unsigned int mbase; /* I/O memory address */ + volatile unsigned char *maddr; /* memory mapped address */ +#define A_SCANNED 0x0001 +#define B_SCANNED 0x0002 +#define EXTENDED_TRANSLATION 0x0004 +#define FLAGS_CHANNEL_B_PRIMARY 0x0008 +#define MULTI_CHANNEL 0x0010 +#define ULTRA_ENABLED 0x0020 +#define PAGE_ENABLED 0x0040 +#define USE_DEFAULTS 0x0080 +#define BIOS_ENABLED 0x0100 +#define IN_ISR 0x0200 +#define IN_TIMEOUT 0x0400 +#define SHARED_SCBDATA 0x0800 +#define HAVE_SEEPROM 0x1000 unsigned int flags; unsigned int isr_count; /* Interrupt count */ unsigned short needsdtr_copy; /* default config */ @@ -669,36 +821,22 @@ unsigned short wdtr_pending; unsigned short orderedtag; unsigned short discenable; /* Targets allowed to disconnect */ - aha_type type; /* card type */ - aha_chip_type chip_type; /* chip base type */ + aha_chip_type chip_type; /* card type */ + aha_chip_class_type chip_class; aha_bus_type bus_type; /* normal/twin/wide bus */ - char * mbase; /* I/O memory address */ - unsigned char chan_num; /* for 3940/3985, channel number */ + unsigned char chan_num; /* for 39xx, channel number */ unsigned char unpause; /* unpause value for HCNTRL */ unsigned char pause; /* pause value for HCNTRL */ unsigned char qcntmask; - struct seeprom_config seeprom; + unsigned char qfullcount; + unsigned char curqincnt; struct Scsi_Host *next; /* allow for multiple IRQs */ - struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; /* active commands */ - struct aic7xxx_scb *pagedout_ntscbs[16]; /* - * paged-out, non-tagged scbs - * indexed by target. - */ - scb_queue_type page_scbs; /* - * SCBs that will require paging - * before use (no assigned slot) - */ + unsigned char activescbs; /* active scbs */ scb_queue_type waiting_scbs; /* - * SCBs waiting to be paged and - * started. - */ - scb_queue_type assigned_scbs; /* - * SCBs that were waiting but have - * have now been assigned a slot - * by aic7xxx_free_scb + * SCBs waiting for space in + * the QINFIFO. */ - scb_usage_type scb_usage; - scb_usage_type *scb_link; + scb_data_type *scb_data; struct aic7xxx_cmd_queue { Scsi_Cmnd *head; @@ -710,6 +848,7 @@ #define BUS_DEVICE_RESET_PENDING 0x02 int flags; int commands_sent; + int active_cmds; } device_status[16]; #ifdef AIC7XXX_PROC_STATS /* @@ -735,34 +874,10 @@ #endif /* AIC7XXX_PROC_STATS */ }; -struct aic7xxx_host_config { - int irq; /* IRQ number */ - int mbase; /* memory base address*/ - int base; /* I/O base address*/ - int maxhscbs; /* hardware SCBs */ - int maxscbs; /* max SCBs (including pageable) */ - int unpause; /* unpause value for HCNTRL */ - int pause; /* pause value for HCNTRL */ - int scsi_id; /* host SCSI ID */ - int scsi_id_b; /* host SCSI ID B channel for twin cards */ - unsigned int flags; /* used the same as struct aic7xxx_host flags */ - int chan_num; /* for 3940/3985, channel number */ - unsigned char busrtime; /* bus release time */ - unsigned char bus_speed; /* bus speed */ - unsigned char qcntmask; - aha_type type; /* card type */ - aha_chip_type chip_type; /* chip base type */ - aha_bus_type bus_type; /* normal/twin/wide bus */ - aha_status_type bios; /* BIOS is enabled/disabled */ - aha_status_type parity; /* bus parity enabled/disabled */ - aha_status_type low_term; /* bus termination low byte */ - aha_status_type high_term; /* bus termination high byte (wide cards only) */ -}; - /* * Valid SCSIRATE values. (p. 3-17) - * Provides a mapping of transfer periods in ns to the proper value to - * stick in the scsiscfr reg to use that transfer rate. + * Provides a mapping of transfer periods in ns/4 to the proper value to + * stick in the SCSIRATE reg to use that transfer rate. */ static struct { short period; @@ -771,17 +886,17 @@ short rate; const char *english; } aic7xxx_syncrates[] = { - { 50, 0x100, "20.0" }, - { 62, 0x110, "16.0" }, - { 75, 0x120, "13.4" }, - { 100, 0x000, "10.0" }, - { 125, 0x010, "8.0" }, - { 150, 0x020, "6.67" }, - { 175, 0x030, "5.7" }, - { 200, 0x040, "5.0" }, - { 225, 0x050, "4.4" }, - { 250, 0x060, "4.0" }, - { 275, 0x070, "3.6" } + { 12, 0x100, "20.0" }, + { 15, 0x110, "16.0" }, + { 18, 0x120, "13.4" }, + { 25, 0x000, "10.0" }, + { 31, 0x010, "8.0" }, + { 37, 0x020, "6.67" }, + { 43, 0x030, "5.7" }, + { 50, 0x040, "5.0" }, + { 56, 0x050, "4.4" }, + { 62, 0x060, "4.0" }, + { 68, 0x070, "3.6" } }; static int num_aic7xxx_syncrates = @@ -790,166 +905,51 @@ #ifdef CONFIG_PCI static int number_of_3940s = 0; static int number_of_3985s = 0; -#ifdef AIC7XXX_SHARE_SCBS -static scb_usage_type *shared_3985_scbs = NULL; -#endif -#endif CONFIG_PCI +#endif /* CONFIG_PCI */ #ifdef AIC7XXX_DEBUG -static void -debug_config(struct aic7xxx_host_config *p) -{ - int scsi_conf; - unsigned char brelease; - unsigned char dfthresh; - - static int DFT[] = { 0, 50, 75, 100 }; - static int SST[] = { 256, 128, 64, 32 }; - static const char *BUSW[] = { "", "-TWIN", "-WIDE" }; - - scsi_conf = inb(SCSICONF + p->base); - - /* - * Scale the Data FIFO Threshhold and the Bus Release Time; they are - * stored in formats compatible for writing to sequencer registers. - */ - dfthresh = p->bus_speed >> 6; - - if (p->chip_type == AIC_777x) - { - brelease = p->busrtime >> 2; - } - else - { - brelease = p->busrtime; - } - if (brelease == 0) - { - brelease = 2; - } - - switch (p->type) - { - case AIC_7770: - case AIC_7771: - printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type], - p->base >> 12); - break; - - case AIC_284x: - printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type], - p->base >> 12); - break; - - case AIC_7850: - case AIC_7855: - case AIC_7860: - case AIC_7861: - case AIC_7870: - case AIC_7871: - case AIC_7872: - case AIC_7873: - case AIC_7874: - case AIC_7880: - case AIC_7881: - case AIC_7882: - case AIC_7883: - case AIC_7884: - printk("%s%s (PCI-bus), I/O 0x%x, Mem 0x%x:\n", board_names[p->type], - BUSW[p->bus_type], p->base, p->mbase); - break; - - default: - panic("aic7xxx: (debug_config) internal error.\n"); - } - - printk(" irq %d\n" - " bus release time %d bclks\n" - " data fifo threshold %d%%\n", - p->irq, - brelease, - DFT[dfthresh]); - - printk(" SCSI CHANNEL A:\n" - " scsi id %d\n" - " scsi selection timeout %d ms\n" - " scsi bus reset at power-on %sabled\n", - scsi_conf & 0x07, - SST[(scsi_conf >> 3) & 0x03], - (scsi_conf & 0x40) ? "en" : "dis"); - - if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN)) - { - /* - * Set the parity for 7770 based cards. - */ - p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED; - } - if (p->parity != AIC_UNKNOWN) - { - printk(" scsi bus parity %sabled\n", - (p->parity == AIC_ENABLED) ? "en" : "dis"); - } - - if ((p->type == AIC_7770) || (p->type == AIC_7771)) - { - p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED; - } - if (p->low_term != AIC_UNKNOWN) - { - printk(" scsi bus termination (low byte) %sabled\n", - (p->low_term == AIC_ENABLED) ? "en" : "dis"); - } - if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN)) - { - printk(" scsi bus termination (high byte) %sabled\n", - (p->high_term == AIC_ENABLED) ? "en" : "dis"); - } -} - #if 0 static void debug_scb(struct aic7xxx_scb *scb) { - printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n", - scb->control, scb->target_channel_lun, scb->SG_segment_count, - (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) | - (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0], - (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) | - (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0], - scb->SCSI_cmd_length); - printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n", - (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status, - scb->residual_SG_segment_count, - ((scb->residual_data_count[2] << 16) | - (scb->residual_data_count[1] << 8) | - (scb->residual_data_count[0])); - printk("data ptr 0x%x, data count %d, next waiting %d\n", - (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) | - (scb->data_pointer[1] << 8) | scb->data_pointer[0], - scb->data_count, scb->next_waiting); - printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n", - (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state, - scb->position); + struct aic7xxx_hwscb *hscb = scb->hscb; + + printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n", + scb, + hscb->control, + hscb->target_channel_lun, + hscb->SCSI_cmd_length, + hscb->SCSI_cmd_pointer ); + printk(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n", + hscb->data_count, + hscb->data_pointer, + hscb->SG_segment_count, + hscb->SG_list_pointer); + printk(" sg_addr:%lx sg_len:%ld\n", + hscb->sg_list[0].address, + hscb->sg_list[0].length); } #endif #else -# define debug_config(x) # define debug_scb(x) #endif AIC7XXX_DEBUG -#define TCL_OF_SCB(x) (((x)->target_channel_lun >> 4) & 0xf), \ - (((x)->target_channel_lun >> 3) & 0x01), \ - ((x)->target_channel_lun & 0x07) +#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \ + (((scb->hscb)->target_channel_lun >> 3) & 0x01), \ + ((scb->hscb)->target_channel_lun & 0x07) -#define TARGET_INDEX(x) ((x)->target | ((x)->channel << 3)) +#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \ + (((scb->hscb)->target_channel_lun >> 3) & 0x01) + +#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1) + +#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3)) /* * XXX - these options apply unilaterally to _all_ 274x/284x/294x - * cards in the system. This should be fixed, but then, - * does anyone really have more than one in a machine? + * cards in the system. This should be fixed. */ static unsigned int aic7xxx_extended = 0; /* extended translation on? */ static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */ @@ -959,6 +959,53 @@ * 1 use level triggered */ static int aic7xxx_enable_ultra = 0; /* enable ultra SCSI speeds */ +static int aic7xxx_verbose = 0; /* verbose messages */ + + +/**************************************************************************** + * + * These functions are not used yet, but when we do memory mapped + * IO, we'll use them then. + * + ***************************************************************************/ +static inline unsigned char +aic_inb(struct aic7xxx_host *p, long port) +{ + if (p->maddr != NULL) + return (p->maddr[port]); + else + return (inb(p->base + port)); +} + +static inline void +aic_outb(struct aic7xxx_host *p, unsigned char val, long port) +{ + if (p->maddr != NULL) + p->maddr[port] = val; + else + outb(val, p->base + port); +} + +static inline void +aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size) +{ + if (p->maddr != NULL) + { + __asm __volatile(" + cld; + 1: lodsb; + movb %%al,(%0); + loop 1b" : + : + "r" ((p)->maddr + (port)), + "S" ((valp)), "c" ((size)) : + "%esi", "%ecx", "%eax"); + } + else + { + outsb(p->base + port, valp, size); + } +} /*+F************************************************************************* * Function: @@ -983,6 +1030,7 @@ { "no_reset", &aic7xxx_no_reset }, { "irq_trigger", &aic7xxx_irq_trigger }, { "ultra", &aic7xxx_enable_ultra }, + { "verbose", &aic7xxx_verbose }, { NULL, NULL } }; @@ -1008,141 +1056,317 @@ /*+F************************************************************************* * Function: - * aic7xxx_loadseq + * pause_sequencer * * Description: - * Load the sequencer code into the controller memory. + * Pause the sequencer and wait for it to actually stop - this + * is important since the sequencer can disable pausing for critical + * sections. *-F*************************************************************************/ -static void -aic7xxx_loadseq(int base) +static inline void +pause_sequencer(struct aic7xxx_host *p) { - static unsigned char seqprog[] = { - /* - * Each sequencer instruction is 29 bits - * long (fill in the excess with zeroes) - * and has to be loaded from least -> most - * significant byte, so this table has the - * byte ordering reversed. - */ -# include "aic7xxx_seq.h" - }; - - /* - * When the AIC-7770 is paused (as on chip reset), the - * sequencer address can be altered and a sequencer - * program can be loaded by writing it, byte by byte, to - * the sequencer RAM port - the Adaptec documentation - * recommends using REP OUTSB to do this, hence the inline - * assembly. Since the address autoincrements as we load - * the program, reset it back to zero afterward. Disable - * sequencer RAM parity error detection while loading, and - * make sure the LOADRAM bit is enabled for loading. - */ - outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base); - - outsb(SEQRAM + base, seqprog, sizeof(seqprog)); - - /* - * WARNING! This is a magic sequence! After extensive - * experimentation, it seems that you MUST turn off the - * LOADRAM bit before you play with SEQADDR again, else - * you will end up with parity errors being flagged on - * your sequencer program. (You would also think that - * turning off LOADRAM and setting SEQRESET to reset the - * address to zero would work, but you need to do it twice - * for it to take effect on the address. Timing problem?) - */ - do { - /* - * Actually, reset it until - * the address shows up as - * zero just to be safe.. - */ - outb(SEQRESET | FASTMODE, SEQCTL + base); - } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0)); + outb(p->pause, p->base + HCNTRL); + while ((inb(p->base + HCNTRL) & PAUSE) == 0) + { + ; + } } /*+F************************************************************************* * Function: - * aic7xxx_delay + * unpause_sequencer * * Description: - * Delay for specified amount of time. + * Unpause the sequencer. Unremarkable, yet done often enough to + * warrant an easy way to do it. *-F*************************************************************************/ -static void -aic7xxx_delay(int seconds) +static inline void +unpause_sequencer(struct aic7xxx_host *p, int unpause_always) { - unsigned long i; - - i = jiffies + (seconds * HZ); /* compute time to stop */ - - while (jiffies < i) + if (unpause_always || + ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)) { - ; /* Do nothing! */ + outb(p->unpause, p->base + HCNTRL); } } /*+F************************************************************************* * Function: - * rcs_version + * restart_sequencer * * Description: - * Return a string containing just the RCS version number from either - * an Id or Revision RCS clause. + * Restart the sequencer program from address zero. This assumes + * that the sequencer is already paused. *-F*************************************************************************/ -static const char * -rcs_version(const char *version_info) +static inline void +restart_sequencer(struct aic7xxx_host *p) { - static char buf[10]; - char *bp, *ep; + /* Set the sequencer address to 0. */ + outb(0, p->base + SEQADDR0); + outb(0, p->base + SEQADDR1); - bp = NULL; - strcpy(buf, "????"); - if (!strncmp(version_info, "$Id: ", 5)) + /* + * Reset and unpause the sequencer. The reset is suppose to + * start the sequencer running, but we do an unpause to make + * sure. + */ + outb(SEQRESET | FASTMODE, p->base + SEQCTL); + + unpause_sequencer(p, /*unpause_always*/ TRUE); +} + + +/*+F************************************************************************* + * Function: + * aic7xxx_next_patch + * + * Description: + * Find the next patch to download. + *-F*************************************************************************/ +static struct patch * +aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr) +{ + while (cur_patch != NULL) { - if ((bp = strchr(version_info, ' ')) != NULL) + if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE)) + || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE)) + || (instrptr >= cur_patch->end)) { - bp++; - if ((bp = strchr(bp, ' ')) != NULL) + /* + * Either we want to keep this section of code, or we have consumed + * this patch. Skip to the next patch. + */ + cur_patch++; + if (cur_patch->options == 0) { - bp++; + /* Out of patches. */ + cur_patch = NULL; } } - } - else - { - if (!strncmp(version_info, "$Revision: ", 11)) + else { - if ((bp = strchr(version_info, ' ')) != NULL) - { - bp++; - } + /* Found an OK patch. */ + break; } } + return (cur_patch); +} - if (bp != NULL) - { - if ((ep = strchr(bp, ' ')) != NULL) - { - register int len = ep - bp; - strncpy(buf, bp, len); - buf[len] = '\0'; +/*+F************************************************************************* + * Function: + * aic7xxx_download_instr + * + * Description: + * Find the next patch to download. + *-F*************************************************************************/ +static void +aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr) +{ + unsigned char opcode; + struct ins_format3 *instr; + + instr = (struct ins_format3 *) &seqprog[instrptr * 4]; + /* Pull the opcode */ + opcode = instr->opcode_addr >> 1; + switch (opcode) + { + case AIC_OP_JMP: + case AIC_OP_JC: + case AIC_OP_JNC: + case AIC_OP_CALL: + case AIC_OP_JNE: + case AIC_OP_JNZ: + case AIC_OP_JE: + case AIC_OP_JZ: + { + int address_offset; + struct ins_format3 new_instr; + unsigned int address; + struct patch *patch; + int i; + + address_offset = 0; + new_instr = *instr; /* Strucure copy */ + address = new_instr.address; + address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8; + for (i = 0; i < NUMBER(patches); i++) + { + patch = &patches[i]; + if ((((patch->options & options) == 0) && (patch->negative == FALSE)) || + (((patch->options & options) != 0) && (patch->negative == TRUE))) + { + if (address >= patch->end) + { + address_offset += patch->end - patch->begin; + } + } + } + address -= address_offset; + new_instr.address = address &0xFF; + new_instr.opcode_addr &= ~ADDR_HIGH_BIT; + new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT; + outsb(p->base + SEQRAM, &new_instr.immediate, 4); + break; } - } - return buf; + case AIC_OP_OR: + case AIC_OP_AND: + case AIC_OP_XOR: + case AIC_OP_ADD: + case AIC_OP_ADC: + case AIC_OP_ROL: + outsb(p->base + SEQRAM, &instr->immediate, 4); + break; + + default: + panic("aic7xxx: Unknown opcode encountered in sequencer program."); + break; + } } + /*+F************************************************************************* * Function: - * aic7xxx_info + * aic7xxx_loadseq * * Description: - * Return a string describing the driver. + * Load the sequencer code into the controller memory. *-F*************************************************************************/ -const char * -aic7xxx_info(struct Scsi_Host *notused) +static void +aic7xxx_loadseq(struct aic7xxx_host *p) +{ + int options; + struct patch *cur_patch; + int i; + int downloaded; + + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Downloading sequencer code..."); + } + options = 1; /* Code for all options. */ + downloaded = 0; + if ((p->flags & ULTRA_ENABLED) != 0) + options |= ULTRA; + if (p->bus_type == AIC_TWIN) + options |= TWIN_CHANNEL; + if (p->scb_data->maxscbs > p->scb_data->maxhscbs) + options |= SCB_PAGING; + + cur_patch = patches; + outb(PERRORDIS | LOADRAM, p->base + SEQCTL); + outb(0, p->base + SEQADDR0); + outb(0, p->base + SEQADDR1); + + for (i = 0; i < sizeof(seqprog) / 4; i++) + { + cur_patch = aic7xxx_next_patch(cur_patch, options, i); + if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i)) + { + /* Skip this instruction for this configuration. */ + continue; + } + aic7xxx_download_instr(p, options, i); + downloaded++; + } + + outb(FASTMODE, p->base + SEQCTL); + outb(0, p->base + SEQADDR0); + outb(0, p->base + SEQADDR1); + + if (aic7xxx_verbose) + { + printk(" %d instructions downloaded\n", downloaded); + } +} + +/*+F************************************************************************* + * Function: + * aic7xxx_delay + * + * Description: + * Delay for specified amount of time. We use udelay because the timer + * interrupt is not guaranteed to be enabled. This will cause an + * infinite loop since jiffies (clock ticks) is not updated. + *-F*************************************************************************/ +static void +aic7xxx_delay(int seconds) +{ + int i; + + /* + * Call udelay() for 1 millisecond inside a loop for + * the requested amount of seconds. + */ + for (i=0; i < seconds*1000; i++) + { + udelay(1000); /* Delay for 1 millisecond. */ + } +} + +/*+F************************************************************************* + * Function: + * rcs_version + * + * Description: + * Return a string containing just the RCS version number from either + * an Id or Revision RCS clause. + *-F*************************************************************************/ +const char * +rcs_version(const char *version_info) +{ + static char buf[10]; + char *bp, *ep; + + bp = NULL; + strcpy(buf, "????"); + if (!strncmp(version_info, "$Id: ", 5)) + { + if ((bp = strchr(version_info, ' ')) != NULL) + { + bp++; + if ((bp = strchr(bp, ' ')) != NULL) + { + bp++; + } + } + } + else + { + if (!strncmp(version_info, "$Revision: ", 11)) + { + if ((bp = strchr(version_info, ' ')) != NULL) + { + bp++; + } + } + } + + if (bp != NULL) + { + if ((ep = strchr(bp, ' ')) != NULL) + { + register int len = ep - bp; + + strncpy(buf, bp, len); + buf[len] = '\0'; + } + } + + return buf; +} + +/*+F************************************************************************* + * Function: + * aic7xxx_info + * + * Description: + * Return a string describing the driver. + *-F*************************************************************************/ +const char * +aic7xxx_info(struct Scsi_Host *notused) { static char buffer[128]; @@ -1150,8 +1374,10 @@ strcat(buffer, rcs_version(AIC7XXX_C_VERSION)); strcat(buffer, "/"); strcat(buffer, rcs_version(AIC7XXX_H_VERSION)); +#if 0 strcat(buffer, "/"); strcat(buffer, rcs_version(AIC7XXX_SEQ_VER)); +#endif return buffer; } @@ -1161,9 +1387,12 @@ * aic7xxx_length * * Description: - * How much data should be transferred for this SCSI command? Stop - * at segment sg_last if it's a scatter-gather command so we can - * compute underflow easily. + * How much data should be transferred for this SCSI command? Assume + * all segments are to be transferred except for the last sg_last + * segments. This will allow us to compute underflow easily. To + * calculate the total length of the command, use sg_last = 0. To + * calculate the length of all but the last 2 SG segments, use + * sg_last = 2. *-F*************************************************************************/ static unsigned aic7xxx_length(Scsi_Cmnd *cmd, int sg_last) @@ -1177,7 +1406,7 @@ if (cmd->use_sg) { - for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++) + for (i = length = 0; i < segments; i++) { length += sg[i].length; } @@ -1199,9 +1428,9 @@ *-F*************************************************************************/ static void aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate, - short period, unsigned char offset, int target, char channel) + unsigned char *period, unsigned char *offset, int target, char channel) { - int i; + int i = num_aic7xxx_syncrates; unsigned long ultra_enb_addr; unsigned char ultra_enb, sxfrctl0; @@ -1209,11 +1438,11 @@ * If the offset is 0, then the device is requesting asynchronous * transfers. */ - if (offset != 0) + if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0) { for (i = 0; i < num_aic7xxx_syncrates; i++) { - if ((aic7xxx_syncrates[i].period - period) >= 0) + if (*period <= aic7xxx_syncrates[i].period) { /* * Watch out for Ultra speeds when ultra is not enabled and @@ -1229,99 +1458,57 @@ */ continue; } - *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F); + *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F); + *period = aic7xxx_syncrates[i].period; - /* - * Ensure Ultra mode is set properly for this target. - */ - ultra_enb_addr = ULTRA_ENB; - if ((channel == 'B') || (target > 7)) - { - ultra_enb_addr++; - } - ultra_enb = inb(p->base + ultra_enb_addr); - sxfrctl0 = inb(p->base + SXFRCTL0); - if (aic7xxx_syncrates[i].rate & ULTRA_SXFR) - { - ultra_enb |= 0x01 << (target & 0x07); - sxfrctl0 |= ULTRAEN; - } - else + if (aic7xxx_verbose) { - ultra_enb &= ~(0x01 << (target & 0x07)); - sxfrctl0 &= ~ULTRAEN; + printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, " + "offset %d.\n", p->host_no, target, channel, + aic7xxx_syncrates[i].english, *offset); } - outb(ultra_enb, p->base + ultra_enb_addr); - outb(sxfrctl0, p->base + SXFRCTL0); - - printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, " - "offset %d.\n", p->host_no, target, channel, - aic7xxx_syncrates[i].english, offset); - return; + break; } } } - /* - * Default to asynchronous transfer - */ - *scsirate = 0; - printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n", - p->host_no, target, channel); -} - -/*+F************************************************************************* - * Function: - * aic7xxx_putscb - * - * Description: - * Transfer a SCB to the controller. - *-F*************************************************************************/ -static inline void -aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) -{ - int base = p->base; - - outb(SCBAUTO, SCBCNT + base); + if (i >= num_aic7xxx_syncrates) + { + /* + * Use asynchronous transfers. + */ + *scsirate = 0; + *period = 0; + *offset = 0; + if (aic7xxx_verbose) + { + printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n", + p->host_no, target, channel); + } + } /* - * By turning on the SCB auto increment, any reference - * to the SCB I/O space postincrements the SCB address - * we're looking at. So turn this on and dump the relevant - * portion of the SCB to the card. - * - * We can do 16bit transfers on all but 284x. + * Ensure Ultra mode is set properly for this target. */ - if (p->type == AIC_284x) + ultra_enb_addr = ULTRA_ENB; + if ((channel == 'B') || (target > 7)) + { + ultra_enb_addr++; + } + ultra_enb = inb(p->base + ultra_enb_addr); + sxfrctl0 = inb(p->base + SXFRCTL0); + if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR)) { - outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE); + ultra_enb |= 0x01 << (target & 0x07); + sxfrctl0 |= FAST20; } else { - outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4); + ultra_enb &= ~(0x01 << (target & 0x07)); + sxfrctl0 &= ~FAST20; } - - outb(0, SCBCNT + base); -} - -/*+F************************************************************************* - * Function: - * aic7xxx_getscb - * - * Description: - * Get a SCB from the controller. - *-F*************************************************************************/ -static inline void -aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) -{ - int base = p->base; - - /* - * This is almost identical to aic7xxx_putscb(). - */ - outb(SCBAUTO, SCBCNT + base); - insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE); - outb(0, SCBCNT + base); + outb(ultra_enb, p->base + ultra_enb_addr); + outb(sxfrctl0, p->base + SXFRCTL0); } /*+F************************************************************************* @@ -1375,6 +1562,47 @@ /*+F************************************************************************* * Function: + * scbq_remove + * + * Description: + * Removes an SCB from the list. + * + *-F*************************************************************************/ +static inline void +scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb) +{ + if (queue->head == scb) + { + /* At beginning of queue, remove from head. */ + scbq_remove_head(queue); + } + else + { + struct aic7xxx_scb *curscb = queue->head; + + /* + * Search until the next scb is the one we're looking for, or + * we run out of queue. + */ + while ((curscb != NULL) && (curscb->q_next != scb)) + { + curscb = curscb->q_next; + } + if (curscb != NULL) + { + /* Found it. */ + curscb->q_next = scb->q_next; + if (scb->q_next == NULL) + { + /* Update the tail when removing the tail. */ + queue->tail = curscb; + } + } + } +} + +/*+F************************************************************************* + * Function: * scbq_insert_tail * * Description: @@ -1404,23 +1632,87 @@ * to be reset and all devices on that channel must be aborted. *-F*************************************************************************/ static int -aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel) +aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel, + int lun, unsigned char tag) { - int targ = (scb->target_channel_lun >> 4) & 0x0F; - char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F; + char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + int slun = scb->hscb->target_channel_lun & 0x07; + int match; #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n", - target, channel, targ, chan); + printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n", + scb->cmd->device->host->host_no, target, channel, targ, chan); #endif - if (target == ALL_TARGETS) + match = ((chan == channel) || (channel == ALL_CHANNELS)); + if (match != 0) + match = ((targ == target) || (target == ALL_TARGETS)); + if (match != 0) + match = ((lun == slun) || (lun == ALL_LUNS)); + if (match != 0) + match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL)); + + return (match); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_add_curscb_to_free_list + * + * Description: + * Adds the current scb (in SCBPTR) to the list of free SCBs. + *-F*************************************************************************/ +static void +aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p) +{ + /* + * Invalidate the tag so that aic7xxx_find_scb doesn't think + * it's active + */ + outb(SCB_LIST_NULL, p->base + SCB_TAG); + + outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT); + outb(inb(p->base + SCBPTR), p->base + FREE_SCBH); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_rem_scb_from_disc_list + * + * Description: + * Removes the current SCB from the disconnected list and adds it + * to the free list. + *-F*************************************************************************/ +static unsigned char +aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr) +{ + unsigned char next; + unsigned char prev; + + outb(scbptr, p->base + SCBPTR); + next = inb(p->base + SCB_NEXT); + prev = inb(p->base + SCB_PREV); + + outb(0, p->base + SCB_CONTROL); + + aic7xxx_add_curscb_to_free_list(p); + + if (prev != SCB_LIST_NULL) { - return (chan == channel); + outb(prev, p->base + SCBPTR); + outb(next, p->base + SCB_NEXT); } else { - return ((chan == channel) && (targ == target)); + outb(next, p->base + DISCONNECTED_SCBH); + } + + if (next != SCB_LIST_NULL) + { + outb(next, p->base + SCBPTR); + outb(prev, p->base + SCB_PREV); } + return next; } /*+F************************************************************************* @@ -1428,51 +1720,93 @@ * aic7xxx_busy_target * * Description: - * Set the specified target active. + * Set the specified target busy. *-F*************************************************************************/ static void -aic7xxx_busy_target(unsigned char target, char channel, int base) +aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target, + char channel, unsigned char scbid) +{ + unsigned char active_scb; + unsigned char info_scb; + unsigned int scb_offset; + + info_scb = target / 4; + if (channel == 'B') + info_scb = info_scb + 2; + + active_scb = inb(p->base + SCBPTR); + outb(info_scb, p->base + SCBPTR); + scb_offset = SCB_BUSYTARGETS + (target & 0x03); + outb(scbid, p->base + scb_offset); + outb(active_scb, p->base + SCBPTR); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_index_busy_target + * + * Description: + * Returns the index of the busy target, and optionally sets the + * target inactive. + *-F*************************************************************************/ +static unsigned char +aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target, + char channel, int unbusy) { - unsigned char active; - unsigned long active_port = ACTIVE_A + base; + unsigned char active_scb; + unsigned char info_scb; + unsigned char busy_scbid; + unsigned int scb_offset; + + info_scb = target / 4; + if (channel == 'B') + info_scb = info_scb + 2; - if ((target > 0x07) || (channel == 'B')) + active_scb = inb(p->base + SCBPTR); + outb(info_scb, p->base + SCBPTR); + scb_offset = SCB_BUSYTARGETS + (target & 0x03); + busy_scbid = inb(p->base + scb_offset); + if (unbusy) { - /* - * targets on the Second channel or above id 7 store info in byte two - * of ACTIVE - */ - active_port++; + outb(SCB_LIST_NULL, p->base + scb_offset); } - active = inb(active_port); - active |= (0x01 << (target & 0x07)); - outb(active, active_port); + outb(active_scb, p->base + SCBPTR); + return (busy_scbid); } /*+F************************************************************************* * Function: - * aic7xxx_unbusy_target + * aic7xxx_find_scb * * Description: - * Set the specified target inactive. + * Look through the SCB array of the card and attempt to find the + * hardware SCB that corresponds to the passed in SCB. Return + * SCB_LIST_NULL if unsuccessful. This routine assumes that the + * card is already paused. *-F*************************************************************************/ -static void -aic7xxx_unbusy_target(unsigned char target, char channel, int base) +static unsigned char +aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - unsigned char active; - unsigned long active_port = ACTIVE_A + base; + unsigned char saved_scbptr; + unsigned char curindex; - if ((target > 0x07) || (channel == 'B')) + saved_scbptr = inb(p->base + SCBPTR); + curindex = 0; + for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++) { - /* - * targets on the Second channel or above id 7 store info in byte two - * of ACTIVE - */ - active_port++; + outb(curindex, p->base + SCBPTR); + if (inb(p->base + SCB_TAG) == scb->hscb->tag) + { + break; + } + } + outb(saved_scbptr, p->base + SCBPTR); + if (curindex >= p->scb_data->maxhscbs) + { + curindex = SCB_LIST_NULL; } - active = inb(active_port); - active &= ~(0x01 << (target & 0x07)); - outb(active, active_port); + + return (curindex); } /*+F************************************************************************* @@ -1480,68 +1814,60 @@ * aic7xxx_allocate_scb * * Description: - * Get a free SCB either from one already assigned to a hardware - * slot, or one that will require an SCB to be paged out before - * use. If there are none, attempt to allocate a new one. + * Get an SCB from the free list or by allocating a new one. *-F*************************************************************************/ static struct aic7xxx_scb * aic7xxx_allocate_scb(struct aic7xxx_host *p) { - struct aic7xxx_scb *scbp = NULL; - int maxscbs; + struct aic7xxx_scb *scbp = NULL; + struct aic7xxx_hwscb *hscbp = NULL; +#ifdef AGRESSIVE + long processor_flags; + + save_flags(processor_flags); + cli(); +#endif - scbp = p->scb_link->free_scbs.head; + scbp = p->scb_data->free_scbs.head; if (scbp != NULL) { - scbq_remove_head(&p->scb_link->free_scbs); + scbq_remove_head(&p->scb_data->free_scbs); } else { - /* - * This should always be NULL if paging is not enabled. - */ - scbp = p->page_scbs.head; - if (scbp != NULL) - { - scbq_remove_head(&p->page_scbs); - } - else + if (p->scb_data->numscbs < p->scb_data->maxscbs) { - /* - * Set limit the SCB allocation to the maximum number of - * hardware SCBs if paging is not enabled; otherwise use - * the maximum (255). - */ - if (p->flags & PAGE_ENABLED) - maxscbs = p->maxscbs; - else - maxscbs = p->maxhscbs; - if (p->scb_link->numscbs < maxscbs) - { - int scb_index = p->scb_link->numscbs; - int scb_size = sizeof(struct aic7xxx_scb); - - p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA); - scbp = (p->scb_array[scb_index]); - if (scbp != NULL) - { - memset(scbp, 0, sizeof(*scbp)); - scbp->tag = scb_index; - if (scb_index < p->maxhscbs) - scbp->position = scb_index; - else - scbp->position = SCB_LIST_NULL; - p->scb_link->numscbs++; - } + int scb_index = p->scb_data->numscbs; + int scb_size = sizeof(struct aic7xxx_scb) + + sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG; + + scbp = kmalloc(scb_size, GFP_ATOMIC); + if (scbp != NULL) + { + memset(scbp, 0, sizeof(struct aic7xxx_scb)); + hscbp = &p->scb_data->hscbs[scb_index]; + scbp->hscb = hscbp; + scbp->sg_list = (struct hw_scatterlist *) &scbp[1]; + memset(hscbp, 0, sizeof(struct aic7xxx_hwscb)); + hscbp->tag = scb_index; + p->scb_data->numscbs++; + /* + * Place in the scb array; never is removed + */ + p->scb_data->scb_array[scb_index] = scbp; } } } +#ifdef AIC7XXX_DEBUG if (scbp != NULL) { -#ifdef AIC7XXX_DEBUG - p->scb_link->activescbs++; -#endif + p->activescbs++; } +#endif + +#ifdef AGRESSIVE + restore_flags(processor_flags); +#endif return (scbp); } @@ -1581,6 +1907,7 @@ cmd = p->completeq.head; p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble; cmd->host_scribble = NULL; + p->device_status[TARGET_INDEX(cmd)].active_cmds--; cmd->scsi_done(cmd); } p->completeq.tail = NULL; @@ -1591,53 +1918,29 @@ * aic7xxx_free_scb * * Description: - * Free the scb and update the page, waiting, free scb lists. + * Free the scb and insert into the free scb list. *-F*************************************************************************/ static void aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - struct aic7xxx_scb *wscb; + struct aic7xxx_hwscb *hscb; + long flags; + + hscb = scb->hscb; + save_flags(flags); + cli(); - scb->state = SCB_FREE; + scb->flags = SCB_FREE; scb->cmd = NULL; - scb->control = 0; - scb->state = 0; + hscb->control = 0; + hscb->target_status = 0; - if (scb->position == SCB_LIST_NULL) - { - scbq_insert_head(&p->page_scbs, scb); - } - else - { - /* - * If there are any SCBS on the waiting queue, assign the slot of this - * "freed" SCB to the first one. We'll run the waiting queues after - * all command completes for a particular interrupt are completed or - * when we start another command. - */ - wscb = p->waiting_scbs.head; - if (wscb != NULL) - { - scbq_remove_head(&p->waiting_scbs); - wscb->position = scb->position; - scbq_insert_tail(&p->assigned_scbs, wscb); - wscb->state = (wscb->state & ~SCB_WAITINGQ) | SCB_ASSIGNEDQ; - - /* - * The "freed" SCB will need to be assigned a slot before being - * used, so put it in the page_scbs queue. - */ - scb->position = SCB_LIST_NULL; - scbq_insert_head(&p->page_scbs, scb); - } - else - { - scbq_insert_head(&p->scb_link->free_scbs, scb); - } + scbq_insert_head(&p->scb_data->free_scbs, scb); #ifdef AIC7XXX_DEBUG - p->scb_link->activescbs--; /* For debugging purposes. */ + p->activescbs--; /* For debugging purposes. */ #endif - } + + restore_flags(flags); } /*+F************************************************************************* @@ -1652,68 +1955,113 @@ { Scsi_Cmnd *cmd = scb->cmd; + if (scb->flags & SCB_RECOVERY_SCB) + { + p->flags &= ~IN_TIMEOUT; + } + if (cmd->result == DID_OK) + { + if (scb->flags & SCB_ABORTED) + { + cmd->result = (DID_RESET << 16); + } + } + if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0) + { + unsigned short mask; + + mask = 0x01 << TARGET_INDEX(scb->cmd); + if (scb->flags & SCB_MSGOUT_WDTR) + { + p->wdtr_pending &= ~mask; + } + if (scb->flags & SCB_MSGOUT_SDTR) + { + p->sdtr_pending &= ~mask; + } + } aic7xxx_free_scb(p, scb); aic7xxx_queue_cmd_complete(p, cmd); +#ifdef AIC7XXX_PROC_STATS + { + int actual; + + /* + * XXX: we should actually know how much actually transferred + * XXX: for each command, but apparently that's too difficult. + */ + actual = aic7xxx_length(cmd, 0); + if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0) + && (aic7xxx_error(cmd) == 0)) + { + struct aic7xxx_xferstats *sp; + long *ptr; + int x; + + sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07]; + sp->xfers++; + + if (cmd->request.cmd == WRITE) + { + sp->w_total++; + sp->w_total512 += (actual >> 9); + ptr = sp->w_bins; + } + else + { + sp->r_total++; + sp->r_total512 += (actual >> 9); + ptr = sp->r_bins; + } + for (x = 9; x <= 17; x++) + { + if (actual < (1 << x)) + { + ptr[x - 9]++; + break; + } + } + if (x > 17) + { + ptr[x - 9]++; + } + } + } +#endif /* AIC7XXX_PROC_STATS */ } /*+F************************************************************************* * Function: - * aic7xxx_done_aborted_scbs + * aic7xxx_run_done_queue * * Description: - * Calls the scsi_done() for the Scsi_Cmnd of each scb in the - * aborted list, and adds each scb to the free list. + * Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the + * aborted list, and adds each scb to the free list. If complete + * is TRUE, we also process the commands complete list. *-F*************************************************************************/ static void -aic7xxx_done_aborted_scbs(struct aic7xxx_host *p) +aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete) { - Scsi_Cmnd *cmd; struct aic7xxx_scb *scb; int i; - for (i = 0; i < p->scb_link->numscbs; i++) + for (i = 0; i < p->scb_data->numscbs; i++) { - scb = (p->scb_array[i]); - if (scb->state & SCB_QUEUED_FOR_DONE) + scb = p->scb_data->scb_array[i]; + if (scb->flags & SCB_QUEUED_FOR_DONE) { #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n", - scb->position, TCL_OF_SCB(scb)); + printk("(scsi%d:%d:%d) Aborting scb %d\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag); #endif - /* - * Process the command after marking the scb as free - * and adding it to the free list. - */ - cmd = scb->cmd; - p->device_status[TARGET_INDEX(cmd)].flags = 0; - aic7xxx_free_scb(p, scb); - cmd->scsi_done(cmd); /* call the done function */ + aic7xxx_done(p, scb); } } -} - -/*+F************************************************************************* - * Function: - * aic7xxx_add_waiting_scb - * - * Description: - * Add this SCB to the head of the "waiting for selection" list. - *-F*************************************************************************/ -static void -aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb) -{ - unsigned char next; - unsigned char curscb; - - curscb = inb(SCBPTR + base); - next = inb(WAITING_SCBH + base); - - outb(scb->position, SCBPTR + base); - outb(next, SCB_NEXT + base); - outb(scb->position, WAITING_SCBH + base); - - outb(curscb, SCBPTR + base); + if (complete) + { + aic7xxx_done_cmds_complete(p); + } } /*+F************************************************************************* @@ -1726,26 +2074,23 @@ *-F*************************************************************************/ static unsigned char aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb, - unsigned char prev) + unsigned char scbpos, unsigned char prev) { unsigned char curscb, next; - int target = (scb->target_channel_lun >> 4) & 0x0F; - char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A'; - int base = p->base; /* * Select the SCB we want to abort and pull the next pointer out of it. */ - curscb = inb(SCBPTR + base); - outb(scb->position, SCBPTR + base); - next = inb(SCB_NEXT + base); + curscb = inb(p->base + SCBPTR); + outb(scbpos, p->base + SCBPTR); + next = inb(p->base + SCB_NEXT); /* * Clear the necessary fields */ - outb(0, SCB_CONTROL + base); - outb(SCB_LIST_NULL, SCB_NEXT + base); - aic7xxx_unbusy_target(target, channel, base); + outb(0, p->base + SCB_CONTROL); + + aic7xxx_add_curscb_to_free_list(p); /* * Update the waiting list @@ -1755,22 +2100,23 @@ /* * First in the list */ - outb(next, WAITING_SCBH + base); + outb(next, p->base + WAITING_SCBH); } else { /* * Select the scb that pointed to us and update its next pointer. */ - outb(prev, SCBPTR + base); - outb(next, SCB_NEXT + base); + outb(prev, p->base + SCBPTR); + outb(next, p->base + SCB_NEXT); } /* * Point us back at the original scb position and inform the SCSI * system that the command has been aborted. */ - outb(curscb, SCBPTR + base); - scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; + outb(curscb, p->base + SCBPTR); + scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; + scb->flags &= ~SCB_ACTIVE; scb->cmd->result = (DID_RESET << 16); return (next); @@ -1778,6 +2124,75 @@ /*+F************************************************************************* * Function: + * aic7xxx_search_qinfifo + * + * Description: + * Search the queue-in FIFO for matching SCBs and conditionally + * requeue. Returns the number of matching SCBs. + *-F*************************************************************************/ +static int +aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel, + int lun, unsigned char tag, int flags, int requeue) +{ + unsigned char saved_queue[AIC7XXX_MAXSCB]; + int queued = inb(p->base + QINCNT) & p->qcntmask; + int i; + int found; + struct aic7xxx_scb *scbp; + scb_queue_type removed_scbs; + + found = 0; + scbq_init (&removed_scbs); + for (i = 0; i < (queued - found); i++) + { + saved_queue[i] = inb(p->base + QINFIFO); + scbp = p->scb_data->scb_array[saved_queue[i]]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + /* + * We found an scb that needs to be removed. + */ + if (requeue) + { + scbq_insert_head(&removed_scbs, scbp); + } + else + { + scbp->flags = flags; + scbp->flags &= ~SCB_ACTIVE; + /* + * XXX - Don't know what error to use here. + */ + aic7xxx_error(scbp->cmd) = DID_RESET; + } + i--; + found++; + } + } + /* Now put the saved scbs back. */ + for (queued = 0; queued < i; queued++) + outb(saved_queue[queued], p->base + QINFIFO); + + if (requeue) + { + scbp = removed_scbs.head; + while (scbp != NULL) + { + scbq_remove_head(&removed_scbs); + /* + * XXX - Shouldn't we be adding this to the free list? + */ + scbq_insert_head(&p->waiting_scbs, scbp); + scbp->flags |= SCB_WAITINGQ; + scbp = removed_scbs.head; + } + } + + return (found); +} + +/*+F************************************************************************* + * Function: * aic7xxx_reset_device * * Description: @@ -1785,131 +2200,280 @@ * all active and queued scbs for that target/channel. *-F*************************************************************************/ static int -aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel) +aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel, + int lun, unsigned char tag) { - int base = p->base; - struct aic7xxx_scb *scb; + struct aic7xxx_scb *scbp; unsigned char active_scb; int i = 0; - int found = 0; + int found; /* * Restore this when we're done */ - active_scb = inb(SCBPTR + base); + active_scb = inb(p->base + SCBPTR); #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n", - target, channel, active_scb); + printk("(scsi%d:%d:%d) Reset device, active_scb %d\n", + p->host_no, target, CHAN_TO_INT(channel), active_scb); #endif + /* - * Search the QINFIFO. + * Deal with the busy target and linked next issues. */ { - int saved_queue[AIC7XXX_MAXSCB]; - int queued = inb(QINCNT + base) & p->qcntmask; + int min_target, max_target; + unsigned char busy_scbid; - for (i = 0; i < (queued - found); i++) + /* Make all targets 'relative' to bus A. */ + if (target == ALL_TARGETS) { - saved_queue[i] = inb(QINFIFO + base); - outb(saved_queue[i], SCBPTR + base); - scb = (p->scb_array[inb(SCB_TAG + base)]); - if (aic7xxx_match_scb(scb, target, channel)) + switch (channel) { - /* - * We found an scb that needs to be aborted. - */ -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_device) aborting SCB %d, TCL=%d/%d/%d\n", - saved_queue[i], TCL_OF_SCB(scb)); -#endif - scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; - scb->cmd->result = (DID_RESET << 16); - outb(0, SCB_CONTROL + base); - i--; - found++; + case 'A': + min_target = 0; + max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15; + break; + case 'B': + min_target = 8; + max_target = 15; + break; + case ALL_CHANNELS: + default: + min_target = 0; + max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15; + break; } } - /* - * Now put the saved scbs back. - */ - for (queued = 0; queued < i; queued++) + else + { + min_target = target + channel == 'B' ? 8 : 0; + max_target = min_target; + } + + for (i = min_target; i <= max_target; i++) { - outb(saved_queue[queued], QINFIFO + base); + busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE); + if (busy_scbid < p->scb_data->numscbs) + { + struct aic7xxx_scb *busy_scb; + struct aic7xxx_scb *next_scb; + unsigned char next_scbid; + + busy_scb = p->scb_data->scb_array[busy_scbid]; + + next_scbid = busy_scb->hscb->data_count >> 24; + + if (next_scbid == SCB_LIST_NULL) + { + busy_scbid = aic7xxx_find_scb(p, busy_scb); + + if (busy_scbid != SCB_LIST_NULL) + { + outb(busy_scbid, p->base + SCBPTR); + next_scbid = inb(p->base + SCB_LINKED_NEXT); + } + } + + if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag)) + { + aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE); + } + + if (next_scbid != SCB_LIST_NULL) + { + next_scb = p->scb_data->scb_array[next_scbid]; + if (aic7xxx_match_scb(next_scb, target, channel, lun, tag)) + { + continue; + } + /* Requeue for later processing */ + scbq_insert_head(&p->waiting_scbs, next_scb); + next_scb->flags |= SCB_WAITINGQ; + } + } } } + found = aic7xxx_search_qinfifo(p, target, channel, lun, tag, + SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE); + /* * Search waiting for selection list. */ { - unsigned char next, prev; + unsigned char next, prev, scb_index; - next = inb(WAITING_SCBH + base); /* Start at head of list. */ + next = inb(p->base + WAITING_SCBH); /* Start at head of list. */ prev = SCB_LIST_NULL; while (next != SCB_LIST_NULL) { - outb(next, SCBPTR + base); - scb = (p->scb_array[inb(SCB_TAG + base)]); - /* - * Select the SCB. - */ - if (aic7xxx_match_scb(scb, target, channel)) + outb(next, p->base + SCBPTR); + scb_index = inb(p->base + SCB_TAG); + if (scb_index >= p->scb_data->numscbs) { - next = aic7xxx_abort_waiting_scb(p, scb, prev); + panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n", + scb_index, p->scb_data->numscbs); + } + scbp = p->scb_data->scb_array[scb_index]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + unsigned char linked_next; + + next = aic7xxx_abort_waiting_scb(p, scbp, next, prev); + linked_next = inb(p->base + SCB_LINKED_NEXT); + if (linked_next != SCB_LIST_NULL) + { + struct aic7xxx_scb *next_scb; + /* + * Requeue the waiting SCB via the waiting list. + */ + next_scb = p->scb_data->scb_array[linked_next]; + if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag)) + { + scbq_insert_head(&p->waiting_scbs, next_scb); + next_scb->flags |= SCB_WAITINGQ; + } + } found++; } else { prev = next; - next = inb(SCB_NEXT + base); + next = inb(p->base + SCB_NEXT); } } } /* - * Go through the entire SCB array now and look for commands for - * for this target that are active. These are other (most likely - * tagged) commands that were disconnected when the reset occurred. + * Go through disconnected list and remove any entries we have queued + * for completion, zeroing their control byte too. + */ + { + unsigned char next, prev, scb_index; + + next = inb(p->base + DISCONNECTED_SCBH); + prev = SCB_LIST_NULL; + + while (next != SCB_LIST_NULL) + { + outb(next, p->base + SCBPTR); + scb_index = inb(p->base + SCB_TAG); + if (scb_index > p->scb_data->numscbs) + { + panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, " + "num scbs = %d.\n", scb_index, p->scb_data->numscbs); + } + scbp = p->scb_data->scb_array[scb_index]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + next = aic7xxx_rem_scb_from_disc_list(p, next); + } + else + { + prev = next; + next = inb(p->base + SCB_NEXT); + } + } + } + + /* + * Go through the hardware SCB array looking for commands that + * were active but not on any list. */ - for (i = 0; i < p->scb_link->numscbs; i++) + for (i = 0; i < p->scb_data->maxhscbs; i++) { - scb = (p->scb_array[i]); - if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel)) + unsigned char scbid; + + outb(i, p->base + SCBPTR); + scbid = inb(p->base + SCB_TAG); + if (scbid < p->scb_data->numscbs) { - /* - * Ensure the target is "free" - */ - aic7xxx_unbusy_target(target, channel, base); - if (! (scb->state & SCB_PAGED_OUT)) + scbp = p->scb_data->scb_array[scbid]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) { - outb(scb->position, SCBPTR + base); - outb(0, SCB_CONTROL + base); + aic7xxx_add_curscb_to_free_list(p); } - scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; - scb->cmd->result = (DID_RESET << 16); + } + } + + /* + * Go through the entire SCB array now and look for commands for + * for this target that are stillactive. These are other (most likely + * tagged) commands that were disconnected when the reset occurred. + */ + for (i = 0; i < p->scb_data->numscbs; i++) + { + scbp = p->scb_data->scb_array[i]; + if (((scbp->flags & SCB_ACTIVE) != 0) && + aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; + scbp->flags &= ~SCB_ACTIVE; + aic7xxx_error(scbp->cmd) = DID_RESET; + found++; + + if ((scbp->flags & SCB_WAITINGQ) != 0) + { + scbq_remove(&p->waiting_scbs, scbp); + scbp->flags &= ~SCB_WAITINGQ; + } } } - outb(active_scb, SCBPTR + base); + outb(active_scb, p->base + SCBPTR); return (found); } /*+F************************************************************************* * Function: + * aic7xxx_clear_intstat + * + * Description: + * Clears the interrupt status. + *-F*************************************************************************/ +static void +aic7xxx_clear_intstat(struct aic7xxx_host *p) +{ + /* Clear any interrupt conditions this may have caused. */ + outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0); + outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR | + CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); +} + +/*+F************************************************************************* + * Function: * aic7xxx_reset_current_bus * * Description: * Reset the current SCSI bus. *-F*************************************************************************/ static void -aic7xxx_reset_current_bus(int base) +aic7xxx_reset_current_bus(struct aic7xxx_host *p) { - outb(SCSIRSTO, SCSISEQ + base); + unsigned char scsiseq; + + /* Disable reset interrupts. */ + outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1); + + /* Turn on the bus reset. */ + scsiseq = inb(p->base + SCSISEQ); + outb(scsiseq | SCSIRSTO, p->base + SCSISEQ); + + udelay(1000); + + /* Turn off the bus reset. */ + outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ); + + aic7xxx_clear_intstat(p); + + /* Re-enable reset interrupts. */ + outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1); + udelay(1000); - outb(0, SCSISEQ + base); } /*+F************************************************************************* @@ -1922,25 +2486,24 @@ static int aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset) { - int base = p->base; - unsigned char sblkctl; - char cur_channel; unsigned long offset, offset_max; int found; + unsigned char sblkctl; + char cur_channel; + pause_sequencer(p); /* - * Clean up all the state information for the - * pending transactions on this bus. + * Clean up all the state information for the pending transactions + * on this bus. */ - found = aic7xxx_reset_device(p, ALL_TARGETS, channel); + found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL); if (channel == 'B') { p->needsdtr |= (p->needsdtr_copy & 0xFF00); p->sdtr_pending &= 0x00FF; - outb(0, ACTIVE_B + base); - offset = TARG_SCRATCH + base + 8; - offset_max = TARG_SCRATCH + base + 16; + offset = TARG_SCRATCH + 8; + offset_max = TARG_SCRATCH + 16; } else { @@ -1950,132 +2513,100 @@ p->needwdtr = p->needwdtr_copy; p->sdtr_pending = 0x0; p->wdtr_pending = 0x0; - outb(0, ACTIVE_A + base); - outb(0, ACTIVE_B + base); - offset = TARG_SCRATCH + base; - offset_max = TARG_SCRATCH + base + 16; + offset = TARG_SCRATCH; + offset_max = TARG_SCRATCH + 16; } else { + /* Channel A */ p->needsdtr |= (p->needsdtr_copy & 0x00FF); p->sdtr_pending &= 0xFF00; - outb(0, ACTIVE_A + base); - offset = TARG_SCRATCH + base; - offset_max = TARG_SCRATCH + base + 8; + offset = TARG_SCRATCH; + offset_max = TARG_SCRATCH + 8; } } + while (offset < offset_max) { /* - * Revert to async/narrow transfers - * until we renegotiate. + * Revert to async/narrow transfers until we renegotiate. */ u_char targ_scratch; - targ_scratch = inb(offset); + + targ_scratch = inb(p->base + offset); targ_scratch &= SXFR; - outb(targ_scratch, offset); + outb(targ_scratch, p->base + offset); offset++; } /* * Reset the bus and unpause/restart the controller */ - - /* - * Case 1: Command for another bus is active - */ - sblkctl = inb(SBLKCTL + base); + sblkctl = inb(p->base + SBLKCTL); cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A'; if (cur_channel != channel) { + /* + * Case 1: Command for another bus is active + */ #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n", - channel); + printk("scsi%d: Stealthily resetting channel %c\n", + p->host_no, channel); #endif /* - * Stealthily reset the other bus without upsetting the current bus + * Stealthily reset the other bus without upsetting the current bus. */ - outb(sblkctl ^ SELBUSB, SBLKCTL + base); + outb(sblkctl ^ SELBUSB, p->base + SBLKCTL); + outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1); if (initiate_reset) { - aic7xxx_reset_current_bus(base); + aic7xxx_reset_current_bus(p); + /* + * Cause the mid-level SCSI code to delay any further + * queueing by the bus settle time for us. + */ + p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); } - outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); - outb(sblkctl, SBLKCTL + base); - - UNPAUSE_SEQUENCER(p); + outb(0, p->base + SCSISEQ); + aic7xxx_clear_intstat(p); + outb(sblkctl, p->base + SBLKCTL); + unpause_sequencer(p, /* unpause_always */ FALSE); } - /* - * Case 2: A command from this bus is active or we're idle - */ else { + /* + * Case 2: A command from this bus is active or we're idle. + */ #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_channel) Resetting current channel %c\n", - channel); + printk("scsi%d: Resetting current channel %c\n", + p->host_no, channel); #endif + outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1); if (initiate_reset) { - aic7xxx_reset_current_bus(base); + aic7xxx_reset_current_bus(p); + /* + * Cause the mid-level SCSI code to delay any further + * queueing by the bus settle time for us. + */ +#if 0 + p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); +#endif } - outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); - RESTART_SEQUENCER(p); + outb(0, p->base + SCSISEQ); + aic7xxx_clear_intstat(p); + restart_sequencer(p); #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n"); + printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no); #endif } /* - * Cause the mid-level SCSI code to delay any further - * queueing by the bus settle time for us. - */ - p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); - - /* * Now loop through all the SCBs that have been marked for abortion, * and call the scsi_done routines. */ - aic7xxx_done_aborted_scbs(p); - return found; -} - -/*+F************************************************************************* - * Function: - * aic7xxx_page_scb - * - * Description: - * Swap in_scbp for out_scbp down in the cards SCB array. - * We assume that the SCB for out_scbp is already selected in SCBPTR. - * - *-F*************************************************************************/ -static inline void -aic7xxx_page_scb(struct aic7xxx_host *p, struct aic7xxx_scb *out_scbp, - struct aic7xxx_scb *in_scbp) -{ - int index; - - /* Page-out */ -#if 0 -printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n", - out_scbp->cmd->target, in_scbp->cmd->target); -#endif - aic7xxx_getscb(p, out_scbp); - out_scbp->state |= SCB_PAGED_OUT; - if (!(out_scbp->control & TAG_ENB)) - { - /* Stick in non-tagged array */ - index = (out_scbp->target_channel_lun >> 4) | - (out_scbp->target_channel_lun & SELBUSB); - p->pagedout_ntscbs[index] = out_scbp; - } - - /* Page-in */ - in_scbp->position = out_scbp->position; - out_scbp->position = SCB_LIST_NULL; - aic7xxx_putscb(p, in_scbp); - in_scbp->state &= ~SCB_PAGED_OUT; + aic7xxx_run_done_queue(p, /*complete*/ TRUE); + return (found); } /*+F************************************************************************* @@ -2083,1159 +2614,1326 @@ * aic7xxx_run_waiting_queues * * Description: - * Scan the assigned_scbs and waiting_scbs queues. For scbs in the - * assigned_scbs queue, we download and start them. For scbs in the - * waiting_scbs queue, we page in as many as we can being careful - * not to cause a deadlock for a reconnecting target. - * + * Scan the awaiting_scbs queue downloading and starting as many + * scbs as we can. *-F*************************************************************************/ static inline void aic7xxx_run_waiting_queues(struct aic7xxx_host *p) { struct aic7xxx_scb *scb; - u_char cur_scb, intstat; - u_long base = p->base; - long flags; - if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL)) + if (p->waiting_scbs.head == NULL) return; - save_flags(flags); - cli(); - - PAUSE_SEQUENCER(p); - cur_scb = inb(SCBPTR + base); - intstat = inb(INTSTAT + base); - + pause_sequencer(p); /* * First handle SCBs that are waiting but have been assigned a slot. */ - scb = p->assigned_scbs.head; - while (scb != NULL) - { - scbq_remove_head(&(p->assigned_scbs)); - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - /* Mark this as an active command. */ - scb->state = (scb->state & ~SCB_ASSIGNEDQ) | SCB_ACTIVE; - outb(scb->position, QINFIFO + base); - scb = p->assigned_scbs.head; - } - - /* Now deal with SCBs that require paging. */ scb = p->waiting_scbs.head; - if (scb != NULL) + while (scb != NULL) { - u_char disc_scb = inb(DISCONNECTED_SCBH + base); - u_char active = inb(FLAGS + base) & (SELECTED | IDENTIFY_SEEN); - int count = 0; - u_char next_scb; - - while (scb != NULL) + if (p->curqincnt >= p->qfullcount) { - /* Attempt to page this SCB in */ - if (disc_scb == SCB_LIST_NULL) - break; - - /* - * Advance disc_scb to the next one in the list. - */ - outb(disc_scb, SCBPTR + base); - next_scb = inb(SCB_NEXT + base); - - /* - * We have to be careful about when we allow an SCB to be paged out. - * There must always be at least one slot availible for a reconnecting - * target in case it references an SCB that has been paged out. Our - * heuristic is that either the disconnected list has at least two - * entries in it or there is one entry and the sequencer is activily - * working on an SCB which implies that it will either complete or - * disconnect before another reconnection can occur. - */ - if ((next_scb != SCB_LIST_NULL) || active) + p->curqincnt = inb(p->base + QINCNT) & p->qcntmask; + if (p->curqincnt >= p->qfullcount) { - u_char out_scbi; - struct aic7xxx_scb *out_scbp; - - scbq_remove_head(&(p->waiting_scbs)); - - /* - * Find the in-core SCB for the one we're paging out. - */ - out_scbi = inb(SCB_TAG + base); - out_scbp = (p->scb_array[out_scbi]); - - /* Do the page out and mark the paged in SCB as active. */ - aic7xxx_page_scb(p, out_scbp, scb); - - /* Mark this as an active command. */ - scb->state = (scb->state & ~SCB_WAITINGQ) | SCB_ACTIVE; - - /* Queue the command */ - outb(scb->position, QINFIFO + base); - count++; - - /* Advance to the next disconnected SCB */ - disc_scb = next_scb; - scb = p->waiting_scbs.head; + break; } - else - scb = NULL; } - if (count) + /* + * We have some space. + */ + scbq_remove_head(&(p->waiting_scbs)); + scb->flags &= ~SCB_WAITINGQ; + + outb(scb->hscb->tag, p->base + QINFIFO); + + if ((p->flags & PAGE_ENABLED) != 0) { - /* - * Update the head of the disconnected list. + /* + * We only care about this statistic when paging + * since it's impossible to overflow the qinfifo + * in the non-paging case. */ - outb(disc_scb, DISCONNECTED_SCBH + base); - if (disc_scb != SCB_LIST_NULL) - { - outb(disc_scb, SCBPTR + base); - outb(SCB_LIST_NULL, SCB_PREV + base); - } + p->curqincnt++; } + scb = p->waiting_scbs.head; } - /* Restore old position */ - outb(cur_scb, SCBPTR + base); - /* - * Guard against unpausing the sequencer if there is an interrupt - * waiting to happen. - */ - if (!(intstat & (BRKADRINT | SEQINT | SCSIINT))) - { - UNPAUSE_SEQUENCER(p); - } + unpause_sequencer(p, FALSE); +} - restore_flags(flags); +/*+F************************************************************************* + * Function: + * aic7xxx_construct_sdtr + * + * Description: + * Constucts a synchronous data transfer message in the message + * buffer on the sequencer. + *-F*************************************************************************/ +static void +aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte, + unsigned char period, unsigned char offset) +{ + outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte); + outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte); + outb(MSG_EXT_SDTR, p->base + MSG_OUT + 2 + start_byte); + outb(period, p->base + MSG_OUT + 3 + start_byte); + outb(offset, p->base + MSG_OUT + 4 + start_byte); + outb(start_byte + 5, p->base + MSG_LEN); } /*+F************************************************************************* * Function: - * aic7xxx_isr + * aic7xxx_construct_wdtr * * Description: - * SCSI controller interrupt handler. + * Constucts a wide data transfer message in the message buffer + * on the sequencer. + *-F*************************************************************************/ +static void +aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte, + unsigned char bus_width) +{ + outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte); + outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte); + outb(MSG_EXT_WDTR, p->base + MSG_OUT + 2 + start_byte); + outb(bus_width, p->base + MSG_OUT + 3 + start_byte); + outb(start_byte + 4, p->base + MSG_LEN); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_calc_residual * - * NOTE: Since we declared this using SA_INTERRUPT, interrupts should - * be disabled all through this function unless we say otherwise. + * Description: + * Calculate the residual data not yet transferred. *-F*************************************************************************/ static void -aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) +aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - int base, intstat, actual, scb_index, run_aborted_queue = FALSE; - struct aic7xxx_host *p; - struct aic7xxx_scb *scb = NULL; - short transfer; - unsigned char ha_flags, scsi_id, bus_width; - unsigned char offset, rate, scratch, scratch_offset; - unsigned char max_offset, rej_byte; - unsigned short target_mask; - char channel; - unsigned int addr; /* must be 32 bits */ + struct aic7xxx_hwscb *hscb; Scsi_Cmnd *cmd; + int actual; - p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata; + cmd = scb->cmd; + hscb = scb->hscb; /* - * Search for the host with a pending interrupt. If we can't find - * one, then we've encountered a spurious interrupt. + * Don't destroy valid residual information with + * residual coming from a check sense operation. */ - while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND)) + if (((scb->hscb->control & DISCONNECTED) == 0) && + (scb->flags & SCB_SENSE) == 0) { - if (p->next == NULL) - { - p = NULL; - } - else + /* + * We had an underflow. At this time, there's only + * one other driver that bothers to check for this, + * and cmd->underflow seems to be set rather half- + * heartedly in the higher-level SCSI code. + */ + actual = aic7xxx_length(cmd, hscb->residual_SG_segment_count); + + actual -= (hscb->residual_data_count[2] << 16) | + (hscb->residual_data_count[1] << 8) | + hscb->residual_data_count[0]; + + if (actual < cmd->underflow) { - p = (struct aic7xxx_host *) p->next->hostdata; + printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - " + "Wanted at least %u, got %u, residual SG count %d.\n", + p->host_no, TC_OF_SCB(scb), cmd->underflow, actual, + hscb->residual_SG_segment_count); + aic7xxx_error(cmd) = DID_RETRY_COMMAND; + aic7xxx_status(cmd) = hscb->target_status; } } - if (p == NULL) - return; - /* - * Keep track of interrupts for /proc/scsi + * Clean out the residual information in the SCB for the + * next consumer. */ - p->isr_count++; + hscb->residual_data_count[2] = 0; + hscb->residual_data_count[1] = 0; + hscb->residual_data_count[0] = 0; + hscb->residual_SG_segment_count = 0; +} - if (!(p->flags & A_SCANNED) && (p->isr_count == 1)) +/*+F************************************************************************* + * Function: + * aic7xxx_handle_device_reset + * + * Description: + * Interrupt handler for sequencer interrupts (SEQINT). + *-F*************************************************************************/ +static void +aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel) +{ + unsigned short targ_mask; + unsigned char targ_scratch; + int scratch_offset = target; + int found; + + if (channel == 'B') { - /* - * We must only have one card at this IRQ and it must have been - * added to the board data before the spurious interrupt occurred. - * It is sufficient that we check isr_count and not the spurious - * interrupt count. - */ - printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n"); - return; + scratch_offset += 8; } - - base = p->base; + targ_mask = (0x01 << scratch_offset); /* - * Handle all the interrupt sources - especially for SCSI - * interrupts, we won't get a second chance at them. + * Go back to async/narrow transfers and renegotiate. */ - intstat = inb(INTSTAT + base); + p->needsdtr |= p->needsdtr_copy & targ_mask; + p->needwdtr |= p->needwdtr_copy & targ_mask; + p->sdtr_pending &= ~targ_mask; + p->wdtr_pending &= ~targ_mask; + targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + targ_scratch &= SXFR; + outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset); + found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL); + printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, " + "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found); + aic7xxx_run_done_queue(p, /*complete*/ TRUE); +} - /* - * Indicate that we're in the interrupt handler. - */ - p->flags |= IN_ISR; +/*+F************************************************************************* + * Function: + * aic7xxx_handle_seqint + * + * Description: + * Interrupt handler for sequencer interrupts (SEQINT). + *-F*************************************************************************/ +static void +aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) +{ + struct aic7xxx_scb *scb; + unsigned short target_mask; + unsigned char target, scratch_offset; + char channel; - if (intstat & BRKADRINT) + if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0) { - int i; - unsigned char errno = inb(ERROR + base); - - printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno); - for (i = 0; i < NUMBER(hard_error); i++) - { - if (errno & hard_error[i].errno) - { - printk(KERN_ERR " %s\n", hard_error[i].errmesg); - } - } - panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no, - inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base)); + target = (inb(p->base + SELID) >> 4) & 0x0F; } - - if (intstat & SEQINT) + else { - /* - * Although the sequencer is paused immediately on - * a SEQINT, an interrupt for a SCSIINT condition will - * unpaused the sequencer before this point. - */ - PAUSE_SEQUENCER(p); + target = (inb(p->base + SCSIID) >> 4) & 0x0F; + } + scratch_offset = target; + channel = 'A'; + if (inb(p->base + SBLKCTL) & SELBUSB) + { + channel = 'B'; + scratch_offset += 8; + } + target_mask = (0x01 << scratch_offset); - scsi_id = (inb(SCSIID + base) >> 4) & 0x0F; - scratch_offset = scsi_id; - channel = 'A'; - if (inb(SBLKCTL + base) & SELBUSB) - { - channel = 'B'; - scratch_offset += 8; - } - target_mask = (0x01 << scratch_offset); + switch (intstat & SEQINT_MASK) + { + case NO_MATCH: + { + /* + * This could be for a normal abort request. Figure out + * which SCB we were trying to find and only give an error + * if we didn't ask for this to happen. + */ + unsigned char scb_index; + unsigned char busy_scbid; + unsigned char arg1; + + busy_scbid = aic7xxx_index_busy_target(p, target, channel, + /*unbusy*/ FALSE); + arg1 = inb(p->base + ARG_1); - switch (intstat & SEQINT_MASK) - { - case NO_MATCH: - if (p->flags & PAGE_ENABLED) + if (arg1 == SCB_LIST_NULL) { - /* SCB Page-in request */ - struct aic7xxx_scb *outscb; - u_char arg_1 = inb(ARG_1 + base); - int use_disconnected = FALSE; - - /* - * The sequencer expects this value upon return. Assume - * we will find the paged out SCB and set the value now. - * If we don't, and one of the methods used to acquire an - * SCB calls aic7xxx_done(), we will end up in our queue - * routine and unpause the sequencer without giving it the - * correct return value, which causes a hang. - */ - outb(SCB_PAGEDIN, RETURN_1 + base); - if (arg_1 == SCB_LIST_NULL) - { - /* Non-tagged command */ - int index = scsi_id; - if (channel == 'B') - { - index |= SELBUSB; - } - scb = p->pagedout_ntscbs[index]; - } - else - scb = (p->scb_array[arg_1]); + /* untagged request */ + scb_index = busy_scbid; + } + else + { + scb_index = arg1; + } - if (!(scb->state & SCB_PAGED_OUT)) + if (scb_index < p->scb_data->numscbs) + { + scb = p->scb_data->scb_array[scb_index]; + if (scb->hscb->control & ABORT_SCB) { - printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting " - "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - aic7xxx_unbusy_target(scsi_id, channel, base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(0, RETURN_1 + base); + /* + * We expected this. Let the busfree handler take care + * of this when we the abort is finially sent. Set + * IDENTIFY_SEEN so that the busfree handler knows that + * there is an SCB to cleanup. + */ + outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS); + printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n", + p->host_no, TC_OF_SCB(scb)); break; } + } + printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting " + "target - Issuing BUS DEVICE RESET.\n", + p->host_no, target, CHAN_TO_INT(channel)); + + printk(KERN_WARNING " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n", + inb(p->base + SAVED_TCL), arg1, + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + aic7xxx_handle_device_reset(p, target, channel); + } + break; - /* - * Now to pick the SCB to page out. Either take a free SCB, an - * assigned SCB, an SCB that just completed, or the first one - * on the disconnected SCB list. - */ - if (p->scb_link->free_scbs.head != NULL) - { - outscb = p->scb_link->free_scbs.head; - scbq_remove_head(&p->scb_link->free_scbs); - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - scbq_insert_head(&p->page_scbs, outscb); - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - } - else if (p->assigned_scbs.head != NULL) - { - outscb = p->assigned_scbs.head; - scbq_remove_head(&p->assigned_scbs); - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - scbq_insert_head(&p->waiting_scbs, outscb); - outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ; - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - } - else if (intstat & CMDCMPLT) + case NO_MATCH_BUSY: + { + /* + * XXX - Leave this as a panic for the time being since it + * indicates a bug in the timeout code for this to happen. + */ + unsigned char scb_index; + + scb_index = inb(p->base + CUR_SCBID); + scb = p->scb_data->scb_array[scb_index]; + + panic("scsi%d: Target %d, channel %c, Target busy link failure, " + "but busy SCB exists!\n", + p->host_no, target, channel); + } + break; + + case SEND_REJECT: + { + unsigned char rej_byte; + + rej_byte = inb(p->base + REJBYTE); + printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) " + "received from target, SEQ_FLAGS=0x%x\n", + p->host_no, target, CHAN_TO_INT(channel), rej_byte, + inb(p->base + SEQ_FLAGS)); + } + break; + + case NO_IDENT: + { + /* + * The reconnecting target either did not send an identify + * message, or did, but we didn't find and SCB to match and + * before it could respond to our ATN/abort, it hit a dataphase. + * The only safe thing to do is to blow it away with a bus + * reset. + */ + int found; + + printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY " + "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n", + p->host_no, target, CHAN_TO_INT(channel), + inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL)); + + found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE); + + printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; " + "%d SCBs aborted\n", p->host_no, channel, found); + } + break; + + case BAD_PHASE: + if (inb(p->base + LASTPHASE) == P_BUSFREE) + { + printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n", + p->host_no, CHAN_TO_INT(channel), target); + restart_sequencer(p); + } + else + { + printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting " + "to continue\n", p->host_no, CHAN_TO_INT(channel), target); + } + break; + + case EXTENDED_MSG: + { + unsigned char message_length; + unsigned char message_code; + unsigned char scb_index; + + message_length = inb(p->base + MSGIN_EXT_LEN); + message_code = inb(p->base + MSGIN_EXT_OPCODE); + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; + + switch (message_code) + { + case MSG_EXT_SDTR: { - int scb_index; + unsigned char period; + unsigned char offset; + unsigned char saved_offset; + unsigned char targ_scratch; + unsigned char max_offset; + unsigned char rate; - outb(CLRCMDINT, CLRINT + base); - scb_index = inb(QOUTFIFO + base); - if (!(inb(QOUTCNT + base) & p->qcntmask)) - { - intstat &= ~CMDCMPLT; - } - outscb = (p->scb_array[scb_index]); - if (!(outscb->state & SCB_ACTIVE)) + if (message_length != MSG_EXT_SDTR_LEN) { - printk(KERN_WARNING "scsi%d: No command for completed SCB %d " - "during NO_MATCH interrupt\n", scb_index, p->host_no); - use_disconnected = TRUE; + outb(SEND_REJ, p->base + RETURN_1); + break; } + + period = inb(p->base + MSGIN_EXT_BYTES); + saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1); + targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + + if (targ_scratch & WIDEXFER) + max_offset = MAX_OFFSET_16BIT; else + max_offset = MAX_OFFSET_8BIT; + offset = MIN(saved_offset, max_offset); + + aic7xxx_scsirate(p, &rate, &period, &offset, target, channel); + + /* + * Preserve the WideXfer flag. + */ + targ_scratch = rate | (targ_scratch & WIDEXFER); + + /* + * Update both the target scratch area and current SCSIRATE. + */ + outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset); + outb(targ_scratch, p->base + SCSIRATE); + + /* + * See if we initiated Sync Negotiation and didn't have + * have to fall down to async transfers. + */ + if ((scb->flags & SCB_MSGOUT_SDTR) != 0) { - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16); - if ((outscb->cmd->flags & WAS_SENSE) && - !(outscb->cmd->flags & ASKED_FOR_SENSE)) + /* We started it. */ + if (saved_offset == offset) { - /* - * Got sense information. - */ - outscb->cmd->flags &= ASKED_FOR_SENSE; + /* + * Don't send an SDTR back to the target. + */ + outb(0, p->base + RETURN_1); + } + else + { + /* We went too low - force async. */ + outb(SEND_REJ, p->base + RETURN_1); } - p->device_status[TARGET_INDEX(outscb->cmd)].flags - |= DEVICE_SUCCESS; - aic7xxx_done(p, outscb); } + else + { + /* + * Send our own SDTR in reply. + * + * We want to see this message as we don't expect a target + * to send us a SDTR request first. + */ + printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no); + aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset); + outb(SEND_MSG, p->base + RETURN_1); + } + /* + * Clear the flags. + */ + p->needsdtr &= ~target_mask; + break; } - else - { - use_disconnected = TRUE; - } - if (use_disconnected) + + case MSG_EXT_WDTR: { - u_char tag; - u_char next; - u_char disc_scb = inb(DISCONNECTED_SCBH + base); - if (disc_scb != SCB_LIST_NULL) + unsigned char scratch, bus_width; + + if (message_length != MSG_EXT_WDTR_LEN) { - outb(disc_scb, SCBPTR + base); - tag = inb(SCB_TAG + base); - outscb = (p->scb_array[tag]); - next = inb(SCB_NEXT + base); - if (next != SCB_LIST_NULL) - { - outb(next, SCBPTR + base); - outb(SCB_LIST_NULL, SCB_PREV + base); - outb(disc_scb, SCBPTR + base); - } - outb(next, DISCONNECTED_SCBH + base); - aic7xxx_page_scb(p, outscb, scb); - } - else if (inb(QINCNT + base) & p->qcntmask) + outb(SEND_REJ, p->base + RETURN_1); + break; + } + + bus_width = inb(p->base + MSGIN_EXT_BYTES); + scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + + if ((scb->flags & SCB_MSGOUT_WDTR) != 0) { - /* Pull one of our queued commands as a last resort. */ - disc_scb = inb(QINFIFO + base); - outb(disc_scb, SCBPTR + base); - tag = inb(SCB_TAG + base); - outscb = (p->scb_array[tag]); - if ((outscb->control & 0x23) != TAG_ENB) + /* + * Don't send an WDTR back to the target, since we asked first. + */ + outb(0, p->base + RETURN_1); + switch (bus_width) { - /* - * This is not a simple tagged command so its position - * in the queue matters. Take the command at the end of - * the queue instead. - */ - int i; - int saved_queue[AIC7XXX_MAXSCB]; - int queued = inb(QINCNT + base) & p->qcntmask; - - /* Count the command we removed already */ - saved_queue[0] = disc_scb; - queued++; - - /* Empty the input queue. */ - for (i = 1; i < queued; i++) - { - saved_queue[i] = inb(QINFIFO + base); - } - - /* Put everyone back but the last entry. */ - queued--; - for (i = 0; i < queued; i++) - { - outb(saved_queue[i], QINFIFO + base); - } - - outb(saved_queue[queued], SCBPTR + base); - tag = inb(SCB_TAG + base); - outscb = (p->scb_array[tag]); + case BUS_8_BIT: + scratch &= 0x7F; + break; + + case BUS_16_BIT: + if (aic7xxx_verbose) + { + printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 " + "bit transfers.\n", p->host_no, target, channel); + } + scratch |= WIDEXFER; + break; + + case BUS_32_BIT: + outb(SEND_REJ, p->base + RETURN_1); + /* No verbose here! We want to see this condition. */ + printk(KERN_WARNING "scsi%d: Target %d, channel %c, " + "requesting 32 bit transfers, rejecting...\n", + p->host_no, target, channel); + break; + + default: + break; } - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - scbq_insert_head(&p->waiting_scbs, outscb); - outscb->state |= SCB_WAITINGQ; - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; } else { - printk(KERN_WARNING "scsi%d: Page-in request with no candidates " - "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - aic7xxx_unbusy_target(scsi_id, channel, base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(0, RETURN_1 + base); + /* + * Send our own WDTR in reply. + */ + switch (bus_width) + { + case BUS_8_BIT: + scratch &= 0x7F; + break; + + case BUS_32_BIT: + case BUS_16_BIT: + if (p->bus_type == AIC_WIDE) + { + printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 " + "bit transfers.\n", p->host_no, target, channel); + bus_width = BUS_16_BIT; + scratch |= WIDEXFER; + } + else + { + bus_width = BUS_8_BIT; + scratch &= 0x7F; /* XXX - FreeBSD doesn't do this. */ + } + break; + + default: + break; + } + aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width); + outb(SEND_MSG, p->base + RETURN_1); } - } - } - else - { - printk(KERN_WARNING "scsi%d: No active SCB for reconnecting " - "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - aic7xxx_unbusy_target(scsi_id, channel, base); - outb(0, SCB_CONTROL + base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(0, RETURN_1 + base); - } - break; - - case BAD_PHASE: - panic("scsi%d: Unknown scsi bus phase.\n", p->host_no); - break; - - case SEND_REJECT: - rej_byte = inb(REJBYTE + base); - if ((rej_byte & 0xF0) == 0x20) - { - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - printk(KERN_WARNING "scsi%d: Tagged message received without identify." - "Disabling tagged commands for target %d channel %c.\n", - p->host_no, scsi_id, channel); - scb->cmd->device->tagged_supported = 0; - scb->cmd->device->tagged_queue = 0; - } - else - { - printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received " - "from target %d channel %c.\n", - p->host_no, rej_byte, scsi_id, channel); - } - break; - - case NO_IDENT: - panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY " - "message. SAVED_TCL 0x%x.\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - break; + p->needwdtr &= ~target_mask; + outb(scratch, p->base + TARG_SCRATCH + scratch_offset); + outb(scratch, p->base + SCSIRATE); + break; + } /* case MSG_EXT_WDTR */ - case SDTR_MSG: - /* - * Help the sequencer to translate the negotiated - * transfer rate. Transfer is 1/4 the period - * in ns as is returned by the sync negotiation - * message. So, we must multiply by four. - */ - transfer = (inb(ARG_1 + base) << 2); - offset = inb(ACCUM + base); - scratch = inb(TARG_SCRATCH + base + scratch_offset); - /* - * The maximum offset for a wide device is 0x08; for a - * 8-bit bus device the maximum offset is 0x0F. - */ - if (scratch & WIDEXFER) - { - max_offset = 0x08; - } - else - { - max_offset = 0x0F; - } - aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset), - scsi_id, channel); - /* - * Preserve the wide transfer flag. - */ - scratch = rate | (scratch & WIDEXFER); - outb(scratch, TARG_SCRATCH + base + scratch_offset); - outb(scratch, SCSIRATE + base); - if ((scratch & 0x0F) == 0) - { - /* - * One of two things happened. Either the device requested - * asynchronous data transfers, or it requested a synchronous - * data transfer rate that was so low that asynchronous - * transfers are faster (not to mention the controller won't - * support them). In both cases the synchronous data transfer - * rate and the offset are set to 0 indicating asynchronous - * transfers. - * - * If the device requested an asynchronous transfer, then - * accept the request. If the device is being forced to - * asynchronous data transfers and this is the first time - * we've seen the request, accept the request. If we've - * already seen the request, then attempt to force - * asynchronous data transfers by rejecting the message. - */ - if ((offset == 0) || (p->sdtr_pending & target_mask)) - { + default: /* - * Device requested asynchronous transfers or we're - * forcing asynchronous transfers for the first time. + * Unknown extended message - reject it. */ - outb(0, RETURN_1 + base); - } - else - { - /* - * The first time in forcing asynchronous transfers - * failed, so we try sending a reject message. - */ - outb(SEND_REJ, RETURN_1 + base); - } - } - else - { - /* - * See if we initiated Sync Negotiation - */ - if (p->sdtr_pending & target_mask) - { - /* - * Don't send an SDTR back to the target. - */ - outb(0, RETURN_1 + base); - } - else - { - /* - * Send our own SDTR in reply. - */ - printk("aic7xxx: Sending SDTR!!\n"); - outb(SEND_SDTR, RETURN_1 + base); - } - } - /* - * Clear the flags. - */ - p->needsdtr &= ~target_mask; - p->sdtr_pending &= ~target_mask; - break; - - case WDTR_MSG: - { - bus_width = inb(ARG_1 + base); - printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c " - "needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr); - scratch = inb(TARG_SCRATCH + base + scratch_offset); - - if (p->wdtr_pending & target_mask) - { - /* - * Don't send an WDTR back to the target, since we asked first. - */ - outb(0, RETURN_1 + base); - switch (bus_width) - { - case BUS_8_BIT: - scratch &= 0x7F; - break; - - case BUS_16_BIT: - printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit " - "transfers.\n", p->host_no, scsi_id, channel); - scratch |= 0x80; - break; - - case BUS_32_BIT: - outb(SEND_REJ, RETURN_1 + base); - printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit " - "transfers, rejecting...\n", p->host_no, scsi_id, channel); - break; - } - } - else - { - /* - * Send our own WDTR in reply. - */ - printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no); - switch (bus_width) - { - case BUS_8_BIT: - scratch &= 0x7F; - break; - - case BUS_32_BIT: - /* - * Negotiate 16 bits. - */ - bus_width = BUS_16_BIT; - /* Yes, we mean to fall thru here. */ - - case BUS_16_BIT: - printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit " - "transfers.\n", p->host_no, scsi_id, channel); - scratch |= 0x80; - break; - } - outb(bus_width | SEND_WDTR, RETURN_1 + base); - } - p->needwdtr &= ~target_mask; - p->wdtr_pending &= ~target_mask; - outb(scratch, TARG_SCRATCH + base + scratch_offset); - outb(scratch, SCSIRATE + base); - break; - } + outb(SEND_REJ, p->base + RETURN_1); + break; + } /* switch (message_code) */ + } /* case EXTENDED_MSG */ + break; - case REJECT_MSG: + case REJECT_MSG: { /* - * What we care about here is if we had an - * outstanding SDTR or WDTR message for this - * target. If we did, this is a signal that - * the target is refusing negotiation. + * What we care about here is if we had an outstanding SDTR + * or WDTR message for this target. If we did, this is a + * signal that the target is refusing negotiation. */ + unsigned char targ_scratch; + unsigned char scb_index; - scratch = inb(TARG_SCRATCH + base + scratch_offset); + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; + targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); - if (p->wdtr_pending & target_mask) + if ((scb->flags & SCB_MSGOUT_WDTR) != 0) { - /* - * note 8bit xfers and clear flag - */ - scratch &= 0x7F; - p->needwdtr &= ~target_mask; - p->wdtr_pending &= ~target_mask; - printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE " - "negotiation; using 8 bit transfers.\n", - p->host_no, scsi_id, channel); + /* + * note 8bit xfers and clear flag + */ + targ_scratch &= 0x7F; + p->needwdtr &= ~target_mask; + printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE " + "negotiation; using 8 bit transfers.\n", + p->host_no, target, channel); } else { - if (p->sdtr_pending & target_mask) - { - /* - * note asynch xfers and clear flag - */ - scratch &= 0xF0; - p->needsdtr &= ~target_mask; - p->sdtr_pending &= ~target_mask; - printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing " - "synchronous negotiation; using asynchronous transfers.\n", - p->host_no, scsi_id, channel); - } - /* - * Otherwise, we ignore it. - */ + if ((scb->flags & SCB_MSGOUT_SDTR) != 0) + { + /* + * note asynch xfers and clear flag + */ + targ_scratch &= 0xF0; + p->needsdtr &= ~target_mask; + printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing " + "synchronous negotiation; using asynchronous transfers.\n", + p->host_no, target, channel); + } + /* + * Otherwise, we ignore it. + */ } - outb(scratch, TARG_SCRATCH + base + scratch_offset); - outb(scratch, SCSIRATE + base); - break; + outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset); + outb(targ_scratch, p->base + SCSIRATE); } + break; - case BAD_STATUS: - /* The sequencer will notify us when a command has an error that - * would be of interest to the kernel. This allows us to leave - * the sequencerrunning in the common case of command completes - * without error. - */ + case BAD_STATUS: + { + unsigned char scb_index; + struct aic7xxx_hwscb *hscb; + Scsi_Cmnd *cmd; + + /* The sequencer will notify us when a command has an error that + * would be of interest to the kernel. This allows us to leave + * the sequencer running in the common case of command completes + * without error. The sequencer will have DMA'd the SCB back + * up to us, so we can reference the drivers SCB array. + */ + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; + hscb = scb->hscb; - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - outb(0, RETURN_1 + base); /* CHECK_CONDITION may change this */ - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) + /* + * Set the default return value to 0 indicating not to send + * sense. The sense code will change this if needed and this + * reduces code duplication. + */ + outb(0, p->base + RETURN_1); + if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); + printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " + "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no, + intstat, scb_index, scb->flags, (unsigned int) scb->cmd); } else { - cmd = scb->cmd; - scb->target_status = inb(SCB_TARGET_STATUS + base); - aic7xxx_status(cmd) = scb->target_status; + cmd = scb->cmd; + hscb->target_status = inb(p->base + SCB_TARGET_STATUS); + aic7xxx_status(cmd) = hscb->target_status; - cmd->result |= scb->target_status; + cmd->result |= hscb->target_status; - switch (status_byte(scb->target_status)) - { - case GOOD: - printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n"); - break; - - case CHECK_CONDITION: - if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE)) - { - unsigned char tcl; - unsigned int req_buf; /* must be 32 bits */ + switch (status_byte(hscb->target_status)) + { + case GOOD: + printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of " + "GOOD???\n", p->host_no, TC_OF_SCB(scb)); + break; - tcl = scb->target_channel_lun; + case CHECK_CONDITION: + if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE)) + { + unsigned int addr; /* must be 32 bits */ + /* + * XXX - How do we save the residual (if there is one). + */ + aic7xxx_calculate_residual(p, scb); + + /* + * Send a sense command to the requesting target. + * XXX - revisit this and get rid of the memcopys. + */ + memcpy((void *) scb->sense_cmd, (void *) generic_sense, + sizeof(generic_sense)); + + scb->sense_cmd[1] = (cmd->lun << 5); + scb->sense_cmd[4] = sizeof(cmd->sense_buffer); + + scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer); + scb->sg_list[0].length = sizeof(cmd->sense_buffer); + cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); - /* - * Send a sense command to the requesting target. + /* + * XXX - We should allow disconnection, but can't as it + * might allow overlapped tagged commands. */ - cmd->flags |= WAS_SENSE; - memcpy((void *) scb->sense_cmd, (void *) generic_sense, - sizeof(generic_sense)); - - scb->sense_cmd[1] = (cmd->lun << 5); - scb->sense_cmd[4] = sizeof(cmd->sense_buffer); - - scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer); - scb->sg_list[0].length = sizeof(cmd->sense_buffer); - req_buf = VIRT_TO_BUS(&scb->sg_list[0]); - cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); - - scb->control = scb->control & DISCENB; - scb->target_channel_lun = tcl; - addr = VIRT_TO_BUS(scb->sense_cmd); - scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); - memcpy(scb->SCSI_cmd_pointer, &addr, - sizeof(scb->SCSI_cmd_pointer)); - scb->SG_segment_count = 1; - memcpy(scb->SG_list_pointer, &req_buf, - sizeof(scb->SG_list_pointer)); - scb->data_count = scb->sg_list[0].length; - memcpy(scb->data_pointer, &(scb->sg_list[0].address), - sizeof(scb->data_pointer)); + /* hscb->control &= DISCENB; */ + hscb->control = 0; + hscb->target_status = 0; + hscb->SG_segment_count = 1; + + addr = VIRT_TO_BUS(&scb->sg_list[0]); + memcpy(&hscb->SG_list_pointer, &addr, + sizeof(hscb->SG_list_pointer)); + + memcpy(&hscb->data_pointer, &(scb->sg_list[0].address), + sizeof(hscb->data_pointer)); + /* Maintain SCB_LINKED_NEXT */ + hscb->data_count &= 0xFF000000; + hscb->data_count |= scb->sg_list[0].length; + + addr = VIRT_TO_BUS(scb->sense_cmd); + memcpy(&hscb->SCSI_cmd_pointer, &addr, + sizeof(hscb->SCSI_cmd_pointer)); + hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); - aic7xxx_putscb(p, scb); + scb->sg_count = hscb->SG_segment_count; + scb->flags |= SCB_SENSE; /* - * Ensure that the target is "BUSY" so we don't get overlapping - * commands if we happen to be doing tagged I/O. + * Ensure the target is busy since this will be an + * an untagged request. */ - aic7xxx_busy_target(scsi_id, channel, base); + aic7xxx_busy_target(p, target, channel, hscb->tag); + outb(SEND_SENSE, p->base + RETURN_1); + } /* first time sense, no errors */ + else + { + if (aic7xxx_error(cmd) == 0) + { + aic7xxx_error(cmd) = DID_RETRY_COMMAND; + } + } + break; - aic7xxx_add_waiting_scb(base, scb); - outb(SEND_SENSE, RETURN_1 + base); - } /* first time sense, no errors */ - else + case QUEUE_FULL: +#ifdef NOT_YET + if (scb->hscb->control & TAG_ENB) + { + if (cmd->device->queue_depth > 2) + { + cmd->device->queue_depth--; /* Not correct */ + printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth " + "reduced to %d\n", p->host_no, + TC_OF_SCB(scb), cmd->device->queue_depth); + } + /* + * XXX - Requeue this unconditionally? + */ + + /* + * We'd like to be able to give the SCB some more time + * (untimeout, then timeout). + */ + break; + } +#endif + printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; " + "queue depth %d, active %d\n", p->host_no, + TC_OF_SCB(scb), cmd->device->queue_depth, + p->device_status[TARGET_INDEX(cmd)].active_cmds); + + /* Else treat this as if it was a BUSY condition. */ + scb->hscb->target_status = (BUSY << 1) | + (scb->hscb->target_status & 0x01); + /* Fall through to the BUSY case. */ + + case BUSY: + printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n", + p->host_no, TC_OF_SCB(scb)); + if (!aic7xxx_error(cmd)) { - cmd->flags &= ~ASKED_FOR_SENSE; - if (aic7xxx_error(cmd) == 0) - { - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - } + /* + * The mid-level SCSI code should be fixed to + * retry the command at a later time instead of + * trying right away. + */ + aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8); } - break; + udelay(1000); /* A small pause (1ms) to help the drive */ + break; - case BUSY: - printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n", - p->host_no, scb->target_channel_lun); - if (!aic7xxx_error(cmd)) - { - /* The error code here used to be DID_BUS_BUSY, - * but after extensive testing, it has been determined - * that a DID_BUS_BUSY return is a waste of time. If - * the problem is something that will go away, then it - * will, if it isn't, then you don't want the endless - * looping that you get with a DID_BUS_BUSY. Better - * to be on the safe side and specify an error condition - * that will eventually lead to a reset or abort of some - * sort instead of an endless loop. - */ - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - } - break; - - case QUEUE_FULL: - printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no); - scb->state |= SCB_ASSIGNEDQ; - scbq_insert_tail(&p->assigned_scbs, scb); - break; - - default: - printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n", - p->host_no, scb->target_status); - if (!aic7xxx_error(cmd)) - { - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - } - break; - } /* end switch */ + default: + printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target " + "status 0x%x.\n", p->host_no, + TC_OF_SCB(scb), scb->hscb->target_status); + if (!aic7xxx_error(cmd)) + { + aic7xxx_error(cmd) = DID_RETRY_COMMAND; + } + break; + } /* end switch */ } /* end else of */ - break; + } + break; - case RESIDUAL: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); - } - else - { - cmd = scb->cmd; - /* - * Don't destroy valid residual information with - * residual coming from a check sense operation. - */ - if (!(cmd->flags & WAS_SENSE)) - { - /* - * We had an underflow. At this time, there's only - * one other driver that bothers to check for this, - * and cmd->underflow seems to be set rather half- - * heartedly in the higher-level SCSI code. - */ - actual = aic7xxx_length(cmd, scb->residual_SG_segment_count); - - actual -= (inb(SCB_RESID_DCNT2 + base) << 16) | - (inb(SCB_RESID_DCNT1 + base) << 8) | - inb(SCB_RESID_DCNT0 + base); - - if (actual < cmd->underflow) - { - printk(KERN_WARNING "scsi%d: Target %d underflow - " - "Wanted at least %u, got %u, residual SG count %d.\n", - p->host_no, cmd->target, cmd->underflow, actual, - inb(SCB_RESID_SGCNT + base)); - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - aic7xxx_status(cmd) = scb->target_status; - } - } - } - break; + case AWAITING_MSG: + { + unsigned char scb_index; + unsigned char message_offset; - case ABORT_TAG: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); - } - else - { - cmd = scb->cmd; - /* - * We didn't receive a valid tag back from the target - * on a reconnect. - */ - printk("scsi%d: Invalid tag received on target %d, channel %c, " - "lun %d - Sending ABORT_TAG.\n", p->host_no, - scsi_id, channel, cmd->lun & 0x07); + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; - cmd->result = (DID_RETRY_COMMAND << 16); - aic7xxx_done(p, scb); - } - break; - - case AWAITING_MSG: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) + /* + * This SCB had a MK_MESSAGE set in its control byte informing + * the sequencer that we wanted to send a special message to + * this target. + */ + message_offset = inb(p->base + MSG_LEN); + if (scb->flags & SCB_DEVICE_RESET) { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); + outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT); + outb(1, p->base + MSG_LEN); + printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n", + p->host_no, TC_OF_SCB(scb)); } - else + else if (scb->flags & SCB_ABORT) + { + if ((scb->hscb->control & TAG_ENB) != 0) + { + outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset); + } + else + { + outb(MSG_ABORT, p->base + MSG_OUT + message_offset); + } + outb(message_offset + 1, p->base + MSG_LEN); + printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n", + p->host_no, TC_OF_SCB(scb)); + } + else if (scb->flags & SCB_MSGOUT_WDTR) { - /* - * This SCB had a zero length command, informing the sequencer - * that we wanted to send a special message to this target. - * We only do this for BUS_DEVICE_RESET messages currently. - */ - if (scb->state & SCB_DEVICE_RESET) - { -#ifdef AIC7XXX_DEBUG_ABORT - printk ("aic7xxx: (isr) sending bus device reset to target %d\n", - scsi_id); -#endif - outb(MSG_BUS_DEVICE_RESET, MSG0 + base); - outb(1, MSG_LEN + base); - } - else - { - panic("scsi%d: AWAITING_SCB for an SCB that does " - "not have a waiting message.\n", p->host_no); - } - } - break; - - case IMMEDDONE: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n", - scsi_id, scb_index, scb->state); -#endif - if (scb->state & SCB_DEVICE_RESET) + aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT); + } + else if (scb->flags & SCB_MSGOUT_SDTR) { - int found; + unsigned char target_scratch; + unsigned short ultra_enable; + int i, sxfr; /* - * Go back to async/narrow transfers and renegotiate. + * Pull the user defined setting from scratch RAM. */ - aic7xxx_unbusy_target(scsi_id, channel, base); - p->needsdtr |= (p->needsdtr_copy & target_mask); - p->needwdtr |= (p->needwdtr_copy & target_mask); - p->sdtr_pending &= ~target_mask; - p->wdtr_pending &= ~target_mask; - scratch = inb(TARG_SCRATCH + base + scratch_offset); - scratch &= SXFR; - outb(scratch, TARG_SCRATCH + base + scratch_offset); - found = aic7xxx_reset_device(p, (int) scsi_id, channel); - printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs " - "aborted.\n", p->host_no, found); - /* Indicate that we want to call aic7xxx_done_aborted_scbs() */ - run_aborted_queue = TRUE; + target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + sxfr = target_scratch & SXFR; + ultra_enable = inb(p->base + ULTRA_ENB) | + (inb(p->base + ULTRA_ENB + 1) << 8); + if (ultra_enable & target_mask) + { + sxfr |= 0x100; + } + for (i = 0; i < num_aic7xxx_syncrates; i++) + { + if (sxfr == aic7xxx_syncrates[i].rate) + break; + } + aic7xxx_construct_sdtr(p, message_offset, + aic7xxx_syncrates[i].period, + target_scratch & WIDEXFER ? + MAX_OFFSET_16BIT : MAX_OFFSET_8BIT); } - else + else { - panic("scsi%d: Immediate complete for unknown operation.\n", - p->host_no); - } - break; + panic("aic7xxx: AWAITING_MSG for an SCB that does " + "not have a waiting message."); + } + } + break; - case DATA_OVERRUN: + case DATA_OVERRUN: { - unsigned int overrun; - - scb = (p->scb_array[inb(base + SCB_TAG)]); - overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) | - (inb(base + STCNT2) << 16); - overrun =0x00FFFFFF - overrun; - printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing " - "a retry.\n", p->host_no, overrun); - aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND; - break; + unsigned char scb_index = inb(p->base + SCB_TAG); + unsigned char lastphase = inb(p->base + LASTPHASE); + unsigned int i, overrun; + + scb = (p->scb_data->scb_array[scb_index]); + overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) | + (inb(p->base + STCNT + 2) << 16); + overrun = 0x00FFFFFF - overrun; + printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected " + "in %s phase, tag %d; forcing a retry.\n", + p->host_no, TC_OF_SCB(scb), overrun, + lastphase == P_DATAIN ? "Data-In" : "Data-Out", + scb->hscb->tag); + printk(KERN_WARNING "%s seen Data Phase. Length = %d, NumSGs = %d.\n", + inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't", + aic7xxx_length(scb->cmd, 0), scb->sg_count); + for (i = 0; i < scb->sg_count; i++) + { + printk(KERN_INFO " sg[%d] - Addr 0x%x : Length %d\n", + i, scb->sg_list[i].address, scb->sg_list[i].length); + } + /* + * XXX - What do we really want to do on an overrun? The + * mid-level SCSI code should handle this, but for now, + * we'll just indicate that the command should retried. + */ + aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND; } + break; -#if AIC7XXX_NOT_YET - /* XXX Fill these in later */ - case MESG_BUFFER_BUSY: - break; - case MSGIN_PHASEMIS: - break; -#endif +/* #if AIC7XXX_NOT_YET */ + /* XXX Fill these in later */ + case MSG_BUFFER_BUSY: + printk("aic7xxx: Message buffer busy.\n"); + break; + case MSGIN_PHASEMIS: + printk("aic7xxx: Message-in phasemis.\n"); + break; +/*#endif */ + + case ABORT_CMDCMPLT: + /* This interrupt serves to pause the sequencer until we can clean + * up the QOUTFIFO allowing us to handle any abort SCBs that may + * completed yet still have an SCB in the QINFIFO or waiting for + * selection queue. By the time we get here, we should have + * already cleaned up the queues, so all we need to do is unpause + * the sequencer. + */ + break; + + default: /* unknown */ + printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n", + p->host_no, intstat, inb(p->base + SCSISIGI)); + break; + } + + /* + * Clear the sequencer interrupt and unpause the sequencer. + */ + outb(CLRSEQINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause always */ TRUE); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_handle_scsiint + * + * Description: + * Interrupt handler for SCSI interrupts (SCSIINT). + *-F*************************************************************************/ +static void +aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) +{ + unsigned char scb_index; + unsigned char status; + struct aic7xxx_scb *scb; + + scb_index = inb(p->base + SCB_TAG); + status = inb(p->base + SSTAT1); - default: /* unknown */ - printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n", - p->host_no, intstat, inb(SCSISIGI + base)); - break; + if (scb_index < p->scb_data->numscbs) + { + scb = p->scb_data->scb_array[scb_index]; + if ((scb->flags & SCB_ACTIVE) == 0) + { + scb = NULL; } + } + else + { + scb = NULL; + } + if ((status & SCSIRSTI) != 0) + { + char channel; + + channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A'; + + printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n", + p->host_no, channel); /* - * Clear the sequencer interrupt and unpause the sequencer. + * Go through and abort all commands for the channel, but do not + * reset the channel again. */ - outb(CLRSEQINT, CLRINT + base); - UNPAUSE_SEQUENCER(p); + aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE); + scb = NULL; } - - if (intstat & SCSIINT) + else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) ) { - int status = inb(SSTAT1 + base); - scsi_id = (inb(SCSIID + base) >> 4) & 0x0F; - channel = 'A'; - if (inb(SBLKCTL + base) & SELBUSB) - { - channel = 'B'; + /* + * First look at what phase we were last in. If it's message-out, + * chances are pretty good that the bus free was in response to + * one of our abort requests. + */ + unsigned char lastphase = inb(p->base + LASTPHASE); + unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F; + char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A'; + int printerror = TRUE; + + outb(0, p->base + SCSISEQ); + if (lastphase == P_MESGOUT) + { + unsigned char sindex; + unsigned char message; + + sindex = inb(p->base + SINDEX); + message = inb(p->base + sindex - 1); + + if (message == MSG_ABORT) + { + printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag); + aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL); + aic7xxx_run_done_queue(p, /* complete */ TRUE); + scb = NULL; + printerror = 0; + } + else if (message == MSG_ABORT_TAG) + { + printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag); + aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag); + aic7xxx_run_done_queue(p, /* complete */ TRUE); + scb = NULL; + printerror = 0; + } + else if (message == MSG_BUS_DEV_RESET) + { + aic7xxx_handle_device_reset(p, target, channel); + scb = NULL; + printerror = 0; + } } + if (printerror != 0) + { + if (scb != NULL) + { + unsigned char tag; + + if ((scb->hscb->control & TAG_ENB) != 0) + { + tag = scb->hscb->tag; + } + else + { + tag = SCB_LIST_NULL; + } + aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag); + } + else + { + aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL); + } + printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, " + "SEQADDR = 0x%x\n", p->host_no, lastphase, + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + } + outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1); + outb(CLRBUSFREE, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + restart_sequencer(p); + } + else if ((status & SELTO) != 0) + { + unsigned char scbptr; + unsigned char nextscb; + Scsi_Cmnd *cmd; + + scbptr = inb(p->base + WAITING_SCBH); + outb(scbptr, p->base + SCBPTR); + scb_index = inb(p->base + SCB_TAG); - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (status & SCSIRSTI) + scb = NULL; + if (scb_index < p->scb_data->numscbs) { - PAUSE_SEQUENCER(p); - printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n", - p->host_no, channel); - /* - * Go through and abort all commands for the channel, but do not - * reset the channel again. - */ - aic7xxx_reset_channel(p, channel, FALSE); - run_aborted_queue = TRUE; + scb = p->scb_data->scb_array[scb_index]; + if ((scb->flags & SCB_ACTIVE) == 0) + { + scb = NULL; + } } - else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) + if (scb == NULL) + { + printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n", + p->host_no, scb_index); + printk(KERN_WARNING " SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x " + "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ), + inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8), + inb(p->base + SSTAT0), inb(p->base + SSTAT1)); + } + else { - printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no); /* - * Turn off the interrupt and set status to zero, so that it - * falls through the rest of the SCSIINT code. + * XXX - If we queued an abort tag, go clean up the disconnected list. */ - outb(status, CLRSINT1 + base); - UNPAUSE_SEQUENCER(p); - outb(CLRSCSIINT, CLRINT + base); + cmd = scb->cmd; + cmd->result = (DID_TIME_OUT << 16); + + /* + * Clear an pending messages for the timed out + * target and mark the target as free. + */ + outb(0, p->base + MSG_LEN); + aic7xxx_index_busy_target(p, cmd->target, + cmd->channel ? 'B': 'A', /*unbusy*/ TRUE); + outb(0, p->base + SCB_CONTROL); + + /* + * Shift the waiting for selection queue forward + */ + nextscb = inb(p->base + SCB_NEXT); + outb(nextscb, p->base + WAITING_SCBH); + + /* + * Put this SCB back on the free list. + */ + aic7xxx_add_curscb_to_free_list(p); + } + /* + * Stop the selection. + */ + outb(0, p->base + SCSISEQ); + outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + restart_sequencer(p); + } + else if (scb == NULL) + { + printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid " + "during scsiint 0x%x scb(%d)\n" + " SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n", + p->host_no, status, scb_index, inb(p->base + SIMODE0), + inb(p->base + SIMODE1), inb(p->base + SSTAT0), + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + /* + * Turn off the interrupt and set status to zero, so that it + * falls through the rest of the SCSIINT code. + */ + outb(status, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause always */ TRUE); + scb = NULL; + } + else if (status & SCSIPERR) + { + /* + * Determine the bus phase and queue an appropriate message. + */ + char *phase; + Scsi_Cmnd *cmd; + unsigned char mesg_out = MSG_NOOP; + unsigned char lastphase = inb(p->base + LASTPHASE); + + cmd = scb->cmd; + switch (lastphase) + { + case P_DATAOUT: + phase = "Data-Out"; + break; + case P_DATAIN: + phase = "Data-In"; + mesg_out = MSG_INITIATOR_DET_ERR; + break; + case P_COMMAND: + phase = "Command"; + break; + case P_MESGOUT: + phase = "Message-Out"; + break; + case P_STATUS: + phase = "Status"; + mesg_out = MSG_INITIATOR_DET_ERR; + break; + case P_MESGIN: + phase = "Message-In"; + mesg_out = MSG_PARITY_ERROR; + break; + default: + phase = "unknown"; + break; + } + + /* + * A parity error has occurred during a data + * transfer phase. Flag it and continue. + */ + printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n", + p->host_no, TC_OF_SCB(scb), phase); + + /* + * We've set the hardware to assert ATN if we get a parity + * error on "in" phases, so all we need to do is stuff the + * message buffer with the appropriate message. "In" phases + * have set mesg_out to something other than MSG_NOP. + */ + if (mesg_out != MSG_NOOP) + { + outb(mesg_out, p->base + MSG_OUT); + outb(1, p->base + MSG_LEN); scb = NULL; } - else if (status & SCSIPERR) + else { - char *phase; - unsigned char mesg_out = MSG_NOP; - unsigned char lastphase = inb(LASTPHASE + base); + /* + * Should we allow the target to make this decision for us? + */ + cmd->result = DID_RETRY_COMMAND << 16; + } + outb(CLRSCSIPERR, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause_always */ TRUE); + } + else + { + /* + * We don't know what's going on. Turn off the + * interrupt source and try to continue. + */ + printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status); + outb(status, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause always */ TRUE); + scb = NULL; + } + if (scb != NULL) + { + aic7xxx_done(p, scb); + aic7xxx_done_cmds_complete(p); + } +} - cmd = scb->cmd; - switch (lastphase) - { - case P_DATAOUT: - phase = "Data-Out"; - break; - case P_DATAIN: - phase = "Data-In"; - mesg_out = MSG_INITIATOR_DET_ERROR; - break; - case P_COMMAND: - phase = "Command"; - break; - case P_MESGOUT: - phase = "Message-Out"; - break; - case P_STATUS: - phase = "Status"; - mesg_out = MSG_INITIATOR_DET_ERROR; - break; - case P_MESGIN: - phase = "Message-In"; - mesg_out = MSG_MSG_PARITY_ERROR; - break; - default: - phase = "unknown"; - break; - } +/*+F************************************************************************* + * Function: + * aic7xxx_isr + * + * Description: + * SCSI controller interrupt handler. + * + * NOTE: Since we declared this using SA_INTERRUPT, interrupts should + * be disabled all through this function unless we say otherwise. + *-F*************************************************************************/ +static void +aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct aic7xxx_host *p; + unsigned char intstat; + unsigned long flags; - /* - * A parity error has occurred during a data - * transfer phase. Flag it and continue. - */ - printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, " - "channel %d, lun %d.\n", p->host_no, phase, - cmd->target, cmd->channel & 0x01, cmd->lun & 0x07); + p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata; - /* - * We've set the hardware to assert ATN if we get a parity - * error on "in" phases, so all we need to do is stuff the - * message buffer with the appropriate message. In phases - * have set mesg_out to something other than MSG_NOP. - */ - if (mesg_out != MSG_NOP) - { - outb(mesg_out, MSG0 + base); - outb(1, MSG_LEN + base); - cmd->result = DID_PARITY << 16; - } - else - { - /* - * Should we allow the target to make this decision for us? - */ - cmd->result = DID_RETRY_COMMAND << 16; - } - aic7xxx_done(p, scb); + /* + * Search for the host with a pending interrupt. If we can't find + * one, then we've encountered a spurious interrupt. + */ + while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND)) + { + if (p->next == NULL) + { + p = NULL; } - else if (status & SELTO) + else { - unsigned char waiting; + p = (struct aic7xxx_host *) p->next->hostdata; + } + } - cmd = scb->cmd; + if (p == NULL) + return; - cmd->result = (DID_TIME_OUT << 16); - /* - * Clear an pending messages for the timed out - * target and mark the target as free. - */ - ha_flags = inb(FLAGS + base); - outb(0, MSG_LEN + base); - aic7xxx_unbusy_target(scsi_id, channel, base); - /* - * Stop the selection. - */ - outb(0, SCSISEQ + base); - outb(0, SCB_CONTROL + base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); + /* + * Handle all the interrupt sources - especially for SCSI + * interrupts, we won't get a second chance at them. + */ + intstat = inb(p->base + INTSTAT); - /* - * Shift the waiting for selection queue forward - */ - waiting = inb(WAITING_SCBH + base); - outb(waiting, SCBPTR + base); - waiting = inb(SCB_NEXT + base); - outb(waiting, WAITING_SCBH + base); + /* + * Keep track of interrupts for /proc/scsi + */ + p->isr_count++; - RESTART_SEQUENCER(p); - aic7xxx_done(p, scb); - } - else if (!(status & BUSFREE)) + if (!(p->flags & A_SCANNED) && (p->isr_count == 1)) + { + /* + * We must only have one card at this IRQ and it must have been + * added to the board data before the spurious interrupt occurred. + * It is sufficient that we check isr_count and not the spurious + * interrupt count. + */ + printk("scsi%d: Encountered spurious interrupt.\n", p->host_no); + if (intstat) { - /* - * We don't know what's going on. Turn off the - * interrupt source and try to continue. - */ - printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status); - outb(status, CLRSINT1 + base); - UNPAUSE_SEQUENCER(p); - outb(CLRSCSIINT, CLRINT + base); + /* Try clearing all interrupts. */ + outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT); } + return; + } + + if (p->flags & IN_ISR) + { + printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n", + p->host_no); + return; } - if (run_aborted_queue) - aic7xxx_done_aborted_scbs(p); + /* + * Indicate that we're in the interrupt handler. + */ + save_flags(flags); + cli(); + p->flags |= IN_ISR; if (intstat & CMDCMPLT) { - int complete; + struct aic7xxx_scb *scb = NULL; + Scsi_Cmnd *cmd; + unsigned char qoutcnt; + unsigned char scb_index; + int i, interrupts_cleared = 0; /* * The sequencer will continue running when it * issues this interrupt. There may be >1 commands * finished, so loop until we've processed them all. */ - do { - complete = inb(QOUTFIFO + base); + qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask; - scb = (p->scb_array[complete]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n" - " QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, " - "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base), - inb(QINCNT + base), scb->state, (unsigned long) scb->cmd, - scb->position); - outb(CLRCMDINT, CLRINT + base); - continue; - } - cmd = scb->cmd; - cmd->result |= (aic7xxx_error(cmd) << 16); - if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE)) +#if 1 + if (qoutcnt >= p->qfullcount - 1) + printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, " + "qoutcnt = %d.\n", qoutcnt); +#endif + while (qoutcnt > 0) + { + for (i = 0; i < qoutcnt; i++) { - /* - * Got sense information. - */ - cmd->flags &= ASKED_FOR_SENSE; + scb_index = inb(p->base + QOUTFIFO); + scb = p->scb_data->scb_array[scb_index]; + if (scb == NULL) + { + printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, " + "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index, + inb(p->base + QOUTCNT), inb(p->base + QINCNT)); + continue; + } + else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) + { + printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, " + "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n", + p->host_no, scb_index, inb(p->base + QOUTCNT), + inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd); + continue; + } + cmd = scb->cmd; + if (scb->hscb->residual_SG_segment_count != 0) + { + aic7xxx_calculate_residual(p, scb); + } + if ((scb->flags & SCB_QUEUED_ABORT) != 0) + { + /* + * Have to clean up any possible entries in the + * waiting queue and the QINFIFO. + */ + int target; + char channel; + int lun; + unsigned char tag; + + tag = SCB_LIST_NULL; + target = cmd->target; + lun = cmd->lun; + channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + if (scb->hscb->control & TAG_ENB) + { + tag = scb->hscb->tag; + } + aic7xxx_reset_device(p, target, channel, lun, tag); + /* + * Run the done queue, but don't complete the commands; we + * do this once at the end of the loop. + */ + aic7xxx_run_done_queue(p, /*complete*/ FALSE); + } + cmd->result |= (aic7xxx_error(cmd) << 16); + p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS; + aic7xxx_done(p, scb); } - p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS; - /* * Clear interrupt status before checking the output queue again. * This eliminates a race condition whereby a command could @@ -3243,56 +3941,152 @@ * so notification of the command being complete never made it * back up to the kernel. */ - outb(CLRCMDINT, CLRINT + base); - aic7xxx_done(p, scb); + outb(CLRCMDINT, p->base + CLRINT); + interrupts_cleared++; + qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask; + } -#ifdef AIC7XXX_PROC_STATS - /* - * XXX: we should actually know how much actually transferred - * XXX: for each command, but apparently that's too difficult. - */ - actual = aic7xxx_length(cmd, 0); - if (!(cmd->flags & WAS_SENSE) && (actual > 0)) + if (interrupts_cleared == 0) + { + outb(CLRCMDINT, p->base + CLRINT); + } + + aic7xxx_done_cmds_complete(p); + } + + if (intstat & BRKADRINT) + { + int i; + unsigned char errno = inb(p->base + ERROR); + + printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno); + for (i = 0; i < NUMBER(hard_error); i++) + { + if (errno & hard_error[i].errno) { - struct aic7xxx_xferstats *sp; - long *ptr; - int x; + printk(KERN_ERR " %s\n", hard_error[i].errmesg); + } + } + printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no, + inb(p->base + ERROR), + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL); + aic7xxx_run_done_queue(p, /*complete*/ TRUE); + } + + if (intstat & SEQINT) + { + aic7xxx_handle_seqint(p, intstat); + } + + if (intstat & SCSIINT) + { + aic7xxx_handle_scsiint(p, intstat); + } + + if (p->waiting_scbs.head != NULL) + { + aic7xxx_run_waiting_queues(p); + } + + p->flags &= ~IN_ISR; + restore_flags(flags); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_device_queue_depth + * + * Description: + * Determines the queue depth for a given device. There are two ways + * a queue depth can be obtained for a tagged queueing device. One + * way is the default queue depth which is determined by whether + * AIC7XXX_CMDS_PER_LUN is defined. If it is defined, then it is used + * as the default queue depth. Otherwise, we use either 4 or 8 as the + * default queue depth (dependent on the number of hardware SCBs). + * The other way we determine queue depth is through the use of the + * aic7xxx_tag_info array which is enabled by defining + * AIC7XXX_TAGGED_QUEUEING_BY_DEVICE. This array can be initialized + * with queue depths for individual devices. It also allows tagged + * queueing to be [en|dis]abled for a specific adapter. + *-F*************************************************************************/ +static void +aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device) +{ + int default_depth = 2; + + device->queue_depth = default_depth; +#ifdef AIC7XXX_TAGGED_QUEUEING + if (device->tagged_supported) + { + unsigned short target_mask; + int tag_enabled = TRUE; - sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07]; - sp->xfers++; + target_mask = (1 << (device->id | (device->channel << 3))); + +#ifdef AIC7XXX_CMDS_PER_LUN + default_depth = AIC7XXX_CMDS_PER_LUN; +#else + if (p->scb_data->maxhscbs <= 4) + { + default_depth = 4; /* Not many SCBs to work with. */ + } + else + { + default_depth = 8; + } +#endif + + if (!(p->discenable & target_mask)) + { + printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to " + "enable tagged queueing.\n", + p->host_no, device->id, device->channel); + } + else + { +#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE + device->queue_depth = default_depth; +#else + if (p->instance > NUMBER(aic7xxx_tag_info)) + { + device->queue_depth = default_depth; + } + else + { + unsigned char tindex; - if (cmd->request.cmd == WRITE) + tindex = device->id | (device->channel << 3); + if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0) { - sp->w_total++; - sp->w_total512 += (actual >> 9); - ptr = sp->w_bins; + tag_enabled = FALSE; + device->queue_depth = 2; /* Tagged queueing is disabled. */ } - else + else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0) { - sp->r_total++; - sp->r_total512 += (actual >> 9); - ptr = sp->r_bins; + device->queue_depth = default_depth; } - for (x = 9; x <= 17; x++) + else { - if (actual < (1 << x)) - { - ptr[x - 9]++; - break; - } + device->queue_depth = + aic7xxx_tag_info[p->instance].tag_commands[tindex]; } - if (x > 17) + } +#endif + if ((device->tagged_queue == 0) && tag_enabled) + { + if (aic7xxx_verbose) { - ptr[x - 9]++; + printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, " + "queue depth %d.\n", p->host_no, + device->id, device->channel, device->queue_depth); } + device->tagged_queue = 1; + device->current_tag = SCB_LIST_NULL; } -#endif /* AIC7XXX_PROC_STATS */ - - } while (inb(QOUTCNT + base) & p->qcntmask); + } } - aic7xxx_done_cmds_complete(p); - p->flags &= ~IN_ISR; - aic7xxx_run_waiting_queues(p); +#endif } /*+F************************************************************************* @@ -3307,59 +4101,18 @@ * algorithm for determining the queue depth based on the maximum * SCBs for the controller. *-F*************************************************************************/ -static void aic7xxx_select_queue_depth(struct Scsi_Host *host, +static void +aic7xxx_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) { - Scsi_Device *device = scsi_devs; - int tq_depth = 2; + Scsi_Device *device; struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata; -#ifdef AIC7XXX_CMDS_PER_LUN - tq_depth = AIC7XXX_CMDS_PER_LUN; -#else - { - if (p->maxhscbs <= 4) - { - tq_depth = 4; /* Not many SCBs to work with. */ - } - else - { - tq_depth = 8; - } - } -#endif - for (device = scsi_devs; device != NULL; device = device->next) { if (device->host == host) { - device->queue_depth = 2; -#ifdef AIC7XXX_TAGGED_QUEUEING - if (device->tagged_supported) - { - unsigned short target_mask = (1 << device->id) | device->channel; - - if (!(p->discenable & target_mask)) - { - printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable " - "tagged queueing for target %d, channel %d, LUN %d.\n", - host->host_no, device->id, device->channel, device->lun); - } - else - { - device->queue_depth = tq_depth; - if (device->tagged_queue == 0) - { - printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, " - "channel %d, LUN %d, queue depth %d.\n", host->host_no, - device->id, device->channel, device->lun, - device->queue_depth); - device->tagged_queue = 1; - device->current_tag = SCB_LIST_NULL; - } - } - } -#endif + aic7xxx_device_queue_depth(p, device); } } } @@ -3386,7 +4139,7 @@ * The fourth byte's lowest bit seems to be an enabled/disabled * flag (rest of the bits are reserved?). *-F*************************************************************************/ -static aha_type +static aha_chip_type aic7xxx_probe(int slot, int base, aha_status_type *bios) { int i; @@ -3395,7 +4148,7 @@ static struct { int n; unsigned char signature[sizeof(buf)]; - aha_type type; + aha_chip_type type; int bios_disabled; } AIC7xxx[] = { { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */ @@ -3434,7 +4187,8 @@ return (AIC7xxx[i].type); } - printk("aic7xxx: Disabled at slot %d, ignored.\n", slot); + printk("aic7xxx: " + "disabled at slot %d, ignored.\n", slot); } } @@ -3461,10 +4215,9 @@ * useful in that it gives us an 800 nsec timer. After a read from the * SEECTL_2840 register the timing flag is cleared and goes high 800 nsec * later. - * *-F*************************************************************************/ static int -read_2840_seeprom(int base, struct seeprom_config *sc) +read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc) { int i = 0, k = 0; unsigned char temp; @@ -3477,11 +4230,11 @@ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}}; #define CLOCK_PULSE(p) \ - while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0) \ + while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0) \ { \ ; /* Do nothing */ \ } \ - (void) inb(SEECTL_2840 + base); + (void) inb(p->base + SEECTL_2840); /* * Read the first 32 registers of the seeprom. For the 2840, @@ -3494,8 +4247,8 @@ /* * Send chip select for one clock cycle. */ - outb(CK_2840 | CS_2840, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(CK_2840 | CS_2840, p->base + SEECTL_2840); + CLOCK_PULSE(p); /* * Now we're ready to send the read command followed by the @@ -3504,11 +4257,11 @@ for (i = 0; i < seeprom_read.len; i++) { temp = CS_2840 | seeprom_read.bits[i]; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); temp = temp ^ CK_2840; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); } /* * Send the 6 bit address (MSB first, LSB last). @@ -3518,11 +4271,11 @@ temp = k; temp = (temp >> i) & 1; /* Mask out all but lower bit. */ temp = CS_2840 | temp; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); temp = temp ^ CK_2840; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); } /* @@ -3534,12 +4287,12 @@ for (i = 0; i <= 16; i++) { temp = CS_2840; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); temp = temp ^ CK_2840; - seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840); - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); } /* * The serial EEPROM has a checksum in the last word. Keep a @@ -3555,12 +4308,12 @@ /* * Reset the chip select for the next command cycle. */ - outb(0, SEECTL_2840 + base); - CLOCK_PULSE(base); - outb(CK_2840, SEECTL_2840 + base); - CLOCK_PULSE(base); - outb(0, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(0, p->base + SEECTL_2840); + CLOCK_PULSE(p); + outb(CK_2840, p->base + SEECTL_2840); + CLOCK_PULSE(p); + outb(0, p->base + SEECTL_2840); + CLOCK_PULSE(p); } #if 0 @@ -3589,6 +4342,53 @@ /*+F************************************************************************* * Function: + * acquire_seeprom + * + * Description: + * Acquires access to the memory port on PCI controllers. + *-F*************************************************************************/ +static inline int +acquire_seeprom(struct aic7xxx_host *p) +{ + int wait; + + /* + * Request access of the memory port. When access is + * granted, SEERDY will go high. We use a 1 second + * timeout which should be near 1 second more than + * is needed. Reason: after the 7870 chip reset, there + * should be no contention. + */ + outb(SEEMS, p->base + SEECTL); + wait = 1000; /* 1000 msec = 1 second */ + while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0)) + { + wait--; + udelay(1000); /* 1 msec */ + } + if ((inb(p->base + SEECTL) & SEERDY) == 0) + { + outb(0, p->base + SEECTL); + return (0); + } + return (1); +} + +/*+F************************************************************************* + * Function: + * release_seeprom + * + * Description: + * Releases access to the memory port on PCI controllers. + *-F*************************************************************************/ +static inline void +release_seeprom(struct aic7xxx_host *p) +{ + outb(0, p->base + SEECTL); +} + +/*+F************************************************************************* + * Function: * read_seeprom * * Description: @@ -3626,7 +4426,7 @@ * first). The clock cycling from low to high initiates the next data * bit to be sent from the chip. * - * The 7870 interface to the 93C46 serial EEPROM is through the SEECTL + * The 78xx interface to the 93C46 serial EEPROM is through the SEECTL * register. After successful arbitration for the memory port, the * SEECS bit of the SEECTL register is connected to the chip select. * The SEECK, SEEDO, and SEEDI are connected to the clock, data out, @@ -3636,17 +4436,14 @@ * to this is when we first request access to the memory port. The * SEERDY goes high to signify that access has been granted and, for * this case, has no implied timing. - * *-F*************************************************************************/ static int -read_seeprom(int base, int offset, struct seeprom_config *sc, - seeprom_chip_type chip) +read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray, + unsigned int len, seeprom_chip_type chip) { int i = 0, k; - unsigned long timeout; unsigned char temp; unsigned short checksum = 0; - unsigned short *seeprom = (unsigned short *) sc; struct seeprom_cmd { unsigned char len; unsigned char bits[3]; @@ -3654,43 +4451,33 @@ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}}; #define CLOCK_PULSE(p) \ - while ((inb(SEECTL + base) & SEERDY) == 0) \ + while ((inb(p->base + SEECTL) & SEERDY) == 0) \ { \ ; /* Do nothing */ \ } /* - * Request access of the memory port. When access is - * granted, SEERDY will go high. We use a 1 second - * timeout which should be near 1 second more than - * is needed. Reason: after the 7870 chip reset, there - * should be no contention. + * Request access of the memory port. */ - outb(SEEMS, SEECTL + base); - timeout = jiffies + 100; /* 1 second timeout */ - while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0)) + if (acquire_seeprom(p) == 0) { - ; /* Do nothing! Wait for access to be granted. */ - } - if ((inb(SEECTL + base) & SEERDY) == 0) - { - outb(0, SEECTL + base); return (0); } /* - * Read the first 32 registers of the seeprom. For the 7870, - * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers - * but only the first 32 are used by Adaptec BIOS. The loop - * will range from 0 to 31. + * Read 'len' registers of the seeprom. For the 7870, the 93C46 + * SEEPROM is a 1024-bit device with 64 16-bit registers but only + * the first 32 are used by Adaptec BIOS. Some adapters use the + * 93C56 SEEPROM which is a 2048-bit device. The loop will range + * from 0 to 'len' - 1. */ - for (k = 0; k < (sizeof(*sc) / 2); k++) + for (k = 0; k < len; k++) { /* * Send chip select for one clock cycle. */ - outb(SEEMS | SEECK | SEECS, SEECTL + base); - CLOCK_PULSE(base); + outb(SEEMS | SEECK | SEECS, p->base + SEECTL); + CLOCK_PULSE(p); /* * Now we're ready to send the read command followed by the @@ -3699,25 +4486,25 @@ for (i = 0; i < seeprom_read.len; i++) { temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1); - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); temp = temp ^ SEECK; - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); } /* - * Send the 6 bit address (MSB first, LSB last). + * Send the 6 or 8 bit address (MSB first, LSB last). */ for (i = ((int) chip - 1); i >= 0; i--) { temp = k + offset; temp = (temp >> i) & 1; /* Mask out all but lower bit. */ temp = SEEMS | SEECS | (temp << 1); - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); temp = temp ^ SEECK; - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); } /* @@ -3729,56 +4516,57 @@ for (i = 0; i <= 16; i++) { temp = SEEMS | SEECS; - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); temp = temp ^ SEECK; - seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI); - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); } /* - * The serial EEPROM has a checksum in the last word. Keep a - * running checksum for all words read except for the last - * word. We'll verify the checksum after all words have been - * read. + * The serial EEPROM should have a checksum in the last word. + * Keep a running checksum for all words read except for the + * last word. We'll verify the checksum after all words have + * been read. */ - if (k < (sizeof(*sc) / 2) - 1) + if (k < (len - 1)) { - checksum = checksum + seeprom[k]; + checksum = checksum + scarray[k]; } /* * Reset the chip select for the next command cycle. */ - outb(SEEMS, SEECTL + base); - CLOCK_PULSE(base); - outb(SEEMS | SEECK, SEECTL + base); - CLOCK_PULSE(base); - outb(SEEMS, SEECTL + base); - CLOCK_PULSE(base); + outb(SEEMS, p->base + SEECTL); + CLOCK_PULSE(p); + outb(SEEMS | SEECK, p->base + SEECTL); + CLOCK_PULSE(p); + outb(SEEMS, p->base + SEECTL); + CLOCK_PULSE(p); } /* * Release access to the memory port and the serial EEPROM. */ - outb(0, SEECTL + base); + release_seeprom(p); #if 0 - printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum); + printk("Computed checksum 0x%x, checksum read 0x%x\n", + checksum, scarray[len - 1]); printk("Serial EEPROM:"); - for (k = 0; k < (sizeof(*sc) / 2); k++) + for (k = 0; k < len; k++) { if (((k % 8) == 0) && (k != 0)) { printk("\n "); } - printk(" 0x%x", seeprom[k]); + printk(" 0x%x", scarray[k]); } printk("\n"); #endif - if (checksum != sc->checksum) + if (checksum != scarray[len - 1]) { return (0); } @@ -3789,563 +4577,452 @@ /*+F************************************************************************* * Function: - * detect_maxscb + * write_brdctl * * Description: - * Detects the maximum number of SCBs for the controller and returns - * the count and a mask in config (config->maxscbs, config->qcntmask). + * Writes a value to the BRDCTL register. *-F*************************************************************************/ -static void -detect_maxscb(struct aic7xxx_host_config *config) +static inline void +write_brdctl(struct aic7xxx_host *p, unsigned char value) { - unsigned char sblkctl_reg; - int base, i; - -#ifdef AIC7XXX_PAGE_ENABLE - config->flags |= PAGE_ENABLED; -#endif - base = config->base; - switch (config->type) - { - case AIC_7770: - case AIC_7771: - case AIC_284x: - /* - * Check for Rev C or E boards. Rev E boards can supposedly have - * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs. - * It's still not clear extactly what is different about the Rev E - * boards, but we think it allows 8 bit entries in the QOUTFIFO to - * support "paging" SCBs (more than 4 commands can be active at once). - * - * The Rev E boards have a read/write autoflush bit in the - * SBLKCTL register, while in the Rev C boards it is read only. - */ - sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS; - outb(sblkctl_reg, SBLKCTL + base); - if (inb(SBLKCTL + base) == sblkctl_reg) - { - /* - * We detected a Rev E board, we allow paging on this board. - */ - printk(KERN_INFO "aic7xxx: %s Rev E and subsequent.\n", - board_names[config->type]); - outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base); - } - else - { - /* Do not allow paging. */ - config->flags &= ~PAGE_ENABLED; - printk(KERN_INFO "aic7xxx: %s Rev C and previous.\n", - board_names[config->type]); - } - break; - - default: - break; - } - - /* - * Walk the SCBs to determine how many there are. - */ - i = 1; - outb(0, SCBPTR + base); - outb(0, SCBARRAY + base); - - while (i < AIC7XXX_MAXSCB) - { - outb(i, SCBPTR + base); - outb(i, SCBARRAY + base); - if (inb(SCBARRAY + base) != i) - break; - outb(0, SCBPTR + base); - if (inb(SCBARRAY + base) != 0) - break; - - outb(i, SCBPTR + base); /* Clear the control byte. */ - outb(0, SCBARRAY + base); - - config->qcntmask |= i; /* Update the count mask. */ - i++; - } - outb(i, SCBPTR + base); /* Ensure we clear the control bytes. */ - outb(0, SCBARRAY + base); - outb(0, SCBPTR + base); - outb(0, SCBARRAY + base); - - config->maxhscbs = i; - config->qcntmask |= i; - if ((config->flags & PAGE_ENABLED) && (config->maxhscbs < AIC7XXX_MAXSCB)) - { - config->maxscbs = AIC7XXX_MAXSCB; - } - else - { - config->flags &= ~PAGE_ENABLED; /* Disable paging if we have 255 SCBs!. */ - config->maxscbs = config->maxhscbs; - } - - printk(KERN_INFO "aic7xxx: Memory check yields %d SCBs", config->maxhscbs); - if (config->flags & PAGE_ENABLED) - printk(", %d page-enabled SCBs.\n", config->maxscbs); - else - printk(", paging not enabled.\n"); + unsigned char brdctl; + brdctl = BRDCS | BRDSTB; + outb(brdctl, p->base + BRDCTL); + brdctl |= value; + outb(brdctl, p->base + BRDCTL); + brdctl &= ~BRDSTB; + outb(brdctl, p->base + BRDCTL); + brdctl &= ~BRDCS; + outb(brdctl, p->base + BRDCTL); } /*+F************************************************************************* * Function: - * aic7xxx_register + * read_brdctl * * Description: - * Register a Adaptec aic7xxx chip SCSI controller with the kernel. + * Reads the BRDCTL register. *-F*************************************************************************/ -static int -aic7xxx_register(Scsi_Host_Template *template, - struct aic7xxx_host_config *config) +static inline unsigned char +read_brdctl(struct aic7xxx_host *p) { - int i; - unsigned char sblkctl, flags = 0; - int max_targets; - int found = 1; - unsigned int sram, base; - unsigned char target_settings; - unsigned char scsi_conf, host_conf; - unsigned short ultraenable = 0; - int have_seeprom = FALSE; - struct Scsi_Host *host; - struct aic7xxx_host *p; - struct seeprom_config sc; - - base = config->base; + outb(BRDRW | BRDCS, p->base + BRDCTL); + return (inb(p->base + BRDCTL)); +} - /* - * Lock out other contenders for our i/o space. - */ - request_region(base, MAXREG - MINREG, "aic7xxx"); +/*+F************************************************************************* + * Function: + * configure_termination + * + * Description: + * Configures the termination settings on PCI adapters that have + * SEEPROMs available. + *-F*************************************************************************/ +static void +configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1, + unsigned short adapter_control, unsigned char max_targ) +{ + unsigned char brdctl_int, brdctl_ext; + int internal50_present; + int internal68_present = 0; + int external_present = 0; + int eprom_present; + int high_on; + int low_on; + int old_verbose; + + if (acquire_seeprom(p)) + { + if (adapter_control & CFAUTOTERM) + { + old_verbose = aic7xxx_verbose; + printk(KERN_INFO "aic7xxx: Warning - detected auto-termination. Please " + "verify driver"); + printk(KERN_INFO " detected settings and use manual termination " + "if necessary."); - switch (config->type) - { - case AIC_7770: - case AIC_7771: - /* - * Use the boot-time option for the interrupt trigger type. If not - * supplied (-1), then we use BIOS settings to determine the interrupt - * trigger type (level or edge) and use this value for pausing and - * unpausing the sequencer. - */ - switch (aic7xxx_irq_trigger) - { - case 0: config->unpause = INTEN; /* Edge */ - break; - case 1: config->unpause = IRQMS | INTEN; /* Level */ - break; - case -1: - default: config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN; - break; - } - config->pause = config->unpause | PAUSE; + /* Configure auto termination. */ + outb(SEECS | SEEMS, p->base + SEECTL); /* - * For some 274x boards, we must clear the CHIPRST bit and pause - * the sequencer. For some reason, this makes the driver work. - * For 284x boards, we give it a CHIPRST just like the 294x boards. + * First read the status of our cables. Set the rom bank to + * 0 since the bank setting serves as a multiplexor for the + * cable detection logic. BRDDAT5 controls the bank switch. */ - outb(config->pause | CHIPRST, HCNTRL + base); - aic7xxx_delay(1); - if (inb(HCNTRL + base) & CHIPRST) - { - printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n"); - } - outb(config->pause, HCNTRL + base); + write_brdctl(p, 0); /* - * Just to be on the safe side with the 274x, we will re-read the irq - * since there was some issue about resetting the board. + * Now read the state of the internal connectors. The + * bits BRDDAT6 and BRDDAT7 are 0 when cables are present + * set when cables are not present (BRDDAT6 is INT50 and + * BRDDAT7 is INT68). */ - config->irq = inb(INTDEF + base) & 0x0F; - if ((config->type == AIC_7771) && - (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED) - { - config->bios = AIC_DISABLED; - config->flags |= USE_DEFAULTS; - } - else + brdctl_int = read_brdctl(p); + internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1; + if (max_targ > 8) { - host_conf = inb(HOSTCONF + base); - config->bus_speed = host_conf & DFTHRSH; - config->busrtime = (host_conf << 2) & BOFF; + internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1; } /* - * Setup the FIFO threshold and the bus off time + * Set the rom bank to 1 and determine + * the other signals. */ - outb(config->bus_speed & DFTHRSH, BUSSPD + base); - outb(config->busrtime, BUSTIME + base); + write_brdctl(p, BRDDAT5); /* - * A reminder until this can be detected automatically. + * Now read the state of the external connectors. BRDDAT6 is + * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is + * set when the eprom is present. */ - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); - break; - - case AIC_284x: - outb(CHIPRST, HCNTRL + base); - config->unpause = UNPAUSE_284X; - config->pause = REQ_PAUSE; /* DWG would like to be like the rest */ - aic7xxx_delay(1); - outb(config->pause, HCNTRL + base); - - config->parity = AIC_ENABLED; - config->irq = inb(INTDEF + base) & 0x0F; - host_conf = inb(HOSTCONF + base); - - printk(KERN_INFO "aic7xxx: Reading SEEPROM..."); - have_seeprom = read_2840_seeprom(base, &sc); - if (!have_seeprom) - { - printk("aic7xxx: Unable to read SEEPROM.\n"); - } - else + brdctl_ext = read_brdctl(p); + external_present = (brdctl_ext & BRDDAT6) ? 0 : 1; + eprom_present = brdctl_ext & BRDDAT7; + if (aic7xxx_verbose) { - printk("done.\n"); - config->flags |= HAVE_SEEPROM; - if (sc.bios_control & CF284XEXTEND) - config->flags |= EXTENDED_TRANSLATION; - if (!(sc.bios_control & CFBIOSEN)) + if (max_targ > 8) { - /* - * The BIOS is disabled; the values left over in scratch - * RAM are still valid. Do not use defaults as in the - * AIC-7770 case. - */ - config->bios = AIC_DISABLED; + printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, " + "Ext-68 %s)\n", + internal50_present ? "YES" : "NO", + internal68_present ? "YES" : "NO", + external_present ? "YES" : "NO"); } else { - config->parity = (sc.adapter_control & CFSPARITY) ? - AIC_ENABLED : AIC_DISABLED; - config->low_term = (sc.adapter_control & CF284XSTERM) ? - AIC_ENABLED : AIC_DISABLED; - /* - * XXX - Adaptec *does* make 284x wide controllers, but the - * documents do not say where the high byte termination - * enable bit is located. - */ + printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n", + internal50_present ? "YES" : "NO", + external_present ? "YES" : "NO"); } + printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, " + "brdctl_ext=0x%x\n", + eprom_present ? "is" : "not", brdctl_int, brdctl_ext); } - host_conf = inb(HOSTCONF + base); - config->bus_speed = host_conf & DFTHRSH; - config->busrtime = (host_conf << 2) & BOFF; - - /* - * Setup the FIFO threshold and the bus off time - */ - outb(config->bus_speed & DFTHRSH, BUSSPD + base); - outb(config->busrtime, BUSTIME + base); - - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); - break; - - case AIC_7860: - case AIC_7861: - case AIC_7880: - case AIC_7881: - case AIC_7882: - case AIC_7883: - case AIC_7884: - /* - * Remember if Ultra was enabled in case there is no SEEPROM. - * Fall through to the rest of the AIC_78xx code. - */ - if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra) - config->flags |= ULTRA_ENABLED; - - case AIC_7850: - case AIC_7855: - case AIC_7870: - case AIC_7871: - case AIC_7872: - case AIC_7873: - case AIC_7874: /* - * Grab the SCSI ID before chip reset in case there is no SEEPROM. + * Now set the termination based on what we found. BRDDAT6 + * controls wide termination enable. */ - config->scsi_id = inb(SCSIID + base) & OID; - outb(CHIPRST, HCNTRL + base); - config->unpause = UNPAUSE_294X; - config->pause = config->unpause | PAUSE; - aic7xxx_delay(1); - outb(config->pause, HCNTRL + base); - - config->parity = AIC_ENABLED; + high_on = FALSE; + low_on = FALSE; + if ((max_targ > 8) && + ((external_present == 0) || (internal68_present == 0))) + { + high_on = TRUE; + } - printk(KERN_INFO "aic7xxx: Reading SEEPROM..."); - if ((config->type == AIC_7873) || (config->type == AIC_7883)) + if ((internal50_present + internal68_present + external_present) <= 1) { - have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), - &sc, c56_66); + low_on = TRUE; } - else + + if (internal50_present && internal68_present && external_present) { - have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), - &sc, c46); + printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n" + " Only two connectors on the adapter may be " + "used at a time!\n"); } - if (!have_seeprom) + + if (high_on == TRUE) + write_brdctl(p, BRDDAT6); + else + write_brdctl(p, 0); + + if (low_on == TRUE) + *sxfrctl1 |= STPWEN; + + if (aic7xxx_verbose) { - for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++) - { - if (inb(sram) != 0x00) - break; - } - if (sram == base + TARG_SCRATCH) - { - for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++) - { - if (inb(sram) != 0xFF) - break; - } - } - if ((sram != base + 0x60) && (config->scsi_id != 0)) + if (max_targ > 8) { - config->flags &= ~USE_DEFAULTS; - printk("\naic7xxx: Unable to read SEEPROM; " - "using leftover BIOS values.\n"); + printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", + low_on ? "ON" : "OFF", + high_on ? "ON" : "OFF"); } else { - printk("\n"); - printk(KERN_INFO "aic7xxx: Unable to read SEEPROM; using default " - "settings.\n"); - config->flags |= USE_DEFAULTS; - config->flags &= ~ULTRA_ENABLED; - config->scsi_id = 7; + printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF"); } - scsi_conf = ENSPCHK | RESET_SCSI; + } + aic7xxx_verbose = old_verbose; + } + else + { + if (adapter_control & CFSTERM) + { + *sxfrctl1 |= STPWEN; + } + outb(SEEMS | SEECS, p->base + SEECTL); + /* + * Configure high byte termination. + */ + if (adapter_control & CFWSTERM) + { + write_brdctl(p, BRDDAT6); } else { - printk("done.\n"); - config->flags |= HAVE_SEEPROM; - if (!(sc.bios_control & CFBIOSEN)) - { - /* - * The BIOS is disabled; the values left over in scratch - * RAM are still valid. Do not use defaults as in the - * AIC-7770 case. - */ - config->bios = AIC_DISABLED; - scsi_conf = ENSPCHK | RESET_SCSI; - } - else - { - scsi_conf = 0; - if (sc.adapter_control & CFRESETB) - scsi_conf |= RESET_SCSI; - if (sc.adapter_control & CFSPARITY) - scsi_conf |= ENSPCHK; - if (sc.bios_control & CFEXTEND) - config->flags |= EXTENDED_TRANSLATION; - config->scsi_id = (sc.brtime_id & CFSCSIID); - config->parity = (sc.adapter_control & CFSPARITY) ? - AIC_ENABLED : AIC_DISABLED; - config->low_term = (sc.adapter_control & CFSTERM) ? - AIC_ENABLED : AIC_DISABLED; - config->high_term = (sc.adapter_control & CFWSTERM) ? - AIC_ENABLED : AIC_DISABLED; - config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8); - if (((config->type == AIC_7880) || (config->type == AIC_7881) || - (config->type == AIC_7882) || (config->type == AIC_7883) || - (config->type == AIC_7884)) && (sc.adapter_control & CFULTRAEN)) - { - printk(KERN_INFO "aic7xxx: Enabling support for Ultra SCSI " - "speed.\n"); - config->flags |= ULTRA_ENABLED; - } - } + write_brdctl(p, 0); + } + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", + (adapter_control & CFSTERM) ? "ON" : "OFF", + (adapter_control & CFWSTERM) ? "ON" : "OFF"); } + } + release_seeprom(p); + } +} + +/*+F************************************************************************* + * Function: + * detect_maxscb + * + * Description: + * Detects the maximum number of SCBs for the controller and returns + * the count and a mask in p (p->maxscbs, p->qcntmask). + *-F*************************************************************************/ +static void +detect_maxscb(struct aic7xxx_host *p) +{ + int i; + unsigned char max_scbid = 255; - outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base); - config->bus_speed = DFTHRSH_100; - outb(config->bus_speed, DSPCISTATUS + base); + /* + * It's possible that we've already done this for multichannel + * adapters. + */ + if (p->scb_data->maxhscbs == 0) + { + /* + * We haven't initialized the SCB settings yet. Walk the SCBs to + * determince how many there are. + */ + outb(0, p->base + FREE_SCBH); - /* - * In case we are a wide card... - */ - outb(config->scsi_id, SCSICONF + base + 1); + for (i = 0; i < AIC7XXX_MAXSCB; i++) + { + outb(i, p->base + SCBPTR); + outb(i, p->base + SCB_CONTROL); + if (inb(p->base + SCB_CONTROL) != i) + break; + outb(0, p->base + SCBPTR); + if (inb(p->base + SCB_CONTROL) != 0) + break; - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); - break; + outb(i, p->base + SCBPTR); + outb(0, p->base + SCB_CONTROL); /* Clear the control byte. */ + outb(i + 1, p->base + SCB_NEXT); /* Set the next pointer. */ + outb(SCB_LIST_NULL, p->base + SCB_TAG); /* Make the tag invalid. */ - default: - panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n"); + /* Make the non-tagged targets not busy. */ + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS); + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1); + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2); + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3); + } + + /* Make sure the last SCB terminates the free list. */ + outb(i - 1, p->base + SCBPTR); + outb(SCB_LIST_NULL, p->base + SCB_NEXT); + + /* Ensure we clear the first (0) SCBs control byte. */ + outb(0, p->base + SCBPTR); + outb(0, p->base + SCB_CONTROL); + + p->scb_data->maxhscbs = i; } - detect_maxscb(config); + if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB)) + { + /* Determine the number of valid bits in the FIFOs. */ + outb(max_scbid, p->base + QINFIFO); + max_scbid = inb(p->base + QINFIFO); + p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1); + } + else + { + p->scb_data->maxscbs = p->scb_data->maxhscbs; + } + if (p->scb_data->maxscbs == p->scb_data->maxhscbs) + { + /* + * Disable paging if the QINFIFO doesn't allow more SCBs than + * we have in hardware. + */ + p->flags &= ~PAGE_ENABLED; + } - if (config->chip_type == AIC_777x) + /* + * Set the Queue Full Count. Some cards have more queue space than + * SCBs. + */ + switch (p->chip_class) { - if (config->pause & IRQMS) - { - printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n"); - } - else - { - printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n"); - } + case AIC_777x: + p->qfullcount = 4; + p->qcntmask = 0x07; + break; + case AIC_785x: + case AIC_786x: + p->qfullcount = 8; + p->qcntmask = 0x0f; + break; + case AIC_787x: + case AIC_788x: + if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB) + { + p->qfullcount = AIC7XXX_MAXSCB; + p->qcntmask = 0xFF; + } + else + { + p->qfullcount = 16; + p->qcntmask = 0x1F; + } + break; } +} + +/*+F************************************************************************* + * Function: + * aic7xxx_register + * + * Description: + * Register a Adaptec aic7xxx chip SCSI controller with the kernel. + *-F*************************************************************************/ +static int +aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p) +{ + int i; + unsigned char sblkctl, flags = 0; + int max_targets; + int found = 1; + char channel_ids[] = {'A', 'B', 'C'}; + unsigned char target_settings; + unsigned char scsi_conf, sxfrctl1; + unsigned short ultraenable = 0; + struct Scsi_Host *host; + + /* + * Lock out other contenders for our i/o space. + */ + request_region(p->base, MAXREG - MINREG, "aic7xxx"); /* * Read the bus type from the SBLKCTL register. Set the FLAGS * register in the sequencer for twin and wide bus cards. */ - sblkctl = inb(SBLKCTL + base); - if (config->flags & PAGE_ENABLED) + sblkctl = inb(p->base + SBLKCTL); + if (p->flags & PAGE_ENABLED) flags = PAGESCBS; switch (sblkctl & SELBUS_MASK) { case SELNARROW: /* narrow/normal bus */ - config->scsi_id = inb(SCSICONF + base) & 0x07; - config->bus_type = AIC_SINGLE; - outb(flags | SINGLE_BUS, FLAGS + base); + p->scsi_id = inb(p->base + SCSICONF) & 0x07; + p->bus_type = AIC_SINGLE; + p->flags &= ~FLAGS_CHANNEL_B_PRIMARY; + if (p->flags & MULTI_CHANNEL) + { + printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ", + channel_ids[p->chan_num], p->scsi_id); + } + else + { + printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ", + p->scsi_id); + } + outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS); break; case SELWIDE: /* Wide bus */ - config->scsi_id = inb(SCSICONF + base + 1) & 0x0F; - config->bus_type = AIC_WIDE; - printk("aic7xxx: Enabling wide channel of %s-Wide.\n", - board_names[config->type]); - outb(flags | WIDE_BUS, FLAGS + base); - break; - - case SELBUSB: /* Twin bus */ - config->scsi_id = inb(SCSICONF + base) & 0x07; -#ifdef AIC7XXX_TWIN_SUPPORT - config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07; - config->bus_type = AIC_TWIN; - printk(KERN_INFO "aic7xxx: Enabled channel B of %s-Twin.\n", - board_names[config->type]); - outb(flags | TWIN_BUS, FLAGS + base); -#else - config->bus_type = AIC_SINGLE; - printk(KERN_INFO "aic7xxx: Channel B of %s-Twin will be ignored.\n", - board_names[config->type]); - outb(flags, FLAGS + base); -#endif + p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID; + p->bus_type = AIC_WIDE; + p->flags &= ~FLAGS_CHANNEL_B_PRIMARY; + if (p->flags & MULTI_CHANNEL) + { + printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ", + channel_ids[p->chan_num], p->scsi_id); + } + else + { + printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ", + p->scsi_id); + } + outb(flags | WIDE_BUS, p->base + SEQ_FLAGS); break; - default: - printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please " - "mail deang@teleport.com\n", inb(SBLKCTL + base)); - outb(0, FLAGS + base); - return (0); - } - - /* - * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will - * take the card out of diagnostic mode and make the host adapter - * LED follow bus activity (will not always be on). - */ - outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base); - - /* - * The IRQ level in i/o port 4 maps directly onto the real - * IRQ number. If it's ok, register it with the kernel. - * - * NB. the Adaptec documentation says the IRQ number is only - * in the lower four bits; the ECU information shows the - * high bit being used as well. Which is correct? - * - * The PCI cards get their interrupt from PCI BIOS. - */ - if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15))) - { - printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ level, " - "ignoring.\n"); - return (0); + case SELBUSB: /* Twin bus */ + p->scsi_id = inb(p->base + SCSICONF) & HSCSIID; + p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID; + p->bus_type = AIC_TWIN; + printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ", + p->scsi_id, p->scsi_id_b); + outb(flags | TWIN_BUS, p->base + SEQ_FLAGS); + break; + + default: + printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please " + "mail deang@teleport.com\n", inb(p->base + SBLKCTL)); + outb(0, p->base + SEQ_FLAGS); + return (0); } /* - * Print out debugging information before re-enabling - * the card - a lot of registers on it can't be read - * when the sequencer is active. - */ - debug_config(config); - - /* - * Register each "host" and fill in the returned Scsi_Host - * structure as best we can. Some of the parameters aren't - * really relevant for bus types beyond ISA, and none of the - * high-level SCSI code looks at it anyway. Why are the fields - * there? Also save the pointer so that we can find the - * information when an IRQ is triggered. + * Detect SCB parameters and initialize the SCB array. */ - host = scsi_register(template, sizeof(struct aic7xxx_host)); - host->can_queue = config->maxscbs; + detect_maxscb(p); + printk("%d/%d SCBs, QFull %d, QMask 0x%x\n", + p->scb_data->maxhscbs, p->scb_data->maxscbs, + p->qfullcount, p->qcntmask); + + host = p->host; + + host->can_queue = p->scb_data->maxscbs; host->cmd_per_lun = 2; + host->sg_tablesize = AIC7XXX_MAX_SG; host->select_queue_depths = aic7xxx_select_queue_depth; - host->this_id = config->scsi_id; - host->io_port = config->base; + host->this_id = p->scsi_id; + host->io_port = p->base; host->n_io_port = 0xFF; - host->base = (unsigned char *)config->mbase; - host->irq = config->irq; - if (config->bus_type == AIC_WIDE) + host->base = (unsigned char *) p->mbase; + host->irq = p->irq; + if (p->bus_type == AIC_WIDE) { host->max_id = 16; } - if (config->bus_type == AIC_TWIN) + if (p->bus_type == AIC_TWIN) { host->max_channel = 1; } - p = (struct aic7xxx_host *) host->hostdata; - p->host = host; - p->host_no = (int)host->host_no; + p->host_no = host->host_no; p->isr_count = 0; - p->base = base; - p->maxscbs = config->maxscbs; - p->maxhscbs = config->maxhscbs; - p->qcntmask = config->qcntmask; - p->mbase = (char *)config->mbase; - p->type = config->type; - p->chip_type = config->chip_type; - p->flags = config->flags; - p->chan_num = config->chan_num; - p->scb_link = &(p->scb_usage); -#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS) - if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883))) - { - shared_3985_scbs = &(p->scb_usage); - p->scb_link = &(p->scb_usage); - } -#endif - p->scb_link->numscbs = 0; - p->bus_type = config->bus_type; - p->seeprom = sc; p->next = NULL; p->completeq.head = NULL; p->completeq.tail = NULL; - scbq_init(&p->scb_link->free_scbs); - scbq_init(&p->page_scbs); + scbq_init(&p->scb_data->free_scbs); scbq_init(&p->waiting_scbs); - scbq_init(&p->assigned_scbs); - p->unpause = config->unpause; - p->pause = config->pause; - - for (i = 0; i <= 15; i++) + for (i = 0; i <= NUMBER(p->device_status); i++) { p->device_status[i].commands_sent = 0; p->device_status[i].flags = 0; + p->device_status[i].active_cmds = 0; p->device_status[i].last_reset = 0; } - if (aic7xxx_boards[config->irq] == NULL) + if (aic7xxx_boards[p->irq] == NULL) { + int result; + int irq_flags = 0; + +#ifdef AIC7XXX_OLD_ISR_TYPE + irg_flags = SA_INTERRUPT; +#endif /* * Warning! This must be done before requesting the irq. It is * possible for some boards to raise an interrupt as soon as @@ -4353,17 +5030,26 @@ * kernel, an interrupt is triggered immediately. Therefore, we * must ensure the board data is correctly set before the request. */ - aic7xxx_boards[config->irq] = host; + aic7xxx_boards[p->irq] = host; /* - * Register IRQ with the kernel. + * Register IRQ with the kernel. Only allow sharing IRQs with + * PCI devices. */ - if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ, - "aic7xxx", NULL)) + if (p->chip_class == AIC_777x) + { + result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL)); + } + else + { + result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ, + "aic7xxx", NULL)); + } + if (result < 0) { printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n", - config->irq); - aic7xxx_boards[config->irq] = NULL; + p->irq); + aic7xxx_boards[p->irq] = NULL; return (0); } } @@ -4374,79 +5060,74 @@ * registered host adapter. Add this host adapter's Scsi_Host * to the beginning of the linked list of hosts at the same IRQ. */ - p->next = aic7xxx_boards[config->irq]; - aic7xxx_boards[config->irq] = host; - } - - /* - * Load the sequencer program, then re-enable the board - - * resetting the AIC-7770 disables it, leaving the lights - * on with nobody home. On the PCI bus you *may* be home, - * but then your mailing address is dynamically assigned - * so no one can find you anyway :-) - */ - printk(KERN_INFO "aic7xxx: Downloading sequencer code..."); - aic7xxx_loadseq(base); - - /* - * Set Fast Mode and Enable the board - */ - outb(FASTMODE, SEQCTL + base); - - if (p->chip_type == AIC_777x) - { - outb(ENABLE, BCTL + base); + p->next = aic7xxx_boards[p->irq]; + aic7xxx_boards[p->irq] = host; } - printk("done.\n"); - /* * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels */ if (p->bus_type == AIC_TWIN) { /* - * Select Channel B. + * The controller is gated to channel B after a chip reset; set + * bus B values first. */ - outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base); - - outb(config->scsi_id_b, SCSIID + base); - scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL); - outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base); -#if EXPERIMENTAL_FLAGS - outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base); -#else - outb(ENSELTIMO, SIMODE1 + base); -#endif + outb(p->scsi_id_b, p->base + SCSIID); + scsi_conf = inb(p->base + SCSICONF + 1); + sxfrctl1 = inb(p->base + SXFRCTL1); + outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | + ENSTIMER | ACTNEGEN, p->base + SXFRCTL1); + outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1); if (p->flags & ULTRA_ENABLED) { - outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base); + outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0); } else { - outb(DFON | SPIOEN, SXFRCTL0 + base); + outb(DFON | SPIOEN, p->base + SXFRCTL0); } - /* - * Select Channel A - */ - outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base); + if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0)) + { + /* Reset SCSI bus B. */ + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Resetting channel B\n"); + + aic7xxx_reset_current_bus(p); + } + + /* Select channel A */ + outb(SELNARROW, p->base + SBLKCTL); } - outb(config->scsi_id, SCSIID + base); - scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL); - outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base); -#if EXPERIMENTAL_FLAGS - outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base); -#else - outb(ENSELTIMO, SIMODE1 + base); -#endif + + outb(p->scsi_id, p->base + SCSIID); + scsi_conf = inb(p->base + SCSICONF); + sxfrctl1 = inb(p->base + SXFRCTL1); + outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | + ENSTIMER | ACTNEGEN, p->base + SXFRCTL1); + outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1); if (p->flags & ULTRA_ENABLED) { - outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base); + outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0); } else { - outb(DFON | SPIOEN, SXFRCTL0 + base); + outb(DFON | SPIOEN, p->base + SXFRCTL0); + } + + if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0)) + { + /* Reset SCSI bus A. */ + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Resetting channel A\n"); + + aic7xxx_reset_current_bus(p); + + /* + * Delay for the reset delay. + */ + aic7xxx_delay(AIC7XXX_RESET_DELAY); } /* @@ -4473,67 +5154,47 @@ /* * Grab the disconnection disable table and invert it for our needs */ - if (have_seeprom) + if (p->flags & USE_DEFAULTS) { - p->discenable = 0x0; + printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI " + "device parameters.\n"); + p->discenable = 0xFFFF; } else { - if (config->bios == AIC_DISABLED) - { - printk(KERN_INFO "aic7xxx : Host adapter BIOS disabled. Using default SCSI " - "device parameters.\n"); - p->discenable = 0xFFFF; - } - else - { - p->discenable = ~((inb(DISC_DSB + base + 1) << 8) | - inb(DISC_DSB + base)); - } + p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) | + inb(p->base + DISC_DSB)); } for (i = 0; i < max_targets; i++) { - if (config->flags & USE_DEFAULTS) + if (p->flags & USE_DEFAULTS) { - target_settings = 0; /* 10 MHz */ + target_settings = 0; /* 10 or 20 MHz depending on Ultra enable */ p->needsdtr_copy |= (0x01 << i); p->needwdtr_copy |= (0x01 << i); + if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x)) + ultraenable |= (0x01 << i); } else { - if (have_seeprom) + target_settings = inb(p->base + TARG_SCRATCH + i); + if (target_settings & 0x0F) { - target_settings = ((sc.device_flags[i] & CFXFER) << 4); - if (sc.device_flags[i] & CFSYNCH) - { - p->needsdtr_copy |= (0x01 << i); - } - if (sc.device_flags[i] & CFWIDEB) - { - p->needwdtr_copy |= (0x01 << i); - } - if (sc.device_flags[i] & CFDISC) - { - p->discenable |= (0x01 << i); - } + p->needsdtr_copy |= (0x01 << i); + /* + * Default to asynchronous transfers (0 offset) + */ + target_settings &= 0xF0; } - else + if (target_settings & 0x80) { - target_settings = inb(TARG_SCRATCH + base + i); - if (target_settings & 0x0F) - { - p->needsdtr_copy |= (0x01 << i); - /* - * Default to asynchronous transfers (0 offset) - */ - target_settings &= 0xF0; - } - if (target_settings & 0x80) - { - p->needwdtr_copy |= (0x01 << i); - target_settings &= 0x7F; - } + p->needwdtr_copy |= (0x01 << i); + /* + * Clear the wide flag. When wide negotiation is successful, + * we'll enable it. + */ + target_settings &= 0x7F; } if (p->flags & ULTRA_ENABLED) { @@ -4544,126 +5205,471 @@ case 0x20: ultraenable |= (0x01 << i); break; - case 0x40: + case 0x40: /* treat 10MHz as 10MHz without Ultra enabled */ target_settings &= ~(0x70); break; default: break; } } - } - outb(target_settings, (TARG_SCRATCH + base + i)); + } + outb(target_settings, p->base + TARG_SCRATCH + i); + } + + /* + * If we are not wide, forget WDTR. This makes the driver + * work on some cards that don't leave these fields cleared + * when BIOS is not installed. + */ + if (p->bus_type != AIC_WIDE) + { + p->needwdtr_copy = 0; + } + p->needsdtr = p->needsdtr_copy; + p->needwdtr = p->needwdtr_copy; + p->orderedtag = 0; + outb(ultraenable & 0xFF, p->base + ULTRA_ENB); + outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1); + + /* + * Set the number of available hardware SCBs. + */ + outb(p->scb_data->maxhscbs, p->base + SCBCOUNT); + + /* + * 2s compliment of maximum tag value. + */ + i = p->scb_data->maxscbs; + outb(-i & 0xFF, p->base + COMP_SCBCOUNT); + + /* + * Allocate enough hardware scbs to handle the maximum number of + * concurrent transactions we can have. We have to make sure that + * the allocated memory is contiguous memory. The Linux kmalloc + * routine should only allocate contiguous memory, but note that + * this could be a problem if kmalloc() is changed. + */ + if (p->scb_data->hscbs == NULL) + { + size_t array_size; + unsigned int hscb_physaddr; + + array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb); + p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC); + if (p->scb_data->hscbs == NULL) + { + printk("aic7xxx: Unable to allocate hardware SCB array; " + "failing detection.\n"); + release_region(p->base, MAXREG - MINREG); + /* + * Ensure that we only free the IRQ when there is _not_ another + * aic7xxx adapter sharing this IRQ. The adapters are always + * added to the beginning of the list, so we can grab the next + * pointer and place it back in the board array. + */ + if (p->next == NULL) + { + free_irq(p->irq, aic7xxx_isr); + } + aic7xxx_boards[p->irq] = p->next; + return(0); + } + /* At least the control byte of each SCB needs to be 0. */ + memset(p->scb_data->hscbs, 0, array_size); + + /* Tell the sequencer where it can find the hardware SCB array. */ + hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs); + outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR); + outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1); + outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2); + outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3); + } + + /* + * QCount mask to deal with broken aic7850s that sporadically get + * garbage in the upper bits of their QCNT registers. + */ + outb(p->qcntmask, p->base + QCNTMASK); + + /* + * We don't have any waiting selections or disconnected SCBs. + */ + outb(SCB_LIST_NULL, p->base + WAITING_SCBH); + outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH); + + /* + * Message out buffer starts empty + */ + outb(0, p->base + MSG_LEN); + + /* + * Load the sequencer program, then re-enable the board - + * resetting the AIC-7770 disables it, leaving the lights + * on with nobody home. On the PCI bus you *may* be home, + * but then your mailing address is dynamically assigned + * so no one can find you anyway :-) + */ + aic7xxx_loadseq(p); + + if (p->chip_class == AIC_777x) + { + outb(ENABLE, p->base + BCTL); /* Enable the boards BUS drivers. */ + } + + /* + * Unpause the sequencer before returning and enable + * interrupts - we shouldn't get any until the first + * command is sent to us by the high-level SCSI code. + */ + unpause_sequencer(p, /* unpause_always */ TRUE); + + return (found); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_chip_reset + * + * Description: + * Perform a chip reset on the aic7xxx SCSI controller. The controller + * is paused upon return. + *-F*************************************************************************/ +static void +aic7xxx_chip_reset(struct aic7xxx_host *p) +{ + unsigned char hcntrl; + int wait; + + /* Retain the IRQ type across the chip reset. */ + hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN; + + /* + * For some 274x boards, we must clear the CHIPRST bit and pause + * the sequencer. For some reason, this makes the driver work. + */ + outb(PAUSE | CHIPRST, p->base + HCNTRL); + + /* + * In the future, we may call this function as a last resort for + * error handling. Let's be nice and not do any unecessary delays. + */ + wait = 1000; /* 1 second (1000 * 1000 usec) */ + while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)) + { + udelay(1000); /* 1 msec = 1000 usec */ + wait = wait - 1; + } + + if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0) + { + printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n"); + } + + outb(hcntrl | PAUSE, p->base + HCNTRL); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_alloc + * + * Description: + * Allocate and initialize a host structure. Returns NULL upon error + * and a pointer to a aic7xxx_host struct upon success. + *-F*************************************************************************/ +static struct aic7xxx_host * +aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase, + aha_chip_type chip_type, int flags, scb_data_type *scb_data) +{ + struct aic7xxx_host *p = NULL; + struct Scsi_Host *host; + + /* + * Allocate a storage area by registering us with the mid-level + * SCSI layer. + */ + host = scsi_register(sht, sizeof(struct aic7xxx_host)); + + if (host != NULL) + { + p = (struct aic7xxx_host *) host->hostdata; + memset(p, 0, sizeof(struct aic7xxx_host)); + p->host = host; + + if (scb_data != NULL) + { + /* + * We are sharing SCB data areas; use the SCB data pointer + * provided. + */ + p->scb_data = scb_data; + p->flags |= SHARED_SCBDATA; + } + else + { + /* + * We are not sharing SCB data; allocate one. + */ + p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC); + if (p->scb_data != NULL) + { + memset(p->scb_data, 0, sizeof(scb_data_type)); + scbq_init (&p->scb_data->free_scbs); + } + else + { + /* + * For some reason we don't have enough memory. Free the + * allocated memory for the aic7xxx_host struct, and return NULL. + */ + scsi_unregister(host); + p = NULL; + } + } + if (p != NULL) + { + p->host_no = host->host_no; + p->base = base; + p->mbase = mbase; + p->maddr = NULL; + p->flags = flags; + p->chip_type = chip_type; + p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN; + p->pause = p->unpause | PAUSE; + } + } + return (p); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_free + * + * Description: + * Frees and releases all resources associated with an instance of + * the driver (struct aic7xxx_host *). + *-F*************************************************************************/ +static void +aic7xxx_free (struct aic7xxx_host *p) +{ + int i; + + /* + * We should be careful in freeing the scb_data area. For those + * adapters sharing external SCB RAM(398x), there will be only one + * scb_data area allocated. The flag SHARED_SCBDATA indicates if + * one adapter is sharing anothers SCB RAM. + */ + if (!(p->flags & SHARED_SCBDATA)) + { + /* + * Free the allocated hardware SCB space. + */ + if (p->scb_data->hscbs != NULL) + { + kfree(p->scb_data->hscbs); + } + /* + * Free the driver SCBs. These were allocated on an as-need + * basis. + */ + for (i = 0; i < p->scb_data->numscbs; i++) + { + kfree(p->scb_data->scb_array[i]); + } + /* + * Free the hardware SCBs. + */ + if (p->scb_data->hscbs != NULL) + { + kfree(p->scb_data->hscbs); + } + + /* + * Free the SCB data area. + */ + kfree(p->scb_data); + } + /* + * Free the instance of the device structure. + */ + scsi_unregister(p->host); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_load_seeprom + * + * Description: + * Load the seeprom and configure adapter and target settings. + * Returns 1 if the load was successful and 0 otherwise. + *-F*************************************************************************/ +static int +load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1) +{ + int have_seeprom = 0; + int i, max_targets; + unsigned char target_settings, scsi_conf; + unsigned short scarray[128]; + struct seeprom_config *sc = (struct seeprom_config *) scarray; + + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Loading serial EEPROM..."); + } + switch (p->chip_type) + { + case AIC_7770: /* None of these adapters have seeproms. */ + case AIC_7771: + case AIC_7850: + case AIC_7855: + break; + + case AIC_284x: + have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray); + break; + + case AIC_7861: + case AIC_7870: + case AIC_7871: + case AIC_7872: + case AIC_7874: + case AIC_7881: + case AIC_7882: + case AIC_7884: + have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2), + scarray, sizeof(*sc)/2, C46); + break; + + case AIC_7860: /* Motherboard Ultra controllers might have RAID port. */ + case AIC_7880: + have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46); + if (!have_seeprom) + { + have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66); + } + break; + + case AIC_7873: /* The 3985 adapters use the 93c56 serial EEPROM. */ + case AIC_7883: + have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2), + scarray, sizeof(scarray)/2, C56_66); + break; + + default: + break; } - /* - * If we are not wide, forget WDTR. This makes the driver - * work on some cards that don't leave these fields cleared - * when BIOS is not installed. - */ - if (p->bus_type != AIC_WIDE) + if (!have_seeprom) { - p->needwdtr_copy = 0; + if (aic7xxx_verbose) + { + printk("\naic7xxx: No SEEPROM available; using defaults.\n"); + } + p->flags |= USE_DEFAULTS; } - p->needsdtr = p->needsdtr_copy; - p->needwdtr = p->needwdtr_copy; - p->orderedtag = 0; -#if 0 - printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr); - printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr); -#endif - outb(ultraenable & 0xFF, ULTRA_ENB + base); - outb((ultraenable >> 8) & 0xFF, ULTRA_ENB + base + 1); + else + { + if (aic7xxx_verbose) + { + printk("done\n"); + } + p->flags |= HAVE_SEEPROM; - /* - * Set the number of available SCBs. - */ - outb(config->maxhscbs, SCBCOUNT + base); + /* + * Update the settings in sxfrctl1 to match the termination settings. + */ + *sxfrctl1 = 0; - /* - * 2s compliment of maximum tag value. - */ - i = p->maxscbs; - outb(-i & 0xFF, COMP_SCBCOUNT + base); + /* + * First process the settings that are different between the VLB + * and PCI adapter seeproms. + */ + if (p->chip_class == AIC_777x) + { + /* VLB adapter seeproms */ + if (sc->bios_control & CF284XEXTEND) + p->flags |= EXTENDED_TRANSLATION; - /* - * Set the QCNT (queue count) mask to deal with broken aic7850s that - * sporatically get garbage in the upper bits of their QCNT registers. - */ - outb(config->qcntmask, QCNTMASK + base); + if (sc->adapter_control & CF284XSTERM) + *sxfrctl1 |= STPWEN; + /* + * The 284x SEEPROM doesn't have a max targets field. We + * set it to 16 to make sure we take care of the 284x-wide + * adapters. For narrow adapters, going through the extra + * 8 target entries will not cause any harm since they will + * will not be used. + * + * XXX - We should probably break out the bus detection + * from the register function so we can use it here + * to tell us how many targets there really are. + */ + max_targets = 16; + } + else + { + /* PCI adapter seeproms */ + if (sc->bios_control & CFEXTEND) + p->flags |= EXTENDED_TRANSLATION; - /* - * Clear the active flags - no targets are busy. - */ - outb(0, ACTIVE_A + base); - outb(0, ACTIVE_B + base); + if (sc->adapter_control & CFSTERM) + *sxfrctl1 |= STPWEN; - /* - * We don't have any waiting selections or disconnected SCBs. - */ - outb(SCB_LIST_NULL, WAITING_SCBH + base); - outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base); + /* Limit to 16 targets just in case. */ + max_targets = MIN(sc->max_targets & CFMAXTARG, 16); + } - /* - * Message out buffer starts empty - */ - outb(0, MSG_LEN + base); + for (i = 0; i < max_targets; i++) + { + target_settings = (sc->device_flags[i] & CFXFER) << 4; + if (sc->device_flags[i] & CFSYNCH) + target_settings |= SOFS; + if (sc->device_flags[i] & CFWIDEB) + target_settings |= WIDEXFER; + if (sc->device_flags[i] & CFDISC) + p->discenable |= (0x01 << i); + outb(target_settings, p->base + TARG_SCRATCH + i); + } + outb(~(p->discenable & 0xFF), p->base + DISC_DSB); + outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1); - /* - * Reset the SCSI bus. Is this necessary? - * There may be problems for a warm boot without resetting - * the SCSI bus. Either BIOS settings in scratch RAM - * will not get reinitialized, or devices may stay at - * previous negotiated settings (SDTR and WDTR) while - * the driver will think that no negotiations have been - * performed. - * - * Some devices need a long time to "settle" after a SCSI - * bus reset. - */ - if (!aic7xxx_no_reset) - { - printk("aic7xxx: Resetting the SCSI bus..."); - if (p->bus_type == AIC_TWIN) + p->scsi_id = sc->brtime_id & CFSCSIID; + + scsi_conf = (p->scsi_id & 0x7); + if (sc->adapter_control & CFSPARITY) + scsi_conf |= ENSPCHK; + if (sc->adapter_control & CFRESETB) + scsi_conf |= RESET_SCSI; + + if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x)) { /* - * Select Channel B. + * We allow the operator to override ultra enable through + * the boot prompt. */ - outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base); + if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0)) + { + /* Treat us as a non-ultra card */ + p->flags &= ~ULTRA_ENABLED; + } + } - outb(SCSIRSTO, SCSISEQ + base); - udelay(1000); - outb(0, SCSISEQ + base); - - /* Ensure we don't get a RSTI interrupt from this. */ - outb(CLRSCSIRSTI, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); + /* Set the host ID */ + outb(scsi_conf, p->base + SCSICONF); + /* In case we are a wide card */ + outb(p->scsi_id, p->base + SCSICONF + 1); - /* - * Select Channel A. + if (p->chip_class != AIC_777x) + { + /* + * Update the settings in sxfrctl1 to match the termination + * settings. */ - outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base); + *sxfrctl1 = 0; + configure_termination(p, sxfrctl1, sc->adapter_control, + (unsigned char) sc->max_targets & CFMAXTARG); } - - outb(SCSIRSTO, SCSISEQ + base); - udelay(1000); - outb(0, SCSISEQ + base); - - /* Ensure we don't get a RSTI interrupt from this. */ - outb(CLRSCSIRSTI, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); - - aic7xxx_delay(AIC7XXX_RESET_DELAY); - - printk("done.\n"); } - - /* - * Unpause the sequencer before returning and enable - * interrupts - we shouldn't get any until the first - * command is sent to us by the high-level SCSI code. - */ - UNPAUSE_SEQUENCER(p); - return (found); + return (have_seeprom); } /*+F************************************************************************* @@ -4672,17 +5678,24 @@ * * Description: * Try to detect and register an Adaptec 7770 or 7870 SCSI controller. + * + * XXX - This should really be called aic7xxx_probe(). A sequence of + * probe(), attach()/detach(), and init() makes more sense than + * one do-it-all function. This may be useful when (and if) the + * mid-level SCSI code is overhauled. *-F*************************************************************************/ int aic7xxx_detect(Scsi_Host_Template *template) { - int found = 0, slot, base; - unsigned char irq = 0; + int found = 0; + aha_status_type adapter_bios; + aha_chip_class_type chip_class; + aha_chip_type chip_type; + int slot, base; + int chan_num = 0; + unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0; int i; - struct aic7xxx_host_config config; - - template->proc_dir = &proc_scsi_aic7xxx; - config.chan_num = 0; + struct aic7xxx_host *p; /* * Since we may allow sharing of IRQs, it is imperative @@ -4696,6 +5709,10 @@ aic7xxx_boards[i] = NULL; } + template->proc_dir = &proc_scsi_aic7xxx; + template->name = aic7xxx_info(NULL); + template->sg_tablesize = AIC7XXX_MAX_SG; + /* * Initialize the spurious count to 0. */ @@ -4717,33 +5734,174 @@ continue; } - config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios)); - if (config.type != AIC_NONE) + chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios)); + if (chip_type != AIC_NONE) { + + switch (chip_type) + { + case AIC_7770: + case AIC_7771: + printk("aic7xxx: <%s> at EISA %d\n", + board_names[chip_type], slot); + break; + case AIC_284x: + printk("aic7xxx: <%s> at VLB %d\n", + board_names[chip_type], slot); + break; + default: + break; + } + /* * We found a card, allow 1 spurious interrupt. */ aic7xxx_spurious_count = 1; /* - * We "find" a AIC-7770 if we locate the card - * signature and we can set it up and register - * it with the kernel without incident. + * Pause the card preserving the IRQ type. Allow the operator + * to override the IRQ trigger. */ - config.chip_type = AIC_777x; - config.base = base; - config.mbase = 0; - config.irq = irq; - config.parity = AIC_ENABLED; - config.low_term = AIC_UNKNOWN; - config.high_term = AIC_UNKNOWN; - config.flags = 0; - if (aic7xxx_extended) - config.flags |= EXTENDED_TRANSLATION; - config.bus_speed = DFTHRSH_100; - config.busrtime = BOFF; - found += aic7xxx_register(template, &config); + if (aic7xxx_irq_trigger == 1) + hcntrl = IRQMS; /* Level */ + else if (aic7xxx_irq_trigger == 0) + hcntrl = 0; /* Edge */ + else + hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */ + outb(hcntrl | PAUSE, base + HCNTRL); + + irq = inb(INTDEF + base) & 0x0F; + switch (irq) + { + case 9: + case 10: + case 11: + case 12: + case 14: + case 15: + break; + + default: + printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ " + "level, ignoring.\n"); + irq = 0; + break; + } + + if (irq != 0) + { + p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL); + if (p == NULL) + { + printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n"); + continue; + } + p->irq = irq & 0x0F; + p->chip_class = AIC_777x; +#ifdef AIC7XXX_PAGE_ENABLE + p->flags |= PAGE_ENABLED; +#endif + p->instance = found; + if (aic7xxx_extended) + { + p->flags |= EXTENDED_TRANSLATION; + } + aic7xxx_chip_reset(p); + + switch (p->chip_type) + { + case AIC_7770: + case AIC_7771: + { + unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL); + + /* + * Get the primary channel information. Right now we don't + * do anything with this, but someday we will be able to inform + * the mid-level SCSI code which channel is primary. + */ + if (biosctrl & CHANNEL_B_PRIMARY) + { + p->flags |= FLAGS_CHANNEL_B_PRIMARY; + } + + if ((biosctrl & BIOSMODE) == BIOSDISABLED) + { + p->flags |= USE_DEFAULTS; + } + break; + } + case AIC_284x: + if (!load_seeprom(p, &sxfrctl1)) + { + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: SEEPROM not available.\n"); + } + break; + + default: /* Won't get here. */ + break; + } + printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ", + (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq, + (p->pause & IRQMS) ? "level sensitive" : "edge triggered"); + /* + * Check for Rev C or E boards. Rev E boards can supposedly have + * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs. + * It's still not clear extactly what is different about the Rev E + * boards, but we think it allows 8 bit entries in the QOUTFIFO to + * support "paging" SCBs (more than 4 commands can be active at once). + * + * The Rev E boards have a read/write autoflush bit in the + * SBLKCTL register, while in the Rev C boards it is read only. + */ + sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS; + outb(sblkctl, p->base + SBLKCTL); + if (inb(p->base + SBLKCTL) == sblkctl) + { + /* + * We detected a Rev E board, we allow paging on this board. + */ + printk("Revision >= E\n"); + outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL); + } + else + { + /* Do not allow paging. */ + p->flags &= ~PAGE_ENABLED; + printk("Revision <= C\n"); + } + + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", + (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); + + /* + * Set the FIFO threshold and the bus off time. + */ + hostconf = inb(p->base + HOSTCONF); + outb(hostconf & DFTHRSH, p->base + BUSSPD); + outb((hostconf << 2) & BOFF, p->base + BUSTIME); + + /* + * Try to initialize the card and register it with the kernel. + */ + if (aic7xxx_register(template, p)) + { + /* + * We successfully found a board and registered it. + */ + found = found + 1; + } + else + { + /* + * Something went wrong; release and free all resources. + */ + aic7xxx_free(p); + } + } /* * Disallow spurious interrupts. */ @@ -4759,15 +5917,15 @@ { struct { - unsigned short vendor_id; - unsigned short device_id; - aha_type card_type; - aha_chip_type chip_type; + unsigned short vendor_id; + unsigned short device_id; + aha_chip_type chip_type; + aha_chip_class_type chip_class; } const aic7xxx_pci_devices[] = { {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x}, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_785x}, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x}, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x}, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x}, @@ -4780,14 +5938,14 @@ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x} }; - int error; + int error, flags; int done = 0; unsigned int iobase, mbase; unsigned short index = 0; unsigned char pci_bus, pci_device_fn; - unsigned int csize_lattime; - unsigned int class_revid; - unsigned int devconfig; + unsigned char ultra_enb = 0; + unsigned int devconfig, class_revid; + scb_data_type *shared_scb_data = NULL; char rev_id[] = {'B', 'C', 'D'}; for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++) @@ -4804,36 +5962,33 @@ } else /* Found an Adaptec PCI device. */ { - config.type = aic7xxx_pci_devices[i].card_type; - config.chip_type = aic7xxx_pci_devices[i].chip_type; - config.chan_num = 0; - config.bios = AIC_ENABLED; /* Assume bios is enabled. */ - config.flags = 0; - config.busrtime = 40; - switch (config.type) + chip_class = aic7xxx_pci_devices[i].chip_class; + chip_type = aic7xxx_pci_devices[i].chip_type; + chan_num = 0; + flags = 0; + switch (aic7xxx_pci_devices[i].chip_type) { case AIC_7850: case AIC_7855: - case AIC_7860: - case AIC_7861: - config.bios = AIC_DISABLED; - config.flags |= USE_DEFAULTS; - config.bus_speed = DFTHRSH_100; + flags |= USE_DEFAULTS; break; case AIC_7872: /* 3940 */ case AIC_7882: /* 3940-Ultra */ - config.chan_num = number_of_3940s & 0x1; /* Has 2 controllers */ + flags |= MULTI_CHANNEL; + chan_num = number_of_3940s & 0x1; /* Has 2 controllers */ number_of_3940s++; break; case AIC_7873: /* 3985 */ case AIC_7883: /* 3985-Ultra */ - config.chan_num = number_of_3985s; /* Has 3 controllers */ + chan_num = number_of_3985s; /* Has 3 controllers */ + flags |= MULTI_CHANNEL; number_of_3985s++; if (number_of_3985s == 3) { number_of_3985s = 0; + shared_scb_data = NULL; } break; @@ -4850,39 +6005,165 @@ PCI_INTERRUPT_LINE, &irq); error += pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &mbase); + error += pcibios_read_config_dword(pci_bus, pci_device_fn, + DEVCONFIG, &devconfig); + error += pcibios_read_config_dword(pci_bus, pci_device_fn, + CLASS_PROGIF_REVID, &class_revid); + + printk("aic7xxx: <%s> at PCI %d\n", + board_names[chip_type], PCI_SLOT(pci_device_fn)); /* - * The first bit of PCI_BASE_ADDRESS_0 is always set, so + * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so * we mask it off. */ iobase &= PCI_BASE_ADDRESS_IO_MASK; + p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags, + shared_scb_data); + + if (p == NULL) + { + printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n"); + continue; + } + + /* Remember to set the channel number, irq, and chip class. */ + p->chan_num = chan_num; + p->irq = irq; + p->chip_class = chip_class; +#ifdef AIC7XXX_PAGE_ENABLE + p->flags |= PAGE_ENABLED; +#endif + p->instance = found; + /* - * Read the PCI burst size and latency timer. + * Remember how the card was setup in case there is no seeprom. */ - error += pcibios_read_config_dword(pci_bus, pci_device_fn, - CSIZE_LATTIME, &csize_lattime); - printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d " - "PCLKS\n", (int) (csize_lattime & CACHESIZE), - (csize_lattime >> 8) & 0x000000ff); + p->scsi_id = inb(p->base + SCSIID) & OID; + if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x)) + { + p->flags |= ULTRA_ENABLED; + ultra_enb = inb(p->base + SXFRCTL1) & FAST20; + } + sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN; - error += pcibios_read_config_dword(pci_bus, pci_device_fn, - CLASS_PROGIF_REVID, &class_revid); - if ((class_revid & DEVREVID) < 3) + aic7xxx_chip_reset(p); + +#ifdef AIC7XXX_USE_EXT_SCBRAM + if (devconfig & RAMPSM) + { + printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM " + "access.\n"); + /* + * XXX - Assume 9 bit SRAM and enable parity checking. + */ + devconfig |= EXTSCBPEN; + + /* + * XXX - Assume fast SRAM and only enable 2 cycle access if we + * are sharing the SRAM across multiple adapters (398x). + */ + if ((devconfig & MPORTMODE) == 0) + { + devconfig |= EXTSCBTIME; + } + devconfig &= ~SCBRAMSEL; + pcibios_write_config_dword(pci_bus, pci_device_fn, + DEVCONFIG, devconfig); + } +#endif + + if ((p->flags & USE_DEFAULTS) == 0) { - printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type], - rev_id[class_revid & DEVREVID]); + load_seeprom(p, &sxfrctl1); } - error += pcibios_read_config_dword(pci_bus, pci_device_fn, - DEVCONFIG, &devconfig); - if (error) + /* + * Take the LED out of diagnostic mode + */ + sblkctl = inb(p->base + SBLKCTL); + outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL); + + /* + * We don't know where this is set in the SEEPROM or by the + * BIOS, so we default to 100%. + */ + outb(DFTHRSH_100, p->base + DSPCISTATUS); + + if (p->flags & USE_DEFAULTS) { - panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n", - error); + int j; + /* + * Default setup; should only be used if the adapter does + * not have a SEEPROM. + */ + /* + * Check the target scratch area to see if someone set us + * up already. We are previously set up if the scratch + * area contains something other than all zeroes and ones. + */ + for (j = TARG_SCRATCH; j < 0x60; j++) + { + if (inb(p->base + j) != 0x00) /* Check for all zeroes. */ + break; + } + if (j == TARG_SCRATCH) + { + for (j = TARG_SCRATCH; j < 0x60; j++) + { + if (inb(p->base + 1) != 0xFF) /* Check for all ones. */ + break; + } + } + if ((j != 0x60) && (p->scsi_id != 0)) + { + p->flags &= ~USE_DEFAULTS; + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n"); + } + } + else + { + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: No BIOS found; using default " + "settings.\n"); + } + /* + * Assume only one connector and always turn on + * termination. + */ + sxfrctl1 = STPWEN; + p->scsi_id = 7; + } + outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI, + p->base + SCSICONF); + /* In case we are a wide card. */ + outb(p->scsi_id, p->base + SCSICONF + 1); + if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0)) + { + /* + * If there wasn't a BIOS or the board wasn't in this mode + * to begin with, turn off Ultra. + */ + p->flags &= ~ULTRA_ENABLED; + } } - printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig); + /* + * Print some additional information about the adapter. + */ + printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, " + "IO Mem 0x%x, IRQ %d", + (p->flags & USE_DEFAULTS) ? "dis" : "en", + p->base, p->mbase, p->irq); + if ((class_revid & DEVREVID) < 3) + { + printk(", Revision %c", rev_id[class_revid & DEVREVID]); + } + printk("\n"); /* * I don't think we need to bother with allowing @@ -4891,58 +6172,57 @@ */ aic7xxx_spurious_count = 1; - config.base = iobase; - config.mbase = mbase; - config.irq = irq; - config.parity = AIC_ENABLED; - config.low_term = AIC_UNKNOWN; - config.high_term = AIC_UNKNOWN; if (aic7xxx_extended) - config.flags |= EXTENDED_TRANSLATION; -#ifdef AIC7XXX_SHARE_SCBs - if (devconfig & RAMPSM) -#else - if ((devconfig & RAMPSM) && (config.type != AIC_7873) && - (config.type != AIC_7883)) -#endif + p->flags |= EXTENDED_TRANSLATION; + + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", + (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); + + /* + * Put our termination setting into sxfrctl1 now that the + * generic initialization is complete. + */ + sxfrctl1 |= inb(p->base + SXFRCTL1); + outb(sxfrctl1, p->base + SXFRCTL1); + + if (aic7xxx_register(template, p) == 0) + { + aic7xxx_free(p); + } + else { + found = found + 1; + +#ifdef AIC7XXX_USE_EXT_SCBRAM /* - * External SRAM present. The probe will walk the SCBs to see - * how much SRAM we have and set the number of SCBs accordingly. - * We have to turn off SCBRAMSEL to access the external SCB - * SRAM. - * - * It seems that early versions of the aic7870 didn't use these - * bits, hence the hack for the 3940 above. I would guess that - * recent 3940s using later aic7870 or aic7880 chips do actually - * set RAMPSM. + * Set the shared SCB data once we've successfully probed a + * 398x adapter. * - * The documentation isn't clear, but it sounds like the value - * written to devconfig must not have RAMPSM set. The second - * sixteen bits of the register are R/O anyway, so it shouldn't - * affect RAMPSM either way. + * Note that we can only do this if the use of external + * SCB RAM is enabled. */ - printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM " - "access.\n"); - devconfig &= ~(RAMPSM | SCBRAMSEL); - pcibios_write_config_dword(pci_bus, pci_device_fn, - DEVCONFIG, devconfig); + if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883)) + { + if (shared_scb_data == NULL) + { + shared_scb_data = p->scb_data; + } + } +#endif } - found += aic7xxx_register(template, &config); + index++; /* * Disable spurious interrupts. */ aic7xxx_spurious_count = 0; - - index++; } /* Found an Adaptec PCI device. */ } } } #endif CONFIG_PCI - template->name = aic7xxx_info(NULL); return (found); } @@ -4958,45 +6238,45 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, struct aic7xxx_scb *scb) { - unsigned int addr; /* must be 32 bits */ unsigned short mask; + struct aic7xxx_hwscb *hscb; mask = (0x01 << TARGET_INDEX(cmd)); + hscb = scb->hscb; + /* * Setup the control byte if we need negotiation and have not * already requested it. */ -#ifdef AIC7XXX_TAGGED_QUEUEING - if (cmd->device->tagged_queue) + if (p->discenable & mask) { - cmd->tag = scb->tag; - cmd->device->current_tag = scb->tag; - scb->control |= TAG_ENB; - p->device_status[TARGET_INDEX(cmd)].commands_sent++; - if (p->device_status[TARGET_INDEX(cmd)].commands_sent == 200) - { - scb->control |= 0x02; - p->device_status[TARGET_INDEX(cmd)].commands_sent = 0; - } -#if 0 - if (p->orderedtag & mask) + hscb->control |= DISCENB; +#ifdef AIC7XXX_TAGGED_QUEUEING + if (cmd->device->tagged_queue) { - scb->control |= 0x02; - p->orderedtag = p->orderedtag & ~mask; + cmd->tag = hscb->tag; + p->device_status[TARGET_INDEX(cmd)].commands_sent++; + if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75) + { + hscb->control |= MSG_SIMPLE_Q_TAG; + } + else + { + hscb->control |= MSG_ORDERED_Q_TAG; + p->device_status[TARGET_INDEX(cmd)].commands_sent = 0; + } } -#endif - } -#endif - if (p->discenable & mask) - { - scb->control |= DISCENB; +#endif /* Tagged queueing */ } + if ((p->needwdtr & mask) && !(p->wdtr_pending & mask)) { p->wdtr_pending |= mask; - scb->control |= NEEDWDTR; + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_WDTR; #if 0 - printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target); + printk("scsi%d: Sending WDTR request to target %d.\n", + p->host_no, cmd->target); #endif } else @@ -5004,19 +6284,20 @@ if ((p->needsdtr & mask) && !(p->sdtr_pending & mask)) { p->sdtr_pending |= mask; - scb->control |= NEEDSDTR; + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_SDTR; #if 0 - printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target); + printk("scsi%d: Sending SDTR request to target %d.\n", + p->host_no, cmd->target); #endif } } - #if 0 printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) " "mask(0x%x).\n", cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask); #endif - scb->target_channel_lun = ((cmd->target << 4) & 0xF0) | + hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) | ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07); /* @@ -5030,9 +6311,8 @@ * XXX - this relies on the host data being stored in a * little-endian format. */ - addr = VIRT_TO_BUS(cmd->cmnd); - scb->SCSI_cmd_length = cmd->cmd_len; - memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer)); + hscb->SCSI_cmd_length = cmd->cmd_len; + hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd); if (cmd->use_sg) { @@ -5052,15 +6332,16 @@ scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address); scb->sg_list[i].length = (unsigned int) sg[i].length; } - scb->SG_segment_count = cmd->use_sg; - addr = VIRT_TO_BUS(scb->sg_list); - memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); - memcpy(scb->data_pointer, &(scb->sg_list[0].address), - sizeof(scb->data_pointer)); - scb->data_count = scb->sg_list[0].length; + hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list); + hscb->SG_segment_count = cmd->use_sg; + scb->sg_count = hscb->SG_segment_count; + + /* Copy the first SG into the data pointer area. */ + hscb->data_pointer = scb->sg_list[0].address; + hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24); #if 0 printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n", - cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count); + cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count); #endif } else @@ -5069,28 +6350,23 @@ printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n", (unsigned long) cmd->request_buffer, cmd->request_bufflen); #endif - if (cmd->request_bufflen == 0) + if (cmd->request_bufflen) { - /* - * In case the higher level SCSI code ever tries to send a zero - * length command, ensure the SCB indicates no data. The driver - * will interpret a zero length command as a Bus Device Reset. - */ - scb->SG_segment_count = 0; - memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer)); - memset(scb->data_pointer, 0, sizeof(scb->data_pointer)); - scb->data_count = 0; + hscb->SG_segment_count = 1; + scb->sg_count = 1; + scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer); + scb->sg_list[0].length = cmd->request_bufflen; + hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]); + hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24); + hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer); } else { - scb->SG_segment_count = 1; - scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer); - scb->sg_list[0].length = cmd->request_bufflen; - addr = VIRT_TO_BUS(&scb->sg_list[0]); - memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); - scb->data_count = scb->sg_list[0].length; - addr = VIRT_TO_BUS(cmd->request_buffer); - memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer)); + hscb->SG_segment_count = 0; + scb->sg_count = 0; + hscb->SG_list_pointer = 0; + hscb->data_pointer = 0; + hscb->data_count = SCB_LIST_NULL << 24; } } } @@ -5108,7 +6384,6 @@ long processor_flags; struct aic7xxx_host *p; struct aic7xxx_scb *scb; - u_char curscb, intstat; p = (struct aic7xxx_host *) cmd->host->hostdata; if (p->host != cmd->host) @@ -5140,34 +6415,21 @@ cmd->lun & 0x07); #endif - /* - * This is a critical section, since we don't want the interrupt - * routine mucking with the host data or the card. For this reason - * it is nice to know that this function can only be called in one - * of two ways from scsi.c First, as part of a routine queue command, - * in which case, the irq for our card is disabled before this - * function is called. This doesn't help us if there is more than - * one card using more than one IRQ in our system, therefore, we - * should disable all interrupts on these grounds alone. Second, - * this can be called as part of the scsi_done routine, in which case - * we are in the aic7xxx_isr routine already and interrupts are - * disabled, therefore we should saveflags first, then disable the - * interrupts, do our work, then restore the CPU flags. If it weren't - * for the possibility of more than one card using more than one IRQ - * in our system, we wouldn't have to touch the interrupt flags at all. - */ - save_flags(processor_flags); - cli(); - + if (p->device_status[TARGET_INDEX(cmd)].active_cmds + > cmd->device->queue_depth) + { + printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n", + p->host_no, cmd->target, cmd->channel); + } scb = aic7xxx_allocate_scb(p); if (scb == NULL) { - panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n"); + panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n"); } else { scb->cmd = cmd; - aic7xxx_position(cmd) = scb->tag; + aic7xxx_position(cmd) = scb->hscb->tag; #if 0 debug_scb(scb); #endif; @@ -5179,14 +6441,14 @@ aic7xxx_buildscb(p, cmd, scb); #if 0 - if (scb != (p->scb_array[scb->position])) + if (scb != (p->scb_data->scb_array[scb->hscb->tag])) { printk("aic7xxx: (queue) Address of SCB by position does not match SCB " "address.\n"); } printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n", - scb->position, (unsigned int) scb->cmd, - scb->state, (unsigned int) p->free_scb); + scb->hscb->tag, (unsigned int) scb->cmd, + scb->flags, (unsigned int) p->free_scb); #endif /* @@ -5201,70 +6463,28 @@ cmd->host_scribble = NULL; memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); - if (scb->position != SCB_LIST_NULL) - { - /* We've got a valid slot, yeah! */ - if (p->flags & IN_ISR) - { - scbq_insert_tail(&p->assigned_scbs, scb); - scb->state |= SCB_ASSIGNEDQ; - } - else - { - /* - * Pause the sequencer so we can play with its registers - - * wait for it to acknowledge the pause. - * - * XXX - should the interrupts be left on while doing this? - */ - PAUSE_SEQUENCER(p); - intstat = inb(INTSTAT + p->base); - - /* - * Save the SCB pointer and put our own pointer in - this - * selects one of the four banks of SCB registers. Load - * the SCB, then write its pointer into the queue in FIFO - * and restore the saved SCB pointer. - */ - curscb = inb(SCBPTR + p->base); - outb(scb->position, SCBPTR + p->base); - aic7xxx_putscb(p, scb); - outb(curscb, SCBPTR + p->base); - outb(scb->position, QINFIFO + p->base); - scb->state |= SCB_ACTIVE; + scb->flags |= SCB_ACTIVE | SCB_WAITINGQ; - /* - * Guard against unpausing the sequencer if there is an interrupt - * waiting to happen. - */ - if (!(intstat & (BRKADRINT | SEQINT | SCSIINT))) - { - UNPAUSE_SEQUENCER(p); - } - } - } - else + save_flags(processor_flags); + cli(); + scbq_insert_tail(&p->waiting_scbs, scb); + if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0) { - scb->state |= SCB_WAITINGQ; - scbq_insert_tail(&p->waiting_scbs, scb); - if (!(p->flags & IN_ISR)) - { - aic7xxx_run_waiting_queues(p); - } + aic7xxx_run_waiting_queues(p); } + restore_flags(processor_flags); #if 0 printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n", - (long) cmd, (long) scb->cmd, scb->position); + (long) cmd, (long) scb->cmd, scb->hscb->tag); #endif; - restore_flags(processor_flags); } return (0); } /*+F************************************************************************* * Function: - * aic7xxx_abort_reset + * aic7xxx_bus_device_reset * * Description: * Abort or reset the current SCSI command(s). If the scb has not @@ -5276,204 +6496,257 @@ static int aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { - struct aic7xxx_scb *scb; + struct aic7xxx_scb *scb; + struct aic7xxx_hwscb *hscb; unsigned char bus_state; - int base, result = -1; + int result = -1; char channel; - scb = (p->scb_array[aic7xxx_position(cmd)]); - base = p->base; + scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); + hscb = scb->hscb; + + /* + * Ensure that the card doesn't do anything behind our back. + * Also make sure that we didn't just miss an interrupt that + * could affect this abort/reset. + */ + pause_sequencer(p); + while (inb(p->base + INTSTAT) & INT_PEND); + { + aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL); + pause_sequencer(p); + } + if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0)) + { + result = SCSI_RESET_NOT_RUNNING; + unpause_sequencer(p, /* unpause_always */ TRUE); + return(result); + } + + + printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ", + p->host_no, TC_OF_SCB(scb), scb->flags); + bus_state = inb(p->base + LASTPHASE); - channel = scb->target_channel_lun & SELBUSB ? 'B': 'A'; - if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS)) + switch (bus_state) { + case P_DATAOUT: + printk("Data-Out phase, "); + break; + case P_DATAIN: + printk("Data-In phase, "); + break; + case P_COMMAND: + printk("Command phase, "); + break; + case P_MESGOUT: + printk("Message-Out phase, "); + break; + case P_STATUS: + printk("Status phase, "); + break; + case P_MESGIN: + printk("Message-In phase, "); + break; + default: + /* + * We're not in a valid phase, so assume we're idle. + */ + printk("while idle, LASTPHASE = 0x%x, ", bus_state); + break; + } + printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n", + inb(p->base + SCSISIGI), + inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8), + inb(p->base + SSTAT0), inb(p->base + SSTAT1)); - if (scb->state & SCB_IN_PROGRESS) + channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A'; + /* + * Determine our course of action. + */ + if (scb->flags & SCB_ABORT) + { + /* + * Been down this road before; do a full bus reset. + */ + scb->flags |= SCB_RECOVERY_SCB; + unpause_sequencer(p, /* unpause_always */ TRUE); + result = -1; + } +#if 0 + else if (hscb->control & TAG_ENB) { /* - * Ensure that the card doesn't do anything - * behind our back. + * We could be starving this command; try sending and ordered tag + * command to the target we come from. */ - PAUSE_SEQUENCER(p); + scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB; + p->orderedtag = p->orderedtag | 0xFF; + result = SCSI_RESET_PENDING; + unpause_sequencer(p, /* unpause_always */ TRUE); + printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n", + p->host_no); + } +#endif + else + { + unsigned char active_scb_index, saved_scbptr; + struct aic7xxx_scb *active_scb; - printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state); - bus_state = inb(LASTPHASE + p->base); + /* + * Send an Abort Message: + * The target that is holding up the bus may not be the same as + * the one that triggered this timeout (different commands have + * different timeout lengths). Our strategy here is to queue an + * abort message to the timed out target if it is disconnected. + * Otherwise, if we have an active target we stuff the message buffer + * with an abort message and assert ATN in the hopes that the target + * will let go of the bus and go to the mesgout phase. If this + * fails, we'll get another timeout a few seconds later which will + * attempt a bus reset. + */ + saved_scbptr = inb(p->base + SCBPTR); + active_scb_index = inb(p->base + SCB_TAG); + active_scb = p->scb_data->scb_array[active_scb_index]; - switch (bus_state) + if (bus_state != P_BUSFREE) + { + if (active_scb_index >= p->scb_data->numscbs) { - case P_DATAOUT: - printk("Data-Out phase, "); - break; - case P_DATAIN: - printk("Data-In phase, "); - break; - case P_COMMAND: - printk("Command phase, "); - break; - case P_MESGOUT: - printk("Message-Out phase, "); - break; - case P_STATUS: - printk("Status phase, "); - break; - case P_MESGIN: - printk("Message-In phase, "); - break; - default: - printk("while idle, LASTPHASE = 0x%x, ", bus_state); - /* - * We're not in a valid phase, so assume we're idle. - */ - bus_state = 0; - break; + /* + * Perform a bus reset. + * + * XXX - We want to queue an abort for the timedout SCB + * instead. + */ + result = -1; + printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, " + "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags); } - printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI)); - - /* - * First, determine if we want to do a bus reset or simply a bus device - * reset. If this is the first time that a transaction has timed out - * and the SCB is not paged out, just schedule a bus device reset. - * Otherwise, we reset the bus and abort all pending I/Os on that bus. - */ - if (!(scb->state & (SCB_ABORTED | SCB_PAGED_OUT))) + else { -#if 0 - if (scb->control & TAG_ENB) - { + /* Send the abort message to the active SCB. */ + outb(1, p->base + MSG_LEN); + if (active_scb->hscb->control & TAG_ENB) + { + outb(MSG_ABORT_TAG, p->base + MSG_OUT); + } + else + { + outb(MSG_ABORT, p->base + MSG_OUT); + } + outb(bus_state | ATNO, p->base + SCSISIGO); + printk(KERN_WARNING "scsi%d: abort message in message buffer\n", + p->host_no); + active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB; + if (active_scb != scb) + { /* - * We could be starving this command; try sending and ordered tag - * command to the target we come from. + * XXX - We would like to increment the timeout on scb, but + * access to that routine is denied because it is hidden + * in scsi.c. If we were able to do this, it would give + * scb a new lease on life. */ - scb->state = scb->state | SCB_ABORTED | SCB_SENTORDEREDTAG; - p->orderedtag = p->orderedtag | 0xFF; result = SCSI_RESET_PENDING; - UNPAUSE_SEQUENCER(p); - printk(KERN_WARNING "aic7xxx: (abort_reset) Ordered tag queued.\n"); - } -#endif - unsigned char active_scb, control; - struct aic7xxx_scb *active_scbp; + aic7xxx_error(active_scb->cmd) = DID_RESET; + } + else + { + aic7xxx_error(scb->cmd) = DID_RESET; + result = SCSI_RESET_PENDING; + } + unpause_sequencer(p, /* unpause_always */ TRUE); + } + } + else + { + unsigned char hscb_index, linked_next; + int disconnected; - /* - * Send a Bus Device Reset Message: - * The target we select to send the message to may be entirely - * different than the target pointed to by the scb that timed - * out. If the command is in the QINFIFO or the waiting for - * selection list, its not tying up the bus and isn't responsible - * for the delay so we pick off the active command which should - * be the SCB selected by SCBPTR. If its disconnected or active, - * we device reset the target scbp points to. Although it may - * be that this target is not responsible for the delay, it may - * may also be that we're timing out on a command that just takes - * too much time, so we try the bus device reset there first. - */ - active_scb = inb(SCBPTR + base); - active_scbp = (p->scb_array[inb(SCB_TAG + base)]); - control = inb(SCB_CONTROL + base); + disconnected = FALSE; + hscb_index = aic7xxx_find_scb(p, scb); + if (hscb_index == SCB_LIST_NULL) + { + disconnected = TRUE; + linked_next = (scb->hscb->data_count >> 24) & 0xFF; + } + else + { + outb(hscb_index, p->base + SCBPTR); + if (inb(p->base + SCB_CONTROL) & DISCONNECTED) + { + disconnected = TRUE; + } + linked_next = inb(p->base + SCB_LINKED_NEXT); + } + if (disconnected) + { + /* + * Simply set the ABORT_SCB control bit and preserve the + * linked next pointer. + */ + scb->hscb->control |= ABORT_SCB | MK_MESSAGE; + scb->hscb->data_count &= ~0xFF000000; + scb->hscb->data_count |= linked_next << 24; + if ((p->flags & PAGE_ENABLED) == 0) + { + scb->hscb->control &= ~DISCONNECTED; + } + scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB; + if (hscb_index != SCB_LIST_NULL) + { + unsigned char scb_control; - /* - * Test to see if scbp is disconnected - */ - outb(scb->position, SCBPTR + base); - if (inb(SCB_CONTROL + base) & DISCONNECTED) - { -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (abort_scb) scb %d is disconnected; " - "bus device reset message queued.\n", scb->position); -#endif - if (p->flags & PAGE_ENABLED) - { - /* Pull this SCB out of the disconnected list. */ - u_char prev = inb(SCB_PREV + base); - u_char next = inb(SCB_NEXT + base); - if (prev == SCB_LIST_NULL) - { - /* Head of list */ - outb(next, DISCONNECTED_SCBH + base); - } - else - { - outb(prev, SCBPTR + base); - outb(next, SCB_NEXT + base); - if (next != SCB_LIST_NULL) - { - outb(next, SCBPTR + base); - outb(prev, SCB_PREV + base); - } - outb(scb->position, SCBPTR + base); - } - } - scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED); - scb->control = scb->control & DISCENB; - scb->SCSI_cmd_length = 0; - scb->SG_segment_count = 0; - memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer)); - memset(scb->data_pointer, 0, sizeof(scb->data_pointer)); - scb->data_count = 0; - aic7xxx_putscb(p, scb); - aic7xxx_add_waiting_scb(base, scb); - outb(active_scb, SCBPTR + base); - result = SCSI_RESET_PENDING; - UNPAUSE_SEQUENCER(p); - } - else - { - /* - * Is the active SCB really active? - */ - if ((active_scbp->state & SCB_ACTIVE) && bus_state) - { - /* - * Load the message buffer and assert attention. - */ - active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED); - outb(1, MSG_LEN + base); - outb(MSG_BUS_DEVICE_RESET, MSG0 + base); - outb(bus_state | ATNO, SCSISIGO + base); -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (abort_scb) asserted ATN - " - "bus device reset in message buffer.\n"); -#endif - if (active_scbp != scb) - { - /* - * XXX - We would like to increment the timeout on scb, but - * access to that routine is denied because it is hidden - * in scsi.c. If we were able to do this, it would give - * scb a new lease on life. - */ - ; - } - aic7xxx_error(scb->cmd) = DID_RESET; - /* - * Restore the active SCB and unpause the sequencer. - */ - outb(active_scb, SCBPTR + base); - if (active_scbp != scb) - { - /* - * The mid-level SCSI code requested us to reset a command - * different from the one that we actually reset. Return - * a "not running" indication and hope that the SCSI code - * will Do the Right Thing (tm). - */ - result = SCSI_RESET_NOT_RUNNING; - } - else - { - result = SCSI_RESET_PENDING; - } - UNPAUSE_SEQUENCER(p); - } - } + scb_control = inb(p->base + SCB_CONTROL); + outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL); + } + /* + * Actually requeue this SCB in case we can select the + * device before it reconnects. If the transaction we + * want to abort is not tagged, unbusy it first so that + * we don't get held back from sending the command. + */ + if ((scb->hscb->control & TAG_ENB) == 0) + { + unsigned char target; + int lun; + + target = scb->cmd->target; + lun = scb->cmd->lun; + aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL, + 0, /* requeue */ TRUE); + } + printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n", + p->host_no, TC_OF_SCB(scb)); + scbq_insert_head(&p->waiting_scbs, scb); + scb->flags |= SCB_WAITINGQ; + outb(saved_scbptr, p->base + SCBPTR); + if ((p->flags & IN_ISR) == 0) + { + /* + * Processing the waiting queue may unpause us. + */ + aic7xxx_run_waiting_queues(p); + /* + * If we are using AAP, aic7xxx_run_waiting_queues() will not + * unpause us, so ensure we are unpaused. + */ + unpause_sequencer(p, /*unpause_always*/ FALSE); + } + else + { + unpause_sequencer(p, /*unpause_always*/ TRUE); + } + result = SCSI_RESET_PENDING; + } + else + { + scb->flags |= SCB_RECOVERY_SCB; + unpause_sequencer(p, /* unpause_always */ TRUE); + result = -1; } } } - /* Make sure the sequencer is unpaused upon return. */ - if (result == -1) - { - UNPAUSE_SEQUENCER(p); - } return (result); } @@ -5491,16 +6764,48 @@ struct aic7xxx_scb *scb = NULL; struct aic7xxx_host *p; int base, result; + unsigned long processor_flags; p = (struct aic7xxx_host *) cmd->host->hostdata; - scb = (p->scb_array[aic7xxx_position(cmd)]); + scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); base = p->base; + save_flags(processor_flags); + cli(); + #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n", - scb->position, TCL_OF_SCB(scb)); + if (scb != NULL) + { + printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags); + } + else + { + printk("aic7xxx: Abort called with no SCB for cmd.\n"); + } #endif + if (p->flags & IN_TIMEOUT) + { + /* + * We've already started a recovery operation. + */ + if ((scb->flags & SCB_RECOVERY_SCB) == 0) + { + restore_flags(processor_flags); + return (SCSI_ABORT_PENDING); + } + else + { + /* + * This is the second time we've tried to abort the recovery + * SCB. We want the mid-level SCSI code to call the reset + * function to reset the SCSI bus. + */ + restore_flags(processor_flags); + return (SCSI_ABORT_NOT_RUNNING); + } + } if (cmd->serial_number != cmd->serial_number_at_timeout) { result = SCSI_ABORT_NOT_RUNNING; @@ -5509,14 +6814,34 @@ { result = SCSI_ABORT_NOT_RUNNING; } - else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS))) + else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE))) { result = SCSI_ABORT_NOT_RUNNING; } else { - result = SCSI_ABORT_SNOOZE; + /* + * XXX - Check use of IN_TIMEOUT to see if we're Doing the + * Right Thing with it. + */ + p->flags |= IN_TIMEOUT; + result = aic7xxx_bus_device_reset(p, scb->cmd); + switch (result) + { + case SCSI_RESET_NOT_RUNNING: + p->flags &= ~IN_TIMEOUT; + result = SCSI_ABORT_NOT_RUNNING; + break; + case SCSI_RESET_PENDING: + result = SCSI_ABORT_PENDING; + break; + default: + p->flags &= ~IN_TIMEOUT; + result = SCSI_ABORT_SNOOZE; + break; + } } + restore_flags(processor_flags); return (result); } @@ -5536,18 +6861,27 @@ { struct aic7xxx_scb *scb = NULL; struct aic7xxx_host *p; - int base, found, tindex, min_target, max_target, result = -1; + int base, found, tindex, min_target, max_target; + int result = -1; char channel = 'A'; unsigned long processor_flags; p = (struct aic7xxx_host *) cmd->host->hostdata; - scb = (p->scb_array[aic7xxx_position(cmd)]); + scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); base = p->base; channel = cmd->channel ? 'B': 'A'; tindex = (cmd->channel << 4) | cmd->target; -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel); +#ifdef 0 /* AIC7XXX_DEBUG_ABORT */ + if (scb != NULL) + { + printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags); + } + else + { + printk("aic7xxx: Reset called with no SCB for cmd.\n"); + } #endif /* @@ -5562,34 +6896,45 @@ if (scb->cmd != cmd) scb = NULL; - if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) - && (scb != NULL)) + if (p->flags & IN_TIMEOUT) { /* - * Attempt a bus device reset if commands have completed successfully - * since the last bus device reset, or it has been less than 100ms - * since the last reset. + * We've already started a recovery operation. */ - if ((p->flags & DEVICE_SUCCESS) || - ((jiffies - p->device_status[tindex].last_reset) < HZ/10)) + if ((scb->flags & SCB_RECOVERY_SCB) == 0) { - if (cmd->serial_number != cmd->serial_number_at_timeout) - { - result = SCSI_RESET_NOT_RUNNING; - } - else + restore_flags(processor_flags); + return (SCSI_RESET_PENDING); + } + } + else + { + if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) + && (scb != NULL)) + { + /* + * Attempt a bus device reset if commands have completed successfully + * since the last bus device reset, or it has been less than 100ms + * since the last reset. + */ + if ((p->flags & DEVICE_SUCCESS) || + ((jiffies - p->device_status[tindex].last_reset) < HZ/10)) { - if (scb == NULL) + if (cmd->serial_number != cmd->serial_number_at_timeout) + { + result = SCSI_RESET_NOT_RUNNING; + } + else if (scb == NULL) { result = SCSI_RESET_NOT_RUNNING; } else if (flags & SCSI_RESET_ASYNCHRONOUS) { - if (scb->state & SCB_ABORTED) + if (scb->flags & SCB_ABORTED) { result = SCSI_RESET_PENDING; } - else if (!(scb->state & SCB_IN_PROGRESS)) + else if (!(scb->flags & SCB_ACTIVE)) { result = SCSI_RESET_NOT_RUNNING; } @@ -5600,20 +6945,23 @@ if ((flags & SCSI_RESET_SYNCHRONOUS) && (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)) { - scb->state |= SCB_ABORTED; + scb->flags |= SCB_ABORTED; result = SCSI_RESET_PENDING; } else { + p->flags |= IN_TIMEOUT; result = aic7xxx_bus_device_reset(p, cmd); if (result == 0) + { + p->flags &= ~IN_TIMEOUT; result = SCSI_RESET_PENDING; + } } - } + } } } } - if (result == -1) { /* @@ -5626,11 +6974,11 @@ { result = SCSI_RESET_NOT_RUNNING; } - else if (!(scb->state & SCB_IN_PROGRESS)) + else if (!(scb->flags & SCB_ACTIVE)) { result = SCSI_RESET_NOT_RUNNING; } - else if ((scb->state & SCB_ABORTED) && + else if ((scb->flags & SCB_ABORTED) && (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))) { result = SCSI_RESET_PENDING; @@ -5642,8 +6990,9 @@ /* * The reset channel function assumes that the sequencer is paused. */ - PAUSE_SEQUENCER(p); + pause_sequencer(p); found = aic7xxx_reset_channel(p, channel, TRUE); + p->flags = p->flags & ~IN_TIMEOUT; /* * If this is a synchronous reset and there is no SCB for this @@ -5689,8 +7038,10 @@ } result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET; + p->flags &= ~IN_TIMEOUT; } } + aic7xxx_run_waiting_queues(p); restore_flags(processor_flags); return (result); } diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h --- v2.1.47/linux/drivers/scsi/aic7xxx.h Sat Aug 10 00:44:18 1996 +++ linux/drivers/scsi/aic7xxx.h Thu Jul 31 12:37:17 1997 @@ -40,13 +40,13 @@ aic7xxx_info, \ NULL, \ aic7xxx_queue, \ - aic7xxx_abort, \ + NULL, \ aic7xxx_reset, \ NULL, \ aic7xxx_biosparam, \ -1, /* max simultaneous cmds */\ -1, /* scsi id of host adapter */\ - SG_ALL, /* max scatter-gather cmds */\ + 0, /* max scatter-gather cmds */\ 2, /* cmds per lun (linked cmds) */\ 0, /* number of 7xxx's present */\ 0, /* no memory DMA restrictions */\ @@ -57,7 +57,6 @@ extern int aic7xxx_biosparam(Disk *, kdev_t, int[]); extern int aic7xxx_detect(Scsi_Host_Template *); extern int aic7xxx_command(Scsi_Cmnd *); -extern int aic7xxx_abort(Scsi_Cmnd *); extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int); extern const char *aic7xxx_info(struct Scsi_Host *); diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq --- v2.1.47/linux/drivers/scsi/aic7xxx.seq Sun Oct 13 03:44:34 1996 +++ linux/drivers/scsi/aic7xxx.seq Wed Dec 31 16:00:00 1969 @@ -1,1127 +0,0 @@ -/*+M************************************************************************* - * Adaptec AIC7xxx device driver for Linux and FreeBSD. - * - * Copyright (c) 1994 John Aycock - * The University of Calgary Department of Computer Science. - * - *Modifications/enhancements: - * Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other - * optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org) - * - * This version corresponds to version 1.42 of FreeBSDs aic7xxx.seq. - * - *-M*************************************************************************/ - -VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 4.0 1996/10/13 08:23:42 deang Exp $" - -#ifdef linux -#include "aic7xxx_reg.h" -#else -#if defined(__NetBSD__) -#include "../../../../dev/ic/aic7xxxreg.h" -#elif defined(__FreeBSD__) -#include "../../dev/aic7xxx/aic7xxx_reg.h" -#endif -#endif - -/* - * We can't just use ACCUM in the sequencer code because it - * must be treated specially by the assembler, and it currently - * looks for the symbol 'A'. This is the only register defined in - * the assembler's symbol space. - */ -A = ACCUM - -/* After starting the selection hardware, we check for reconnecting targets - * as well as for our selection to complete just in case the reselection wins - * bus arbitration. The problem with this is that we must keep track of the - * SCB that we've already pulled from the QINFIFO and started the selection - * on just in case the reselection wins so that we can retry the selection at - * a later time. This problem cannot be resolved by holding a single entry - * in scratch ram since a reconnecting target can request sense and this will - * create yet another SCB waiting for selection. The solution used here is to - * use byte 27 of the SCB as a pseudo-next pointer and to thread a list - * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets, - * SCB_LIST_NULL is 0xff which is out of range. The kernel driver must - * add an entry to this list every time a request sense occurs. The sequencer - * will automatically consume the entries. - */ - -/* - * We assume that the kernel driver may reset us at any time, even in the - * middle of a DMA, so clear DFCNTRL too. - */ -reset: - clr DFCNTRL - clr SCSISIGO /* De-assert BSY */ -/* - * We jump to start after every bus free. - */ -start: - and FLAGS,0x0f /* clear target specific flags */ - mvi SCSISEQ,ENRSELI /* Always allow reselection */ - clr SCSIRATE /* - * We don't know the target we will - * connect to, so default to narrow - * transfers to avoid parity problems. - */ -poll_for_work: - /* - * Are we a twin channel device? - * For fairness, we check the other bus first, - * since we just finished a transaction on the - * current channel. - */ - test FLAGS,TWIN_BUS jz start2 - xor SBLKCTL,SELBUSB /* Toggle to the other bus */ - test SSTAT0,SELDI jnz reselect - xor SBLKCTL,SELBUSB /* Toggle to the original bus */ -start2: - test SSTAT0,SELDI jnz reselect - cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting - mov A, QCNTMASK - test QINCNT,A jz poll_for_work - -/* - * We have at least one queued SCB now and we don't have any - * SCBs in the list of SCBs awaiting selection. Set the SCB - * pointer from the FIFO so we see the right bank of SCB - * registers. - */ - mov SCBPTR,QINFIFO - -/* - * See if there is not already an active SCB for this target. This code - * locks out on a per target basis instead of target/lun. Although this - * is not ideal for devices that have multiple luns active at the same - * time, it is faster than looping through all SCB's looking for active - * commands. It may be beneficial to make findscb a more general procedure - * to see if the added cost of the search is negligible. This code also - * assumes that the kernel driver will clear the active flags on board - * initialization, board reset, and a target SELTO. Tagged commands - * don't set the active bits since you can queue more than one command - * at a time. We do, however, look to see if there are any non-tagged - * I/Os in progress, and requeue the command if there are. Tagged and - * non-tagged commands cannot be mixed to a single target. - */ - -test_busy: - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz test_a /* Id < 8 && A channel */ - - test ACTIVE_B,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_B,A - jmp start_scb - -/* Place the currently active SCB back on the queue for later processing */ -requeue: - mov QINFIFO, SCBPTR - jmp poll_for_work - -/* - * Pull the first entry off of the waiting for selection list - * We don't have to "test_busy" because only transactions that - * have passed that test can be in the waiting_scb list. - */ -start_waiting: - mov SCBPTR,WAITING_SCBH - jmp start_scb2 - -test_a: - test ACTIVE_A,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_A,A - -start_scb: - mov SCB_NEXT,WAITING_SCBH - mov WAITING_SCBH, SCBPTR -start_scb2: - and SINDEX,0xf7,SBLKCTL /* Clear the channel select bit */ - and A,0x08,SCB_TCL /* Get new channel bit */ - or SINDEX,A - mov SBLKCTL,SINDEX /* select channel */ - mov SCB_TCL call initialize_scsiid - -/* - * Enable selection phase as an initiator, and do automatic ATN - * after the selection. We do this now so that we can overlap the - * rest of our work to set up this target with the arbitration and - * selection bus phases. - */ -start_selection: - mvi SCSISEQ,0x58 /* ENSELO|ENAUTOATNO|ENRSELI */ - -/* - * As soon as we get a successful selection, the target should go - * into the message out phase since we have ATN asserted. Prepare - * the message to send. - * - * Messages are stored in scratch RAM starting with a length byte - * followed by the message itself. - */ - test SCB_CMDLEN,0xff jnz mk_identify /* 0 Length Command? */ - -/* - * The kernel has sent us an SCB with no command attached. This implies - * that the kernel wants to send a message of some sort to this target, - * so we interrupt the driver, allow it to fill the message buffer, and - * then go back into the arbitration loop - */ - mvi INTSTAT,AWAITING_MSG - jmp wait_for_selection - -mk_identify: - and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */ - - and MSG0,0x7,SCB_TCL /* lun */ - or MSG0,A /* or in disconnect privledge */ - or MSG0,MSG_IDENTIFY - mvi MSG_LEN, 1 - - test SCB_CONTROL,0xb0 jz !message /* WDTR, SDTR or TAG?? */ -/* - * Send a tag message if TAG_ENB is set in the SCB control block. - * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. - */ - -mk_tag: - mvi DINDEX, MSG1 - test SCB_CONTROL,TAG_ENB jz mk_tag_done - and DINDIR,0x23,SCB_CONTROL - mov DINDIR,SCB_TAG - - add MSG_LEN,COMP_MSG0,DINDEX /* update message length */ - -mk_tag_done: - - test SCB_CONTROL,0x90 jz !message /* NEEDWDTR|NEEDSDTR */ - mov DINDEX call mk_dtr /* build DTR message if needed */ - -!message: -wait_for_selection: - test SSTAT0,SELDO jnz select - test SSTAT0,SELDI jz wait_for_selection - -/* - * Reselection has been initiated by a target. Make a note that we've been - * reselected, but haven't seen an IDENTIFY message from the target - * yet. - */ -reselect: - clr MSG_LEN /* Don't have anything in the mesg buffer */ - mov SELID call initialize_scsiid - or FLAGS,RESELECTED - jmp select2 - -/* - * After the selection, remove this SCB from the "waiting for selection" - * list. This is achieved by simply moving our "next" pointer into - * WAITING_SCBH. Our next pointer will be set to null the next time this - * SCB is used, so don't bother with it now. - */ -select: - mov WAITING_SCBH,SCB_NEXT - or FLAGS,SELECTED -select2: -/* - * Set CLRCHN here before the target has entered a data transfer mode - - * with synchronous SCSI, if you do it later, you blow away some - * data in the SCSI FIFO that the target has already sent to you. - */ - or SXFRCTL0,CLRCHN -/* - * Initialize SCSIRATE with the appropriate value for this target. - */ - call ndx_dtr - mov SCSIRATE,SINDIR - -/* - * Initialize Ultra mode setting. - */ - mov FUNCTION1,SCSIID - mov A,FUNCTION1 - and SINDEX,0xdf,SXFRCTL0 /* default to Ultra disabled */ - test SCSIID, 0x80 jnz ultra_b /* Target ID > 7 */ - test SBLKCTL, SELBUSB jnz ultra_b /* Second channel device */ - test ULTRA_ENB,A jz set_sxfrctl0 - or SINDEX, ULTRAEN jmp set_sxfrctl0 -ultra_b: - test ULTRA_ENB_B,A jz set_sxfrctl0 - or SINDEX, ULTRAEN - -set_sxfrctl0: - mov SXFRCTL0,SINDEX - - mvi SCSISEQ,ENAUTOATNP /* - * ATN on parity errors - * for "in" phases - */ - mvi CLRSINT1,CLRBUSFREE - mvi CLRSINT0,0x60 /* CLRSELDI|CLRSELDO */ -/* - * Main loop for information transfer phases. If BSY is false, then - * we have a bus free condition, expected or not. Otherwise, wait - * for the target to assert REQ before checking MSG, C/D and I/O - * for the bus phase. - * - */ -ITloop: - test SSTAT1,BUSFREE jnz p_busfree - test SSTAT1,REQINIT jz ITloop - - and A,PHASE_MASK,SCSISIGI - mov LASTPHASE,A - mov SCSISIGO,A - - cmp ALLZEROS,A je p_dataout - cmp A,P_DATAIN je p_datain - cmp A,P_COMMAND je p_command - cmp A,P_MESGOUT je p_mesgout - cmp A,P_STATUS je p_status - cmp A,P_MESGIN je p_mesgin - - mvi INTSTAT,BAD_PHASE /* unknown phase - signal driver */ - jmp ITloop /* Try reading the bus again. */ - -p_dataout: - mvi DMAPARAMS,0x7d /* - * WIDEODD|SCSIEN|SDMAEN|HDMAEN| - * DIRECTION|FIFORESET - */ - jmp data_phase_init - -/* - * If we re-enter the data phase after going through another phase, the - * STCNT may have been cleared, so restore it from the residual field. - */ -data_phase_reinit: - mov STCNT0,SCB_RESID_DCNT0 - mov STCNT1,SCB_RESID_DCNT1 - mov STCNT2,SCB_RESID_DCNT2 - jmp data_phase_loop - -p_datain: - mvi DMAPARAMS,0x79 /* - * WIDEODD|SCSIEN|SDMAEN|HDMAEN| - * !DIRECTION|FIFORESET - */ -data_phase_init: - call assert - - test FLAGS, DPHASE jnz data_phase_reinit - call sg_scb2ram - or FLAGS, DPHASE /* We have seen a data phase */ - -data_phase_loop: -/* Guard against overruns */ - test SG_COUNT, 0xff jnz data_phase_inbounds -/* - * Turn on 'Bit Bucket' mode, set the transfer count to - * 16meg and let the target run until it changes phase. - * When the transfer completes, notify the host that we - * had an overrun. - */ - or SXFRCTL1,BITBUCKET - mvi STCNT0,0xff - mvi STCNT1,0xff - mvi STCNT2,0xff - -data_phase_inbounds: -/* If we are the last SG block, don't set wideodd. */ - cmp SG_COUNT,0x01 jne data_phase_wideodd - and DMAPARAMS, 0xbf /* Turn off WIDEODD */ -data_phase_wideodd: - mov DMAPARAMS call dma - -/* Go tell the host about any overruns */ - test SXFRCTL1,BITBUCKET jnz data_phase_overrun - -/* Exit if we had an underrun */ - test SSTAT0,SDONE jz data_phase_finish /* underrun STCNT != 0 */ - -/* - * Advance the scatter-gather pointers if needed - */ -sg_advance: - dec SG_COUNT /* one less segment to go */ - - test SG_COUNT, 0xff jz data_phase_finish /* Are we done? */ - - clr A /* add sizeof(struct scatter) */ - add SG_NEXT0,SG_SIZEOF,SG_NEXT0 - adc SG_NEXT1,A,SG_NEXT1 - -/* - * Load a struct scatter and set up the data address and length. - * If the working value of the SG count is nonzero, then - * we need to load a new set of values. - * - * This, like all DMA's, assumes a little-endian host data storage. - */ -sg_load: - clr HCNT2 - clr HCNT1 - mvi HCNT0,SG_SIZEOF - - mov HADDR0,SG_NEXT0 - mov HADDR1,SG_NEXT1 - mov HADDR2,SG_NEXT2 - mov HADDR3,SG_NEXT3 - - or DFCNTRL,0xd /* HDMAEN|DIRECTION|FIFORESET */ - -/* - * Wait for DMA from host memory to data FIFO to complete, then disable - * DMA and wait for it to acknowledge that it's off. - */ -dma_finish: - test DFSTATUS,HDONE jz dma_finish - /* Turn off DMA preserving WIDEODD */ - and DFCNTRL,WIDEODD -dma_finish2: - test DFCNTRL,HDMAENACK jnz dma_finish2 - -/* - * Copy data from FIFO into SCB data pointer and data count. In - * both FreeBSD and Linux, the scatter list entry is 8 bytes. - * - * struct ahc_dma_seg { - * physaddr addr; four bytes, little-endian order - * long len; four bytes, little endian order - * }; - */ - - mov HADDR0,DFDAT - mov HADDR1,DFDAT - mov HADDR2,DFDAT - mov HADDR3,DFDAT - mov HCNT0,DFDAT - mov HCNT1,DFDAT - mov HCNT2,DFDAT - -/* Load STCNT as well. It is a mirror of HCNT */ - mov STCNT0,HCNT0 - mov STCNT1,HCNT1 - mov STCNT2,HCNT2 - test SSTAT1,PHASEMIS jz data_phase_loop - -data_phase_finish: -/* - * After a DMA finishes, save the SG and STCNT residuals back into the SCB - * We use STCNT instead of HCNT, since it's a reflection of how many bytes - * were transferred on the SCSI (as opposed to the host) bus. - */ - mov SCB_RESID_DCNT0,STCNT0 - mov SCB_RESID_DCNT1,STCNT1 - mov SCB_RESID_DCNT2,STCNT2 - mov SCB_RESID_SGCNT, SG_COUNT - jmp ITloop - -data_phase_overrun: -/* - * Turn off BITBUCKET mode and notify the host - */ - and SXFRCTL1,0x7f /* ~BITBUCKET */ - mvi INTSTAT,DATA_OVERRUN - jmp ITloop - -/* - * Command phase. Set up the DMA registers and let 'er rip. - */ -p_command: - call assert - -/* - * Load HADDR and HCNT. - */ - mov HADDR0, SCB_CMDPTR0 - mov HADDR1, SCB_CMDPTR1 - mov HADDR2, SCB_CMDPTR2 - mov HADDR3, SCB_CMDPTR3 - mov HCNT0, SCB_CMDLEN - clr HCNT1 - clr HCNT2 - - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 - - mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| - # DIRECTION|FIFORESET - jmp ITloop - -/* - * Status phase. Wait for the data byte to appear, then read it - * and store it into the SCB. - */ -p_status: - mvi SCB_TARGET_STATUS call inb_first - jmp mesgin_done - -/* - * Message out phase. If there is not an active message, but the target - * took us into this phase anyway, build a no-op message and send it. - */ -p_mesgout: - test MSG_LEN, 0xff jnz p_mesgout_start - mvi MSG_NOP call mk_mesg /* build NOP message */ - -p_mesgout_start: -/* - * Set up automatic PIO transfer from MSG0. Bit 3 in - * SXFRCTL0 (SPIOEN) is already on. - */ - mvi SINDEX,MSG0 - mov DINDEX,MSG_LEN - -/* - * When target asks for a byte, drop ATN if it's the last one in - * the message. Otherwise, keep going until the message is exhausted. - * - * Keep an eye out for a phase change, in case the target issues - * a MESSAGE REJECT. - */ -p_mesgout_loop: - test SSTAT1,PHASEMIS jnz p_mesgout_phasemis - test SSTAT0,SPIORDY jz p_mesgout_loop - test SSTAT1,PHASEMIS jnz p_mesgout_phasemis - cmp DINDEX,1 jne p_mesgout_outb /* last byte? */ - mvi CLRSINT1,CLRATNO /* drop ATN */ -p_mesgout_outb: - dec DINDEX - or CLRSINT0, CLRSPIORDY - mov SCSIDATL,SINDIR - -p_mesgout4: - test DINDEX,0xff jnz p_mesgout_loop - -/* - * If the next bus phase after ATN drops is a message out, it means - * that the target is requesting that the last message(s) be resent. - */ -p_mesgout_snoop: - test SSTAT1,BUSFREE jnz p_mesgout_done - test SSTAT1,REQINIT jz p_mesgout_snoop - - test SSTAT1,PHASEMIS jnz p_mesgout_done - - or SCSISIGO,ATNO /* turn on ATNO */ - - jmp ITloop - -p_mesgout_phasemis: - mvi CLRSINT1,CLRATNO /* Be sure to turn ATNO off */ -p_mesgout_done: - clr MSG_LEN /* no active msg */ - jmp ITloop - -/* - * Message in phase. Bytes are read using Automatic PIO mode. - */ -p_mesgin: - mvi A call inb_first /* read the 1st message byte */ - mov REJBYTE,A /* save it for the driver */ - - test A,MSG_IDENTIFY jnz mesgin_identify - cmp A,MSG_DISCONNECT je mesgin_disconnect - cmp A,MSG_SDPTRS je mesgin_sdptrs - cmp ALLZEROS,A je mesgin_complete - cmp A,MSG_RDPTRS je mesgin_rdptrs - cmp A,MSG_EXTENDED je mesgin_extended - cmp A,MSG_REJECT je mesgin_reject - -rej_mesgin: -/* - * We have no idea what this message in is, and there's no way - * to pass it up to the kernel, so we issue a message reject and - * hope for the best. Since we're now using manual PIO mode to - * read in the message, there should no longer be a race condition - * present when we assert ATN. In any case, rejection should be a - * rare occurrence - signal the driver when it happens. - */ - or SCSISIGO,ATNO /* turn on ATNO */ - mvi INTSTAT,SEND_REJECT /* let driver know */ - - mvi MSG_REJECT call mk_mesg - -mesgin_done: - call inb_last /*ack & turn auto PIO back on*/ - jmp ITloop - - -mesgin_complete: -/* - * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT, - * and trigger a completion interrupt. Check status for non zero return - * and interrupt driver if needed. This allows the driver to interpret - * errors only when they occur instead of always uploading the scb. If - * the status is SCSI_CHECK, the driver will download a new scb requesting - * sense to replace the old one, modify the "waiting for selection" SCB list - * and set RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE the - * sequencer imediately jumps to main loop where it will run down the waiting - * SCB list and process the sense request. If the kernel driver does not - * wish to request sense, it need only clear RETURN_1, and the command is - * allowed to complete. We don't bother to post to the QOUTFIFO in the - * error case since it would require extra work in the kernel driver to - * ensure that the entry was removed before the command complete code tried - * processing it. - * - * First check for residuals - */ - test SCB_RESID_SGCNT,0xff jz check_status -/* - * If we have a residual count, interrupt and tell the host. Other - * alternatives are to pause the sequencer on all command completes (yuck), - * dma the resid directly to the host (slick, we may have space to do it now) - * or have the sequencer pause itself when it encounters a non-zero resid - * (unnecessary pause just to flag the command -yuck-, but takes one instruction - * and since it shouldn't happen that often is good enough for our purposes). - */ -resid: - mvi INTSTAT,RESIDUAL - -check_status: - test SCB_TARGET_STATUS,0xff jz status_ok /* Good Status? */ - mvi INTSTAT,BAD_STATUS /* let driver know */ - cmp RETURN_1, SEND_SENSE jne status_ok - jmp mesgin_done - -status_ok: -/* First, mark this target as free. */ - test SCB_CONTROL,TAG_ENB jnz test_immediate /* - * Tagged commands - * don't busy the - * target. - */ - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz clear_a - xor ACTIVE_B,A - jmp test_immediate - -clear_a: - xor ACTIVE_A,A - -test_immediate: - test SCB_CMDLEN,0xff jnz complete /* Immediate message complete */ -/* - * Pause the sequencer until the driver gets around to handling the command - * complete. This is so that any action that might require careful timing - * with the completion of this command can occur. - */ - mvi INTSTAT,IMMEDDONE - jmp start -complete: - mov QOUTFIFO,SCB_TAG - mvi INTSTAT,CMDCMPLT - jmp mesgin_done - - -/* - * Is it an extended message? We only support the synchronous and wide data - * transfer request messages, which will probably be in response to - * WDTR or SDTR message outs from us. If it's not SDTR or WDTR, reject it - - * apparently this can be done after any message in byte, according - * to the SCSI-2 spec. - */ -mesgin_extended: - mvi ARG_1 call inb_next /* extended message length */ - mvi REJBYTE_EXT call inb_next /* extended message code */ - - cmp REJBYTE_EXT,MSG_SDTR je p_mesginSDTR - cmp REJBYTE_EXT,MSG_WDTR je p_mesginWDTR - jmp rej_mesgin - -p_mesginWDTR: - cmp ARG_1,2 jne rej_mesgin /* extended mesg length=2 */ - mvi ARG_1 call inb_next /* Width of bus */ - mvi INTSTAT,WDTR_MSG /* let driver know */ - test RETURN_1,0xff jz mesgin_done /* Do we need to send WDTR? */ - cmp RETURN_1,SEND_REJ je rej_mesgin /* - * Bus width was too large - * Reject it. - */ - -/* We didn't initiate the wide negotiation, so we must respond to the request */ - and RETURN_1,0x7f /* Clear the SEND_WDTR Flag */ - mvi DINDEX,MSG0 - mvi MSG0 call mk_wdtr /* build WDTR message */ - or SCSISIGO,ATNO /* turn on ATNO */ - jmp mesgin_done - -p_mesginSDTR: - cmp ARG_1,3 jne rej_mesgin /* extended mesg length=3 */ - mvi ARG_1 call inb_next /* xfer period */ - mvi A call inb_next /* REQ/ACK offset */ - mvi INTSTAT,SDTR_MSG /* call driver to convert */ - - test RETURN_1,0xff jz mesgin_done /* Do we need to mk_sdtr/rej */ - cmp RETURN_1,SEND_REJ je rej_mesgin /* - * Requested SDTR too small - * Reject it. - */ - clr ARG_1 /* Use the scratch ram rate */ - mvi DINDEX, MSG0 - mvi MSG0 call mk_sdtr - or SCSISIGO,ATNO /* turn on ATNO */ - jmp mesgin_done - -/* - * Is it a disconnect message? Set a flag in the SCB to remind us - * and await the bus going free. - */ -mesgin_disconnect: - or SCB_CONTROL,DISCONNECTED - test FLAGS, PAGESCBS jz mesgin_done -/* - * Link this SCB into the DISCONNECTED list. This list holds the - * candidates for paging out an SCB if one is needed for a new command. - * Modifying the disconnected list is a critical(pause dissabled) section. - */ - mvi SCB_PREV, SCB_LIST_NULL - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - mov SCB_NEXT, DISCONNECTED_SCBH - mov DISCONNECTED_SCBH, SCBPTR - cmp SCB_NEXT,SCB_LIST_NULL je linkdone - mov SCBPTR,SCB_NEXT - mov SCB_PREV,DISCONNECTED_SCBH - mov SCBPTR,DISCONNECTED_SCBH -linkdone: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - jmp mesgin_done - -/* - * Save data pointers message? Copy working values into the SCB, - * usually in preparation for a disconnect. - */ -mesgin_sdptrs: - call sg_ram2scb - jmp mesgin_done - -/* - * Restore pointers message? Data pointers are recopied from the - * SCB anytime we enter a data phase for the first time, so all - * we need to do is clear the DPHASE flag and let the data phase - * code do the rest. - */ -mesgin_rdptrs: - and FLAGS,0xef /* - * !DPHASE we'll reload them - * the next time through - */ - jmp mesgin_done - -/* - * Identify message? For a reconnecting target, this tells us the lun - * that the reconnection is for - find the correct SCB and switch to it, - * clearing the "disconnected" bit so we don't "find" it by accident later. - */ -mesgin_identify: - test A,0x78 jnz rej_mesgin /*!DiscPriv|!LUNTAR|!Reserved*/ - - and A,0x07 /* lun in lower three bits */ - or SAVED_TCL,A,SELID - and SAVED_TCL,0xf7 - and A,SELBUSB,SBLKCTL /* B Channel?? */ - or SAVED_TCL,A - call inb_last /* ACK */ - -/* - * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message. - * If we get one, we use the tag returned to switch to find the proper - * SCB. With SCB paging, this requires using findSCB for both tagged - * and non-tagged transactions since the SCB may exist in any slot. - * If we're not using SCB paging, we can use the tag as the direct - * index to the SCB. - */ - mvi ARG_1,SCB_LIST_NULL /* Default to no-tag */ -snoop_tag_loop: - test SSTAT1,BUSFREE jnz use_findSCB - test SSTAT1,REQINIT jz snoop_tag_loop - test SSTAT1,PHASEMIS jnz use_findSCB - mvi A call inb_first - cmp A,MSG_SIMPLE_TAG jne use_findSCB -get_tag: - mvi ARG_1 call inb_next /* tag value */ -/* - * See if the tag is in range. The tag is < SCBCOUNT if we add - * the complement of SCBCOUNT to the incoming tag and there is - * no carry. - */ - mov A,COMP_SCBCOUNT - add SINDEX,A,ARG_1 - jc abort_tag - -/* - * Ensure that the SCB the tag points to is for a SCB transaction - * to the reconnecting target. - */ - test FLAGS, PAGESCBS jz index_by_tag - call inb_last /* Ack Tag */ -use_findSCB: - mov ALLZEROS call findSCB /* Have to search */ -setup_SCB: - and SCB_CONTROL,0xfb /* clear disconnect bit in SCB */ - or FLAGS,IDENTIFY_SEEN /* make note of IDENTIFY */ - jmp ITloop -index_by_tag: - mov SCBPTR,ARG_1 - mov A,SAVED_TCL - cmp SCB_TCL,A jne abort_tag - test SCB_CONTROL,TAG_ENB jz abort_tag - call inb_last /* Ack Successful tag */ - jmp setup_SCB - -abort_tag: - or SCSISIGO,ATNO /* turn on ATNO */ - mvi INTSTAT,ABORT_TAG /* let driver know */ - mvi MSG_ABORT_TAG call mk_mesg /* ABORT TAG message */ - jmp mesgin_done - -/* - * Message reject? Let the kernel driver handle this. If we have an - * outstanding WDTR or SDTR negotiation, assume that it's a response from - * the target selecting 8bit or asynchronous transfer, otherwise just ignore - * it since we have no clue what it pertains to. - */ -mesgin_reject: - mvi INTSTAT, REJECT_MSG - jmp mesgin_done - -/* - * [ ADD MORE MESSAGE HANDLING HERE ] - */ - -/* - * Bus free phase. It might be useful to interrupt the device - * driver if we aren't expecting this. For now, make sure that - * ATN isn't being asserted and look for a new command. - */ -p_busfree: - mvi CLRSINT1,CLRATNO - clr LASTPHASE - -/* - * if this is an immediate command, perform a pseudo command complete to - * notify the driver. - */ - test SCB_CMDLEN,0xff jz status_ok - jmp start - -/* - * Locking the driver out, build a one-byte message passed in SINDEX - * if there is no active message already. SINDEX is returned intact. - */ -mk_mesg: - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - test MSG_LEN,0xff jz mk_mesg1 /* Should always succeed */ - - /* - * Hmmm. For some reason the mesg buffer is in use. - * Tell the driver. It should look at SINDEX to find - * out what we wanted to use the buffer for and resolve - * the conflict. - */ - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - mvi INTSTAT,MSG_BUFFER_BUSY - -mk_mesg1: - mvi MSG_LEN,1 /* length = 1 */ - mov MSG0,SINDEX /* 1-byte message */ - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ - -/* - * Functions to read data in Automatic PIO mode. - * - * According to Adaptec's documentation, an ACK is not sent on input from - * the target until SCSIDATL is read from. So we wait until SCSIDATL is - * latched (the usual way), then read the data byte directly off the bus - * using SCSIBUSL. When we have pulled the ATN line, or we just want to - * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI - * spec guarantees that the target will hold the data byte on the bus until - * we send our ACK. - * - * The assumption here is that these are called in a particular sequence, - * and that REQ is already set when inb_first is called. inb_{first,next} - * use the same calling convention as inb. - */ - -inb_next: - or CLRSINT0, CLRSPIORDY - mov NONE,SCSIDATL /*dummy read from latch to ACK*/ -inb_next_wait: - test SSTAT1,PHASEMIS jnz mesgin_phasemis - test SSTAT0,SPIORDY jz inb_next_wait /* wait for next byte */ -inb_first: - mov DINDEX,SINDEX - test SSTAT1,PHASEMIS jnz mesgin_phasemis - mov DINDIR,SCSIBUSL ret /*read byte directly from bus*/ -inb_last: - mov NONE,SCSIDATL ret /*dummy read from latch to ACK*/ - -mesgin_phasemis: -/* - * We expected to receive another byte, but the target changed phase - */ - mvi INTSTAT, MSGIN_PHASEMIS - jmp ITloop - -/* - * DMA data transfer. HADDR and HCNT must be loaded first, and - * SINDEX should contain the value to load DFCNTRL with - 0x3d for - * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared - * during initialization. - */ -dma: - mov DFCNTRL,SINDEX -dma1: - test SSTAT0,DMADONE jnz dma3 - test SSTAT1,PHASEMIS jz dma1 /* ie. underrun */ - -/* - * We will be "done" DMAing when the transfer count goes to zero, or - * the target changes the phase (in light of this, it makes sense that - * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are - * doing a SCSI->Host transfer, the data FIFO should be flushed auto- - * magically on STCNT=0 or a phase change, so just wait for FIFO empty - * status. - */ -dma3: - test SINDEX,DIRECTION jnz dma5 -dma4: - test DFSTATUS,FIFOEMP jz dma4 - -/* - * Now shut the DMA enables off and make sure that the DMA enables are - * actually off first lest we get an ILLSADDR. - */ -dma5: - /* disable DMA, but maintain WIDEODD */ - and DFCNTRL,WIDEODD -dma6: - test DFCNTRL,0x38 jnz dma6 /* SCSIENACK|SDMAENACK|HDMAENACK */ - - ret - -/* - * Common SCSI initialization for selection and reselection. Expects - * the target SCSI ID to be in the upper four bits of SINDEX, and A's - * contents are stomped on return. - */ -initialize_scsiid: - and SINDEX,0xf0 /* Get target ID */ - and A,0x0f,SCSIID - or SINDEX,A - mov SCSIID,SINDEX ret - -/* - * Assert that if we've been reselected, then we've seen an IDENTIFY - * message. - */ -assert: - test FLAGS,RESELECTED jz return /* reselected? */ - test FLAGS,IDENTIFY_SEEN jnz return /* seen IDENTIFY? */ - - mvi INTSTAT,NO_IDENT ret /* no - cause a kernel panic */ - -/* - * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag - * value in ARG_1. If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged - * SCB. Have the kernel print a warning message if it can't be found, and - * generate an ABORT/ABORT_TAG message to the target. SINDEX should be - * cleared on call. - */ -findSCB: - mov A,SAVED_TCL - mov SCBPTR,SINDEX /* switch to next SCB */ - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - cmp SCB_TCL,A jne findSCB1 /* target ID/channel/lun match? */ - test SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/ - test SCB_CONTROL,TAG_ENB jnz findTaggedSCB - cmp ARG_1,SCB_LIST_NULL je foundSCB - jmp findSCB1 -findTaggedSCB: - mov A, ARG_1 /* Tag passed in ARG_1 */ - cmp SCB_TAG,A jne findSCB1 /* Found it? */ -foundSCB: - test FLAGS,PAGESCBS jz foundSCB_ret -/* Remove this SCB from the disconnection list */ - cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev - mov SAVED_LINKPTR, SCB_PREV - mov SCBPTR, SCB_NEXT - mov SCB_PREV, SAVED_LINKPTR - mov SCBPTR, SINDEX -unlink_prev: - cmp SCB_PREV,SCB_LIST_NULL je rHead/* At the head of the list */ - mov SAVED_LINKPTR, SCB_NEXT - mov SCBPTR, SCB_PREV - mov SCB_NEXT, SAVED_LINKPTR - mov SCBPTR, SINDEX - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ -rHead: - mov DISCONNECTED_SCBH,SCB_NEXT -foundSCB_ret: - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ - -findSCB1: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - inc SINDEX - mov A,SCBCOUNT - cmp SINDEX,A jne findSCB - - mvi INTSTAT,NO_MATCH /* not found - signal kernel */ - cmp RETURN_1,SCB_PAGEDIN je return - or SCSISIGO,ATNO /* assert ATNO */ - cmp ARG_1,SCB_LIST_NULL jne find_abort_tag - mvi MSG_ABORT call mk_mesg - jmp ITloop -find_abort_tag: - mvi MSG_ABORT_TAG call mk_mesg - jmp ITloop - -/* - * Make a working copy of the scatter-gather parameters from the SCB. - */ -sg_scb2ram: - mov HADDR0, SCB_DATAPTR0 - mov HADDR1, SCB_DATAPTR1 - mov HADDR2, SCB_DATAPTR2 - mov HADDR3, SCB_DATAPTR3 - mov HCNT0, SCB_DATACNT0 - mov HCNT1, SCB_DATACNT1 - mov HCNT2, SCB_DATACNT2 - - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 - - mov SG_COUNT,SCB_SGCOUNT - - mov SG_NEXT0, SCB_SGPTR0 - mov SG_NEXT1, SCB_SGPTR1 - mov SG_NEXT2, SCB_SGPTR2 - mov SG_NEXT3, SCB_SGPTR3 ret - -/* - * Copying RAM values back to SCB, for Save Data Pointers message, but - * only if we've actually been into a data phase to change them. This - * protects against bogus data in scratch ram and the residual counts - * since they are only initialized when we go into data_in or data_out. - */ -sg_ram2scb: - test FLAGS, DPHASE jz return - mov SCB_SGCOUNT,SG_COUNT - - mov SCB_SGPTR0,SG_NEXT0 - mov SCB_SGPTR1,SG_NEXT1 - mov SCB_SGPTR2,SG_NEXT2 - mov SCB_SGPTR3,SG_NEXT3 - - mov SCB_DATAPTR0,SHADDR0 - mov SCB_DATAPTR1,SHADDR1 - mov SCB_DATAPTR2,SHADDR2 - mov SCB_DATAPTR3,SHADDR3 - -/* - * Use the residual number since STCNT is corrupted by any message transfer - */ - mov SCB_DATACNT0,SCB_RESID_DCNT0 - mov SCB_DATACNT1,SCB_RESID_DCNT1 - mov SCB_DATACNT2,SCB_RESID_DCNT2 ret - -/* - * Add the array base TARG_SCRATCH to the target offset (the target address - * is in SCSIID), and return the result in SINDEX. The accumulator - * contains the 3->8 decoding of the target ID on return. - */ -ndx_dtr: - shr A,SCSIID,4 - test SBLKCTL,SELBUSB jz ndx_dtr_2 - or A,0x08 /* Channel B entries add 8 */ -ndx_dtr_2: - add SINDEX,TARG_SCRATCH,A ret - -/* - * If we need to negotiate transfer parameters, build the WDTR or SDTR message - * starting at the address passed in SINDEX. DINDEX is modified on return. - * The SCSI-II spec requires that Wide negotiation occur first and you can - * only negotiate one or the other at a time otherwise in the event of a message - * reject, you wouldn't be able to tell which message was the culprit. - */ -mk_dtr: - test SCB_CONTROL,NEEDWDTR jnz mk_wdtr_16bit - mvi ARG_1, MAXOFFSET /* Force an offset of 15 or 8 if WIDE */ - -mk_sdtr: - mvi DINDIR,1 /* extended message */ - mvi DINDIR,3 /* extended message length = 3 */ - mvi DINDIR,1 /* SDTR code */ - call sdtr_to_rate - mov DINDIR,RETURN_1 /* REQ/ACK transfer period */ - cmp ARG_1, MAXOFFSET je mk_sdtr_max_offset - and DINDIR,0x0f,SINDIR /* Sync Offset */ - -mk_sdtr_done: - add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */ - -mk_sdtr_max_offset: -/* - * We're initiating sync negotiation, so request the max offset we can (15 or 8) - */ - /* Talking to a WIDE device? */ - test SCSIRATE, WIDEXFER jnz wmax_offset - mvi DINDIR, MAX_OFFSET_8BIT - jmp mk_sdtr_done - -wmax_offset: - mvi DINDIR, MAX_OFFSET_16BIT - jmp mk_sdtr_done - -mk_wdtr_16bit: - mvi ARG_1,BUS_16_BIT -mk_wdtr: - mvi DINDIR,1 /* extended message */ - mvi DINDIR,2 /* extended message length = 2 */ - mvi DINDIR,3 /* WDTR code */ - mov DINDIR,ARG_1 /* bus width */ - - add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */ - -sdtr_to_rate: - call ndx_dtr /* index scratch space for target */ - shr A,SINDIR,0x4 - dec SINDEX /* Preserve SINDEX */ - and A,0x7 - clr RETURN_1 -sdtr_to_rate_loop: - test A,0x0f jz sdtr_to_rate_done - add RETURN_1,0x19 - dec A - jmp sdtr_to_rate_loop -sdtr_to_rate_done: - shr RETURN_1,0x2 - add RETURN_1,0x19 - test SXFRCTL0,ULTRAEN jz return - shr RETURN_1,0x1 -return: - ret diff -u --recursive --new-file v2.1.47/linux/drivers/scsi/aic7xxx_asm.c linux/drivers/scsi/aic7xxx_asm.c --- v2.1.47/linux/drivers/scsi/aic7xxx_asm.c Sat Apr 20 10:59:10 1996 +++ linux/drivers/scsi/aic7xxx_asm.c Wed Dec 31 16:00:00 1969 @@ -1,734 +0,0 @@ -/*+M************************************************************************* - * Adaptec AIC7xxx sequencer code assembler. - * - * Copyright (c) 1994 John Aycock - * The University of Calgary Department of Computer Science. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Comments are started by `#' and continue to the end of the line; lines - * may be of the form: - *