diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
commit | dcec8a13bf565e47942a1751a9cec21bec5648fe (patch) | |
tree | 548b69625b18cc2e88c3e68d0923be546c9ebb03 /arch | |
parent | 2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff) |
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash.
o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'arch')
334 files changed, 30492 insertions, 7696 deletions
diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile index 9cd1ab3e6..ff6e422df 100644 --- a/arch/alpha/Makefile +++ b/arch/alpha/Makefile @@ -10,8 +10,38 @@ NM := nm -B +#LINKFLAGS = -static -T arch/alpha/vmlinux.lds +#CFLAGS := $(CFLAGS) -pipe -mno-fp-regs -ffixed-8 + +ifdef CONFIG_CROSSCOMPILE +# enable this for linking under OSF/1: +LINKFLAGS = -non_shared -T 0xfffffc0000310000 -N +else + elf=$(shell if $(LD) --help | grep elf64alpha >/dev/null; then echo yes; fi) + ifeq ($(elf),yes) +# LINKFLAGS = -static -Ttext 0xfffffc0000310000 -N LINKFLAGS = -static -T arch/alpha/vmlinux.lds -CFLAGS := $(CFLAGS) -pipe -mno-fp-regs -ffixed-8 + else + LINKFLAGS = -static -T arch/alpha/vmlinux.lds -N + endif +# GNU gcc/cc1/as can use pipes instead of temporary files +CFLAGS := $(CFLAGS) -pipe +endif + +CFLAGS := $(CFLAGS) -mno-fp-regs -ffixed-8 -Wno-uninitialized + +# determine if we can use the BWX instructions with GAS +$(shell rm -f ./GAS_VER) +$(shell $(AS) --version >& ./GAS_VER) +OLD_GAS := $(shell if cat ./GAS_VER | grep 'version 2.7' > /dev/null; then echo yes; else echo no; fi) +$(shell rm -f ./GAS_VER) + +ifneq ($(OLD_GAS),yes) +# if PYXIS, then enable use of BWIO space + ifeq ($(CONFIG_ALPHA_PYXIS),y) + CFLAGS := $(CFLAGS) -Wa,-m21164a -DBWX_USABLE -DBWIO_ENABLED + endif +endif HEAD := arch/alpha/kernel/head.o @@ -23,7 +53,7 @@ ifeq ($(CONFIG_MATHEMU),y) CORE_FILES := $(CORE_FILES) arch/alpha/math-emu/math-emu.o endif -LIBS := arch/alpha/lib/lib.a $(LIBS) arch/alpha/lib/lib.a +LIBS := $(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot diff --git a/arch/alpha/boot/bootp.c b/arch/alpha/boot/bootp.c index 65829793e..a1dc6d818 100644 --- a/arch/alpha/boot/bootp.c +++ b/arch/alpha/boot/bootp.c @@ -16,6 +16,7 @@ #include <asm/console.h> #include <asm/hwrpb.h> #include <asm/pgtable.h> +#include <asm/io.h> #include <stdarg.h> @@ -156,7 +157,8 @@ void pal_init(void) printk("Ok (rev %lx)\n", rev); /* remove the old virtual page-table mapping */ L1[1] = 0; - flush_tlb_all(); + + tbia(); /* do it directly in case we are SMP */ } static inline long load(unsigned long dst, @@ -189,30 +191,59 @@ extern char _end; void start_kernel(void) { - long i; - int nbytes; - char envval[256]; + static long i; + static int nbytes; + /* + * note that this crufty stuff with static and envval and envbuf + * is because: + * + * 1. frequently, the stack is is short, and we don't want to overrun; + * 2. frequently the stack is where we are going to copy the kernel to; + * 3. a certain SRM console required the GET_ENV output to stack. + */ + static char envval[256]; + char envbuf[256]; printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n"); if (hwrpb.pagesize != 8192) { - printk("Expected 8kB pages, got %ldkB\n", hwrpb.pagesize >> 10); + printk("Expected 8kB pages, got %ldkB\n", + hwrpb.pagesize >> 10); return; } pal_init(); nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS, - envval, sizeof(envval)); - if (nbytes < 0) { + envbuf, sizeof(envbuf)); + if (nbytes < 0 || nbytes >= sizeof(envbuf)) { nbytes = 0; } - envval[nbytes] = '\0'; - strcpy((char*)ZERO_PAGE, envval); - - printk("Loading the kernel ...\n"); + envbuf[nbytes] = '\0'; + memcpy(envval, envbuf, nbytes+1); + printk("Loading the kernel...'%s'\n", envval); /* NOTE: *no* callbacks or printouts from here on out!!! */ +#if 1 + /* + * this is a hack, as some consoles seem to get virtual 20000000 + * (ie where the SRM console puts the kernel bootp image) memory + * overlapping physical 310000 memory, which causes real problems + * when attempting to copy the former to the latter... :-( + * + * so, we first move the kernel virtual-to-physical way above where + * we physically want the kernel to end up, then copy it from there + * to its final resting place... ;-} + * + * sigh... + */ + + i = load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE); + i = load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE); +#else i = load(START_ADDR, KERNEL_ORIGIN, KERNEL_SIZE); +#endif + + strcpy((char*)ZERO_PAGE, envval); runkernel(); diff --git a/arch/alpha/config.in b/arch/alpha/config.in index 1fefe0c62..fc2546218 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -6,10 +6,11 @@ mainmenu_name "Kernel configuration of Linux for Alpha machines" # clear all implied options (don't want default values for those): unset CONFIG_CROSSCOMPILE CONFIG_NATIVE -unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5 +unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5 CONFIG_ALPHA_EV6 unset CONFIG_PCI CONFIG_ALPHA_EISA unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS +unset CONFIG_ALPHA_TSUNAMI CONFIG_ALPHA_MCPCIA unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION mainmenu_option next_comment @@ -48,13 +49,16 @@ choice 'Alpha system type' \ PC164 CONFIG_ALPHA_PC164 \ LX164 CONFIG_ALPHA_LX164 \ SX164 CONFIG_ALPHA_SX164 \ + DP264 CONFIG_ALPHA_DP264 \ Jensen CONFIG_ALPHA_JENSEN \ Noname CONFIG_ALPHA_NONAME \ + Takara CONFIG_ALPHA_TAKARA \ Mikasa CONFIG_ALPHA_MIKASA \ Noritake CONFIG_ALPHA_NORITAKE \ Alcor CONFIG_ALPHA_ALCOR \ Miata CONFIG_ALPHA_MIATA \ Sable CONFIG_ALPHA_SABLE \ + Rawhide CONFIG_ALPHA_RAWHIDE \ AlphaBook1 CONFIG_ALPHA_BOOK1 \ Ruffian CONFIG_ALPHA_RUFFIAN \ Platform2000 CONFIG_ALPHA_P2K" Cabriolet @@ -78,7 +82,8 @@ then define_bool CONFIG_ALPHA_APECS y fi if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \ - -o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" ] + -o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" \ + -o "$CONFIG_ALPHA_TAKARA" = "y" ] then define_bool CONFIG_PCI y define_bool CONFIG_ALPHA_EV5 y @@ -86,9 +91,7 @@ then fi if [ "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ] then - choice 'CPU daughtercard' \ - "Pinnacle CONFIG_ALPHA_PINNACLE \ - Primo CONFIG_ALPHA_PRIMO" Primo + bool 'EV5 CPU daughtercard (model 5/xxx)?' CONFIG_ALPHA_PRIMO if [ "$CONFIG_ALPHA_PRIMO" = "y" ] then define_bool CONFIG_ALPHA_EV5 y @@ -102,7 +105,13 @@ fi if [ "$CONFIG_ALPHA_SABLE" = "y" ] then define_bool CONFIG_PCI y + bool 'EV5 CPU(s) (model 5/xxx)?' CONFIG_ALPHA_GAMMA + if [ "$CONFIG_ALPHA_GAMMA" = "y" ] + then + define_bool CONFIG_ALPHA_EV5 y + else define_bool CONFIG_ALPHA_EV4 y + fi define_bool CONFIG_ALPHA_T2 y fi if [ "$CONFIG_ALPHA_MIATA" = "y" -o "$CONFIG_ALPHA_LX164" = "y" \ @@ -112,6 +121,18 @@ then define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_PYXIS y fi +if [ "$CONFIG_ALPHA_DP264" = "y" ] +then + define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV6 y + define_bool CONFIG_ALPHA_TSUNAMI y +fi +if [ "$CONFIG_ALPHA_RAWHIDE" = "y" ] +then + define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_MCPCIA y +fi if [ "$CONFIG_ALPHA_JENSEN" = "y" ] then define_bool CONFIG_ALPHA_EV4 y @@ -127,12 +148,19 @@ if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \ -o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" \ -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \ -o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \ - -o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" ] + -o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" \ + -o "$CONFIG_ALPHA_DP264" = "y" -o "$CONFIG_ALPHA_RAWHIDE" = "y" ] then - bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM + bool 'Use SRM as bootloader' CONFIG_ALPHA_SRM + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_ALPHA_SRM" = "y" ]; then + bool ' Use SRM PCI setup' CONFIG_ALPHA_SRM_SETUP + fi + fi fi if [ "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \ - -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ] + -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" \ + -o "$CONFIG_ALPHA_RAWHIDE" = "y" ] then define_bool CONFIG_ALPHA_EISA y fi @@ -141,9 +169,15 @@ then define_bool CONFIG_ALPHA_AVANTI y fi +#bool 'Echo console messages on /dev/ttyS0 (COM1)' CONFIG_SERIAL_ECHO + if [ "$CONFIG_PCI" = "y" ]; then bool 'TGA Console Support' CONFIG_TGA_CONSOLE - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +# if [ "$CONFIG_TGA_CONSOLE" = "y" ]; then +# bool 'VGA Console Support' CONFIG_VGA_CONSOLE +# fi + bool 'PCI quirks' CONFIG_PCI_QUIRKS + if [ "$CONFIG_PCI_QUIRKS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE fi bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC @@ -216,17 +250,6 @@ if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then fi endmenu -# Conditionally compile in the Uniform CD-ROM driver -if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then - define_bool CONFIG_CDROM y -else - if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then - define_bool CONFIG_CDROM m - else - define_bool CONFIG_CDROM n - fi -fi - source fs/Config.in source fs/nls/Config.in diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig index 1bdecf444..7d78e4b97 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -27,14 +27,20 @@ CONFIG_NATIVE=y # CONFIG_ALPHA_EB64P is not set # CONFIG_ALPHA_EB164 is not set # CONFIG_ALPHA_PC164 is not set +# CONFIG_ALPHA_LX164 is not set +# CONFIG_ALPHA_SX164 is not set +# CONFIG_ALPHA_DP264 is not set # CONFIG_ALPHA_JENSEN is not set # CONFIG_ALPHA_NONAME is not set +# CONFIG_ALPHA_TAKARA is not set # CONFIG_ALPHA_MIKASA is not set # CONFIG_ALPHA_NORITAKE is not set CONFIG_ALPHA_ALCOR=y # CONFIG_ALPHA_MIATA is not set # CONFIG_ALPHA_SABLE is not set +# CONFIG_ALPHA_RAWHIDE is not set # CONFIG_ALPHA_BOOK1 is not set +# CONFIG_ALPHA_RUFFIAN is not set # CONFIG_ALPHA_P2K is not set CONFIG_PCI=y CONFIG_ALPHA_EV5=y @@ -214,7 +220,6 @@ CONFIG_DE4X5=y # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set -CONFIG_CDROM=y # # Filesystems diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index ba0aee556..8d09ea8c4 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -8,9 +8,9 @@ # Note 2! The CFLAGS definitions are now in the main makefile... .S.s: - $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s + $(CPP) -D__ASSEMBLY__ $(AFLAGS) -traditional $< -o $*.s .S.o: - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o all: kernel.o head.o @@ -35,19 +35,29 @@ endif ifdef CONFIG_ALPHA_T2 O_OBJS += t2.o endif +ifdef CONFIG_ALPHA_TSUNAMI +O_OBJS += tsunami.o +endif +ifdef CONFIG_ALPHA_MCPCIA +O_OBJS += mcpcia.o +endif + ifneq ($(CONFIG_ALPHA_PC164)$(CONFIG_ALPHA_LX164),nn) O_OBJS += smc37c93x.o endif -ifdef CONFIG_ALPHA_SX164 +ifneq ($(CONFIG_ALPHA_SX164)$(CONFIG_ALPHA_MIATA)$(CONFIG_ALPHA_DP264),nnn) O_OBJS += smc37c669.o endif +ifdef SMP +O_OBJS += smp.o +endif all: kernel.o head.o head.o: head.s head.s: head.S $(TOPDIR)/include/asm-alpha/system.h - $(CPP) -traditional -o $*.s $< + $(CPP) -traditional $(AFLAGS) -o $*.s $< include $(TOPDIR)/Rules.make diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c index 132175541..c220ccdc6 100644 --- a/arch/alpha/kernel/alpha_ksyms.c +++ b/arch/alpha/kernel/alpha_ksyms.c @@ -27,7 +27,6 @@ #define __KERNEL_SYSCALLS__ #include <asm/unistd.h> -extern void bcopy (const char *src, char *dst, int len); extern struct hwrpb_struct *hwrpb; extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(struct pt_regs *, elf_fpregset_t *); @@ -134,7 +133,6 @@ EXPORT_SYMBOL(__strlen_user); * interface isn't gonna change any time soon now, so it's OK * to leave it out of version control. */ -# undef bcopy # undef memcpy # undef memset EXPORT_SYMBOL_NOVERS(__divl); @@ -147,7 +145,3 @@ EXPORT_SYMBOL_NOVERS(__remq); EXPORT_SYMBOL_NOVERS(__remqu); EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); - -#if CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/alpha/kernel/apecs.c b/arch/alpha/kernel/apecs.c index d5e7fa2a7..2bbdf0062 100644 --- a/arch/alpha/kernel/apecs.c +++ b/arch/alpha/kernel/apecs.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/config.h> #include <linux/types.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <asm/system.h> @@ -18,13 +17,14 @@ #include <asm/hwrpb.h> #include <asm/ptrace.h> -/* NOTE: Herein are back-to-back mb insns. They are magic. - A plausible explanation is that the i/o controler does not properly - handle the system transaction. Another involves timing. Ho hum. */ +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the i/o controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ extern struct hwrpb_struct *hwrpb; extern asmlinkage void wrmces(unsigned long mces); -extern int alpha_sys_type; /* * BIOS32-style PCI interface: @@ -36,13 +36,16 @@ extern int alpha_sys_type; # define DBG(args) #endif -#define vulp volatile unsigned long * #define vuip volatile unsigned int * static volatile unsigned int apecs_mcheck_expected = 0; static volatile unsigned int apecs_mcheck_taken = 0; -static unsigned long apecs_jd, apecs_jd1, apecs_jd2; +static unsigned int apecs_jd, apecs_jd1, apecs_jd2; +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int APECS_DMA_WIN_BASE = APECS_DMA_WIN_BASE_DEFAULT; +unsigned int APECS_DMA_WIN_SIZE = APECS_DMA_WIN_SIZE_DEFAULT; +#endif /* SRM_SETUP */ /* * Given a bus, device, and function number, compute resulting @@ -194,7 +197,7 @@ static unsigned int conf_read(unsigned long addr, unsigned char type1) } /* reset error status: */ - *(vulp)APECS_IOC_DCSR = stat0; + *(vuip)APECS_IOC_DCSR = stat0; mb(); wrmces(0x7); /* reset machine check */ value = 0xffffffff; @@ -269,7 +272,7 @@ static void conf_write(unsigned long addr, unsigned int value, unsigned char typ } /* reset error status: */ - *(vulp)APECS_IOC_DCSR = stat0; + *(vuip)APECS_IOC_DCSR = stat0; mb(); wrmces(0x7); /* reset machine check */ } @@ -424,6 +427,38 @@ unsigned long apecs_init(unsigned long mem_start, unsigned long mem_end) *(vuip)APECS_IOC_TB2R = 0; #else /* CONFIG_ALPHA_XL */ +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 1 for enabled and mapped to 0 */ + if ((*(vuip)APECS_IOC_PB1R & (1U<<19)) && (*(vuip)APECS_IOC_TB1R == 0)) + { + APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB1R & 0xfff00000U; + APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM1R & 0xfff00000U; + APECS_DMA_WIN_SIZE += 0x00100000U; +#if 0 + printk("apecs_init: using Window 1 settings\n"); + printk("apecs_init: PB1R 0x%x PM1R 0x%x TB1R 0x%x\n", + *(vuip)APECS_IOC_PB1R, + *(vuip)APECS_IOC_PM1R, + *(vuip)APECS_IOC_TB1R); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if ((*(vuip)APECS_IOC_PB2R & (1U<<19)) && (*(vuip)APECS_IOC_TB2R == 0)) + { + APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB2R & 0xfff00000U; + APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM2R & 0xfff00000U; + APECS_DMA_WIN_SIZE += 0x00100000U; +#if 0 + printk("apecs_init: using Window 2 settings\n"); + printk("apecs_init: PB2R 0x%x PM2R 0x%x TB2R 0x%x\n", + *(vuip)APECS_IOC_PB2R, + *(vuip)APECS_IOC_PM2R, + *(vuip)APECS_IOC_TB2R); +#endif + } + else /* we must use our defaults... */ +#endif /* SRM_SETUP */ + { /* * Set up the PCI->physical memory translation windows. * For now, window 2 is disabled. In the future, we may @@ -435,9 +470,11 @@ unsigned long apecs_init(unsigned long mem_start, unsigned long mem_end) *(vuip)APECS_IOC_PB1R = 1U<<19 | (APECS_DMA_WIN_BASE & 0xfff00000U); *(vuip)APECS_IOC_PM1R = (APECS_DMA_WIN_SIZE - 1) & 0xfff00000U; *(vuip)APECS_IOC_TB1R = 0; + } #endif /* CONFIG_ALPHA_XL */ #ifdef CONFIG_ALPHA_CABRIOLET +#ifdef NO_LONGER_NEEDED_I_HOPE /* * JAE: HACK!!! for now, hardwire if configured... * davidm: Older miniloader versions don't set the clock frequency @@ -461,6 +498,7 @@ unsigned long apecs_init(unsigned long mem_start, unsigned long mem_end) sum += *l; hwrpb->chksum = sum; } +#endif /* NO_LONGER_NEEDED_I_HOPE */ #endif /* CONFIG_ALPHA_CABRIOLET */ /* @@ -483,15 +521,15 @@ unsigned long apecs_init(unsigned long mem_start, unsigned long mem_end) int apecs_pci_clr_err(void) { - apecs_jd = *(vulp)APECS_IOC_DCSR; + apecs_jd = *(vuip)APECS_IOC_DCSR; if (apecs_jd & 0xffe0L) { - apecs_jd1 = *(vulp)APECS_IOC_SEAR; - *(vulp)APECS_IOC_DCSR = apecs_jd | 0xffe1L; - apecs_jd = *(vulp)APECS_IOC_DCSR; + apecs_jd1 = *(vuip)APECS_IOC_SEAR; + *(vuip)APECS_IOC_DCSR = apecs_jd | 0xffe1L; + apecs_jd = *(vuip)APECS_IOC_DCSR; mb(); } - *(vulp)APECS_IOC_TBIA = APECS_IOC_TBIA; - apecs_jd2 = *(vulp)APECS_IOC_TBIA; + *(vuip)APECS_IOC_TBIA = (unsigned int)APECS_IOC_TBIA; + apecs_jd2 = *(vuip)APECS_IOC_TBIA; mb(); return 0; } diff --git a/arch/alpha/kernel/bios32.c b/arch/alpha/kernel/bios32.c index af8971834..46880f11e 100644 --- a/arch/alpha/kernel/bios32.c +++ b/arch/alpha/kernel/bios32.c @@ -25,6 +25,7 @@ */ #include <linux/config.h> #include <linux/kernel.h> +#include <linux/tasks.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/init.h> @@ -55,7 +56,6 @@ asmlinkage int sys_pciconfig_write() #else /* CONFIG_PCI */ -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/malloc.h> #include <linux/mm.h> @@ -63,6 +63,8 @@ asmlinkage int sys_pciconfig_write() #include <asm/hwrpb.h> #include <asm/io.h> #include <asm/uaccess.h> +#include <asm/segment.h> +#include <asm/system.h> #define KB 1024 @@ -70,7 +72,9 @@ asmlinkage int sys_pciconfig_write() #define GB (1024*MB) #define MAJOR_REV 0 -#define MINOR_REV 3 + +/* minor revision 4, add multi-PCI handling */ +#define MINOR_REV 4 /* * Align VAL to ALIGN, which must be a power of two. @@ -78,7 +82,20 @@ asmlinkage int sys_pciconfig_write() #define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1)) +#if defined(CONFIG_ALPHA_MCPCIA) || defined(CONFIG_ALPHA_TSUNAMI) +/* multiple PCI bus machines */ +/* make handle from bus number */ +extern struct linux_hose_info *bus2hose[256]; +#define HANDLE(b) (((unsigned long)(bus2hose[(b)]->pci_hose_index)&3)<<32) +#define DEV_IS_ON_PRIMARY(dev) \ + (bus2hose[(dev)->bus->number]->pci_first_busno == (dev)->bus->number) +#else /* MCPCIA || TSUNAMI */ +#define HANDLE(b) (0) +#define DEV_IS_ON_PRIMARY(dev) ((dev)->bus->number == 0) +#endif /* MCPCIA || TSUNAMI */ /* + * PCI_MODIFY + * * Temporary internal macro. If this 0, then do not write to any of * the PCI registers, merely read them (i.e., use configuration as * determined by SRM). The SRM seem do be doing a less than perfect @@ -95,7 +112,18 @@ asmlinkage int sys_pciconfig_write() * the graphics card---there have been some rumor that the #9 BIOS * incorrectly resets that address to 0...). */ +#ifdef CONFIG_ALPHA_SRM_SETUP +#define PCI_MODIFY 0 +static struct pci_dev *irq_dev_to_reset[16]; +static unsigned char irq_to_reset[16]; +static int irq_reset_count = 0; +static struct pci_dev *io_dev_to_reset[16]; +static unsigned char io_reg_to_reset[16]; +static unsigned int io_to_reset[16]; +static int io_reset_count = 0; +#else /* SRM_SETUP */ #define PCI_MODIFY 1 +#endif /* SRM_SETUP */ extern struct hwrpb_struct *hwrpb; @@ -103,9 +131,7 @@ extern struct hwrpb_struct *hwrpb; #if defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164) extern int SMC93x_Init(void); #endif -#ifdef CONFIG_ALPHA_SX164 extern int SMC669_Init(void); -#endif #ifdef CONFIG_ALPHA_MIATA static int es1888_init(void); #endif @@ -115,7 +141,7 @@ static int es1888_init(void); /* * NOTE: we can't just blindly use 64K for machines with EISA busses; they * may also have PCI-PCI bridges present, and then we'd configure the bridge - * incorrectly + * incorrectly. * * Also, we start at 0x8000 or 0x9000, in hopes to get all devices' * IO space areas allocated *before* 0xC000; this is because certain @@ -123,12 +149,17 @@ static int es1888_init(void); * accesses to probe the bus. If a device's registers appear at 0xC000, * it may see an INx/OUTx at that address during BIOS emulation of the * VGA BIOS, and some cards, notably Adaptec 2940UW, take mortal offense. + * + * Note that we may need this stuff for SRM_SETUP also, since certain + * SRM consoles screw up and allocate I/O space addresses > 64K behind + * PCI-to_PCI bridges, which can't pass I/O addresses larger than 64K, AFAIK. */ #if defined(CONFIG_ALPHA_EISA) -static unsigned int io_base = 0x9000; /* start above 8th slot */ +#define DEFAULT_IO_BASE 0x9000 /* start above 8th slot */ #else -static unsigned int io_base = 0x8000; +#define DEFAULT_IO_BASE 0x8000 /* start at 8th slot */ #endif +static unsigned int io_base; #if defined(CONFIG_ALPHA_XL) /* @@ -142,7 +173,7 @@ static unsigned int io_base = 0x8000; * We accept the risk that a broken Myrinet card will be put into a true XL * and thus can more easily run into the problem described below. */ -static unsigned int mem_base = 16*MB + 2*MB; /* 16M to 64M-1 is avail */ +#define DEFAULT_MEM_BASE (16*MB + 2*MB) /* 16M to 64M-1 is avail */ #elif defined(CONFIG_ALPHA_LCA) || defined(CONFIG_ALPHA_APECS) /* @@ -154,7 +185,7 @@ static unsigned int mem_base = 16*MB + 2*MB; /* 16M to 64M-1 is avail */ * However, APECS and LCA have only 34 bits for physical addresses, thus * limiting PCI bus memory addresses for SPARSE access to be less than 128Mb. */ -static unsigned int mem_base = 64*MB + 2*MB; +#define DEFAULT_MEM_BASE (64*MB + 2*MB) #else /* @@ -166,9 +197,10 @@ static unsigned int mem_base = 64*MB + 2*MB; * Because CIA and PYXIS and T2 have more bits for physical addresses, * they support an expanded range of SPARSE memory addresses. */ -static unsigned int mem_base = 128*MB + 16*MB; +#define DEFAULT_MEM_BASE (128*MB + 16*MB) #endif +static unsigned int mem_base; /* * Disable PCI device DEV so that it does not respond to I/O or memory @@ -179,7 +211,6 @@ static void disable_dev(struct pci_dev *dev) struct pci_bus *bus; unsigned short cmd; -#ifdef CONFIG_ALPHA_EISA /* * HACK: the PCI-to-EISA bridge does not seem to identify * itself as a bridge... :-( @@ -189,15 +220,17 @@ static void disable_dev(struct pci_dev *dev) DBG_DEVS(("disable_dev: ignoring PCEB...\n")); return; } -#endif -#ifdef CONFIG_ALPHA_SX164 + + /* + * we don't have code that will init the CYPRESS bridge correctly + * so we do the next best thing, and depend on the previous + * console code to do the right thing, and ignore it here... :-\ + */ if (dev->vendor == PCI_VENDOR_ID_CONTAQ && - /* FIXME: We want a symbolic device name here. */ - dev->device == 0xc693) { + dev->device == PCI_DEVICE_ID_CONTAQ_82C693) { DBG_DEVS(("disable_dev: ignoring CYPRESS bridge...\n")); return; } -#endif bus = dev->bus; pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd); @@ -211,16 +244,16 @@ static void disable_dev(struct pci_dev *dev) /* * Layout memory and I/O for a device: */ -#define MAX(val1, val2) ((val1) > (val2) ? val1 : val2) +#define MAX(val1, val2) ((val1) > (val2) ? (val1) : (val2)) static void layout_dev(struct pci_dev *dev) { struct pci_bus *bus; unsigned short cmd; - unsigned int base, mask, size, reg; + unsigned int base, mask, size, off, idx; unsigned int alignto; + unsigned long handle; -#ifdef CONFIG_ALPHA_EISA /* * HACK: the PCI-to-EISA bridge does not seem to identify * itself as a bridge... :-( @@ -230,32 +263,40 @@ static void layout_dev(struct pci_dev *dev) DBG_DEVS(("layout_dev: ignoring PCEB...\n")); return; } -#endif -#ifdef CONFIG_ALPHA_SX164 + + /* + * we don't have code that will init the CYPRESS bridge correctly + * so we do the next best thing, and depend on the previous + * console code to do the right thing, and ignore it here... :-\ + */ if (dev->vendor == PCI_VENDOR_ID_CONTAQ && - dev->device == 0xc693) { + dev->device == PCI_DEVICE_ID_CONTAQ_82C693) { DBG_DEVS(("layout_dev: ignoring CYPRESS bridge...\n")); return; } -#endif bus = dev->bus; pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd); - for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + for (idx = 0; idx <= 5; idx++) { + off = PCI_BASE_ADDRESS_0 + 4*idx; /* * Figure out how much space and of what type this * device wants. */ - pcibios_write_config_dword(bus->number, dev->devfn, reg, + pcibios_write_config_dword(bus->number, dev->devfn, off, 0xffffffff); - pcibios_read_config_dword(bus->number, dev->devfn, reg, &base); + pcibios_read_config_dword(bus->number, dev->devfn, off, &base); if (!base) { /* this base-address register is unused */ - dev->base_address[(reg - PCI_BASE_ADDRESS_0)>>2] = 0; + dev->base_address[idx] = 0; continue; } + DBG_DEVS(("layout_dev: slot %d fn %d off 0x%x base 0x%x\n", + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + off, base)); + /* * We've read the base address register back after * writing all ones and so now we must decode it. @@ -281,11 +322,13 @@ static void layout_dev(struct pci_dev *dev) base = ALIGN(io_base, alignto); io_base = base + size; pcibios_write_config_dword(bus->number, dev->devfn, - reg, base | 0x1); - dev->base_address[(reg - PCI_BASE_ADDRESS_0)>>2] - = base | 0x1; - DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%x (0x%x)\n", - dev->device, base, size)); + off, base | 0x1); + + handle = HANDLE(bus->number) | base | 1; + dev->base_address[idx] = handle; + + DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%lx (0x%x)\n", + dev->device, handle, size)); } else { unsigned int type; /* @@ -306,7 +349,7 @@ static void layout_dev(struct pci_dev *dev) "slot %d, function %d: \n", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - reg += 4; /* skip extra 4 bytes */ + idx++; /* skip extra 4 bytes */ continue; case PCI_BASE_ADDRESS_MEM_TYPE_1M: @@ -365,10 +408,11 @@ static void layout_dev(struct pci_dev *dev) } mem_base = base + size; pcibios_write_config_dword(bus->number, dev->devfn, - reg, base); - dev->base_address[(reg-PCI_BASE_ADDRESS_0)>>2] = base; - DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%x (0x%x)\n", - dev->device, base, size)); + off, base); + handle = HANDLE(bus->number) | base; + dev->base_address[idx] = handle; + DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%lx (0x%x)\n", + dev->device, handle, size)); } } @@ -397,16 +441,17 @@ static void layout_dev(struct pci_dev *dev) } -static void layout_bus(struct pci_bus *bus) +static int layout_bus(struct pci_bus *bus) { unsigned int l, tio, bio, tmem, bmem; struct pci_bus *child; struct pci_dev *dev; + int found_vga = 0; DBG_DEVS(("layout_bus: starting bus %d\n", bus->number)); if (!bus->devices && !bus->children) - return; + return 0; /* * Align the current bases on appropriate boundaries (4K for @@ -424,6 +469,8 @@ static void layout_bus(struct pci_bus *bus) * devices. They'll be re-enabled only once all address * decoders are programmed consistently. */ + DBG_DEVS(("layout_bus: disable_dev for bus %d\n", bus->number)); + for (dev = bus->devices; dev; dev = dev->sibling) { if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) || (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) { @@ -441,6 +488,8 @@ static void layout_bus(struct pci_bus *bus) (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) { layout_dev(dev); } + if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) + found_vga = 1; } /* * Recursively allocate space for all of the sub-buses: @@ -448,7 +497,7 @@ static void layout_bus(struct pci_bus *bus) DBG_DEVS(("layout_bus: starting bus %d children\n", bus->number)); for (child = bus->children; child; child = child->next) { - layout_bus(child); + found_vga += layout_bus(child); } /* * Align the current bases on 4K and 1MB boundaries: @@ -458,6 +507,8 @@ static void layout_bus(struct pci_bus *bus) if (bus->self) { struct pci_dev *bridge = bus->self; + + DBG_DEVS(("layout_bus: config bus %d bridge\n", bus->number)); /* * Set up the top and bottom of the PCI I/O segment * for this bus. @@ -481,10 +532,13 @@ static void layout_bus(struct pci_bus *bus) pcibios_write_config_dword(bridge->bus->number, bridge->devfn, 0x24, 0x0000ffff); /* - * Tell bridge that there is an ISA bus in the system: + * Tell bridge that there is an ISA bus in the system, + * and (possibly) a VGA as well. */ + l = 0x00040000; /* ISA present */ + if (found_vga) l |= 0x00080000; /* VGA present */ pcibios_write_config_dword(bridge->bus->number, bridge->devfn, - 0x3c, 0x00040000); + 0x3c, l); /* * Clear status bits, enable I/O (for downstream I/O), * turn on master enable (for upstream I/O), turn on @@ -494,75 +548,26 @@ static void layout_bus(struct pci_bus *bus) pcibios_write_config_dword(bridge->bus->number, bridge->devfn, 0x4, 0xffff0007); } + DBG_DEVS(("layout_bus: bus %d finished\n", bus->number)); + return found_vga; } #endif /* !PCI_MODIFY */ -/* - * Given the vendor and device ids, find the n'th instance of that device - * in the system. - */ -int pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - - -/* - * Given the class, find the n'th instance of that device - * in the system. - */ -int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - - int pcibios_present(void) { return 1; } -unsigned long pcibios_init(unsigned long mem_start, - unsigned long mem_end) +void __init +pcibios_init(void) { printk("Alpha PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV); - #if !PCI_MODIFY printk("...NOT modifying existing (SRM) PCI configuration\n"); #endif - return mem_start; } /* @@ -633,6 +638,76 @@ bridge_swizzle(unsigned char pin, unsigned int slot) return (((pin-1) + slot) % 4) + 1; } +#ifdef CONFIG_ALPHA_SRM_SETUP +/* look for mis-configured devices' I/O space addresses behind bridges */ +static void check_behind_io(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + unsigned int reg, orig_base, new_base, found_one = 0; + + for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + /* read the current setting, check for I/O space and >= 64K */ + pcibios_read_config_dword(bus->number, dev->devfn, reg, &orig_base); + if (!orig_base || !(orig_base & PCI_BASE_ADDRESS_SPACE_IO)) + continue; /* unused or non-IO */ + if (orig_base < 64*1024) { +#if 1 +printk("check_behind_io: ALREADY OK! bus %d slot %d base 0x%x\n", + bus->number, PCI_SLOT(dev->devfn), orig_base); +#endif + if (orig_base & ~1) + continue; /* OK! */ + orig_base = 0x12001; /* HACK! FIXME!! */ + } + + /* HACK ALERT! for now, just subtract 32K from the + original address, which should give us addresses + in the range 0x8000 and up */ + new_base = orig_base - 0x8000; +#if 1 +printk("check_behind_io: ALERT! bus %d slot %d old 0x%x new 0x%x\n", + bus->number, PCI_SLOT(dev->devfn), orig_base, new_base); +#endif + pcibios_write_config_dword(bus->number, dev->devfn, + reg, new_base); + + io_dev_to_reset[io_reset_count] = dev; + io_reg_to_reset[io_reset_count] = reg; + io_to_reset[io_reset_count] = orig_base; + io_reset_count++; + found_one++; + } /* end for-loop */ + + /* if any were modified, gotta hack the bridge IO limits too... */ + if (found_one) { + if (bus->self) { + struct pci_dev *bridge = bus->self; + unsigned int l; + /* + * Set up the top and bottom of the PCI I/O segment + * for this bus. + */ + pcibios_read_config_dword(bridge->bus->number, + bridge->devfn, 0x1c, &l); +#if 1 +printk("check_behind_io: ALERT! bus %d slot %d oldLIM 0x%x\n", + bus->number, PCI_SLOT(bridge->devfn), l); +#endif + l = (l & 0xffff0000U) | 0xf080U; /* give it ALL */ + pcibios_write_config_dword(bridge->bus->number, + bridge->devfn, 0x1c, l); + pcibios_write_config_dword(bridge->bus->number, + bridge->devfn, + 0x3c, 0x00040000); + pcibios_write_config_dword(bridge->bus->number, + bridge->devfn, + 0x4, 0xffff0007); + } else + printk("check_behind_io: WARNING! bus->self NULL\n"); + } +} +#endif /* CONFIG_ALPHA_SRM_SETUP */ + /* * Most evaluation boards share most of the fixup code, which is isolated * here. This function is declared "inline" as only one platform will ever @@ -644,7 +719,7 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, char irq_tab[max_idsel - min_idsel + 1][irqs_per_slot], long ide_base) { - struct pci_dev *dev; + struct pci_dev *dev, *curr; unsigned char pin; unsigned char slot; @@ -652,12 +727,18 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, * Go through all devices, fixing up irqs as we see fit: */ for (dev = pci_devices; dev; dev = dev->next) { - if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE - /* PCEB (PCI to EISA bridge) does not identify - itself as a bridge... :-P */ - && !(dev->vendor == PCI_VENDOR_ID_INTEL && - dev->device == PCI_DEVICE_ID_INTEL_82375)) - || dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) { + if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE || + dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) { + /* + * HACK: the PCI-to-EISA bridge appears not to identify + * itself as a bridge... :-( + */ + if (dev->vendor == PCI_VENDOR_ID_INTEL && + dev->device == PCI_DEVICE_ID_INTEL_82375) { + DBG_DEVS(("common_fixup: ignoring PCEB...\n")); + continue; + } + /* * This device is not on the primary bus, we need * to figure out which interrupt pin it will come @@ -668,45 +749,100 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, * the inline static routine above). */ dev->irq = 0; - if (dev->bus->number != 0) { - struct pci_dev *curr = dev; + if (!DEV_IS_ON_PRIMARY(dev)) { /* read the pin and do the PCI-PCI bridge interrupt pin swizzle */ pcibios_read_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, &pin); - /* cope with 0 */ - if (pin == 0) + /* cope with 0 and illegal */ + if (pin == 0 || pin > 4) pin = 1; /* follow the chain of bridges, swizzling as we go */ + curr = dev; #if defined(CONFIG_ALPHA_MIATA) + /* check first for the built-in bridge */ + if ((PCI_SLOT(dev->bus->self->devfn) == 8) || + (PCI_SLOT(dev->bus->self->devfn) == 20)) { slot = PCI_SLOT(dev->devfn) + 5; DBG_DEVS(("MIATA: bus 1 slot %d pin %d" " irq %d min_idsel %d\n", PCI_SLOT(dev->devfn), pin, irq_tab[slot - min_idsel][pin], min_idsel)); + } + else /* must be a card-based bridge */ + { + do { + if ((PCI_SLOT(curr->bus->self->devfn) == 8) || + (PCI_SLOT(curr->bus->self->devfn) == 20)) + { + slot = PCI_SLOT(curr->devfn) + 5; + break; + } + /* swizzle */ + pin = bridge_swizzle( + pin, PCI_SLOT(curr->devfn)) ; + /* move up the chain of bridges */ + curr = curr->bus->self ; + /* slot of the next bridge. */ + slot = PCI_SLOT(curr->devfn); + } while (curr->bus->self) ; + } #elif defined(CONFIG_ALPHA_NORITAKE) - /* WAG Alert! */ - slot = PCI_SLOT(dev->devfn) + 14; + /* check first for the built-in bridge */ + if (PCI_SLOT(dev->bus->self->devfn) == 8) { + slot = PCI_SLOT(dev->devfn) + 15; /* WAG! */ DBG_DEVS(("NORITAKE: bus 1 slot %d pin %d" - " irq %d min_idsel %d\n", + "irq %d min_idsel %ld\n", PCI_SLOT(dev->devfn), pin, irq_tab[slot - min_idsel][pin], min_idsel)); -#else + } + else /* must be a card-based bridge */ + { do { + if (PCI_SLOT(curr->bus->self->devfn) == 8) { + slot = PCI_SLOT(curr->devfn) + 15; + break; + } /* swizzle */ - pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)); + pin = bridge_swizzle( + pin, PCI_SLOT(curr->devfn)) ; + /* move up the chain of bridges */ + curr = curr->bus->self ; + /* slot of the next bridge. */ + slot = PCI_SLOT(curr->devfn); + } while (curr->bus->self) ; + } +#else /* everyone but MIATA and NORITAKE */ + DBG_DEVS(("common_fixup: bus %d slot %d pin %d " + "irq %d min_idsel %ld\n", + curr->bus->number, + PCI_SLOT(dev->devfn), pin, + irq_tab[slot - min_idsel][pin], + min_idsel)); + do { + /* swizzle */ + pin = + bridge_swizzle(pin, PCI_SLOT(curr->devfn)); /* move up the chain of bridges */ curr = curr->bus->self; } while (curr->bus->self); /* The slot is the slot of the last bridge. */ slot = PCI_SLOT(curr->devfn); -#endif /* MIATA */ - } else { +#endif +#ifdef CONFIG_ALPHA_SRM_SETUP + /* + * must make sure that SRM didn't screw up + * and allocate an address > 64K for I/O + * space behind a PCI-PCI bridge + */ + check_behind_io(dev); +#endif /* CONFIG_ALPHA_SRM_SETUP */ + } else { /* just a device on a primary bus */ /* work out the slot */ slot = PCI_SLOT(dev->devfn); /* read the pin */ @@ -714,16 +850,48 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, dev->devfn, PCI_INTERRUPT_PIN, &pin); + DBG_DEVS(("common_fixup: bus %d slot %d" + " pin %d irq %d min_idsel %ld\n", + dev->bus->number, slot, pin, + irq_tab[slot - min_idsel][pin], + min_idsel)); + /* cope with 0 and illegal */ + if (pin == 0 || pin > 4) + pin = 1; } if (irq_tab[slot - min_idsel][pin] != -1) dev->irq = irq_tab[slot - min_idsel][pin]; -#if PCI_MODIFY - /* tell the device: */ - pcibios_write_config_byte(dev->bus->number, +#ifdef CONFIG_ALPHA_RAWHIDE + dev->irq += + 24 * bus2hose[dev->bus->number]->pci_hose_index; +#endif /* RAWHIDE */ +#ifdef CONFIG_ALPHA_SRM + { + unsigned char irq_orig; + /* read the original SRM-set IRQ and tell */ + pcibios_read_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_LINE, - dev->irq); -#endif + &irq_orig); + if (irq_orig != dev->irq) { + DBG_DEVS(("common_fixup: bus %d slot 0x%x " + "SRM IRQ 0x%x changed to 0x%x\n", + dev->bus->number,PCI_SLOT(dev->devfn), + irq_orig, dev->irq)); +#ifdef CONFIG_ALPHA_SRM_SETUP + irq_dev_to_reset[irq_reset_count] = dev; + irq_to_reset[irq_reset_count] = irq_orig; + irq_reset_count++; +#endif /* CONFIG_ALPHA_SRM_SETUP */ + } + } +#endif /* SRM */ + + /* always tell the device, so the driver knows what is + * the real IRQ to use; the device does not use it. + */ + pcibios_write_config_byte(dev->bus->number, dev->devfn, + PCI_INTERRUPT_LINE, dev->irq); DBG_DEVS(("common_fixup: bus %d slot 0x%x" " VID 0x%x DID 0x%x\n" @@ -737,11 +905,24 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, * if it's a VGA, enable its BIOS ROM at C0000 */ if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { - pcibios_write_config_dword(dev->bus->number, + /* but if its a Cirrus 543x/544x DISABLE it, */ + /* since enabling ROM disables the memory... */ + if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) && + (dev->device >= 0x00a0) && + (dev->device <= 0x00ac)) { + pcibios_write_config_dword( + dev->bus->number, + dev->devfn, + PCI_ROM_ADDRESS, + 0x00000000); + } else { + pcibios_write_config_dword( + dev->bus->number, dev->devfn, PCI_ROM_ADDRESS, 0x000c0000 | PCI_ROM_ADDRESS_ENABLE); } + } /* * if it's a SCSI, disable its BIOS ROM */ @@ -752,46 +933,6 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, 0x0000000); } } -#ifdef CONFIG_ALPHA_SX164 - /* If it the CYPRESS PCI-ISA bridge, disable IDE - interrupt routing through PCI (ie do through PIC). */ - else if (dev->vendor == PCI_VENDOR_ID_CONTAQ && - dev->device == 0xc693 && - PCI_FUNC(dev->devfn) == 0) { - pcibios_write_config_word(dev->bus->number, - dev->devfn, 0x04, 0x0007); - - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x40, 0x80); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x41, 0x80); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x42, 0x80); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x43, 0x80); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x44, 0x27); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x45, 0xe0); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x48, 0xf0); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x49, 0x40); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x4a, 0x00); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x4b, 0x80); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x4c, 0x80); - pcibios_write_config_byte(dev->bus->number, - dev->devfn, 0x4d, 0x70); - - outb(0, DMA1_RESET_REG); - outb(0, DMA2_RESET_REG); - outb(DMA_MODE_CASCADE, DMA2_MODE_REG); - outb(0, DMA2_MASK_REG); - } -#endif /* SX164 */ } if (ide_base) { enable_ide(ide_base); @@ -814,6 +955,7 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, static inline void eb66p_fixup(void) { static char irq_tab[5][5] __initlocaldata = { + /*INT INTA INTB INTC INTD */ {16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J25 */ {16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J26 */ { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ @@ -825,8 +967,8 @@ static inline void eb66p_fixup(void) /* - * The PC164/LX164 has 19 PCI interrupts, four from each of the four PCI - * slots, the SIO, PCI/IDE, and USB. + * The PC164 and LX164 have 19 PCI interrupts, four from each of the four + * PCI slots, the SIO, PCI/IDE, and USB. * * Each of the interrupts can be individually masked. This is * accomplished by setting the appropriate bit in the mask register. @@ -901,6 +1043,7 @@ static inline void alphapc164_fixup(void) static inline void cabriolet_fixup(void) { static char irq_tab[5][5] __initlocaldata = { + /*INT INTA INTB INTC INTD */ { 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5, slot 2, J21 */ { 16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J19 */ { 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J20 */ @@ -957,6 +1100,7 @@ static inline void cabriolet_fixup(void) static inline void eb66_and_eb64p_fixup(void) { static char irq_tab[5][5] __initlocaldata = { + /*INT INTA INTB INTC INTD */ {16+7, 16+7, 16+7, 16+7, 16+7}, /* IdSel 5, slot ?, ?? */ {16+0, 16+0, 16+2, 16+4, 16+9}, /* IdSel 6, slot ?, ?? */ {16+1, 16+1, 16+3, 16+8, 16+10}, /* IdSel 7, slot ?, ?? */ @@ -968,7 +1112,7 @@ static inline void eb66_and_eb64p_fixup(void) /* - * Fixup configuration for MIKASA (NORITAKE is different) + * Fixup configuration for MIKASA (AlphaServer 1000) * * Summary @ 0x536: * Bit Meaning @@ -1020,7 +1164,10 @@ static inline void mikasa_fixup(void) } /* - * Fixup configuration for NORITAKE (MIKASA is different) + * Fixup configuration for NORITAKE (AlphaServer 1000A) + * + * This is also used for CORELLE (AlphaServer 800) + * and ALCOR Primo (AlphaStation 600A). * * Summary @ 0x542, summary register #1: * Bit Meaning @@ -1076,8 +1223,11 @@ static inline void mikasa_fixup(void) */ static inline void noritake_fixup(void) { - static char irq_tab[13][5] __initlocaldata = { + static char irq_tab[15][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ + /* note: IDSELs 16, 17, and 25 are CORELLE only */ + { 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */ + { -1, -1, -1, -1, -1}, /* IdSel 17, S3 Trio64 */ { -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */ { -1, -1, -1, -1, -1}, /* IdSel 19, PPB */ { -1, -1, -1, -1, -1}, /* IdSel 20, ???? */ @@ -1085,18 +1235,20 @@ static inline void noritake_fixup(void) { 16+2, 16+2, 16+3, 32+2, 32+3}, /* IdSel 22, slot 0 */ { 16+4, 16+4, 16+5, 32+4, 32+5}, /* IdSel 23, slot 1 */ { 16+6, 16+6, 16+7, 32+6, 32+7}, /* IdSel 24, slot 2 */ - /* The following are actually on bus 1, across the bridge */ + { 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 25, slot 3 */ + /* the following 5 are actually on PCI bus 1, which is */ + /* across the built-in bridge of the NORITAKE only */ { 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */ { 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 17, slot 3 */ {16+10, 16+10, 16+11, 32+10, 32+11}, /* IdSel 18, slot 4 */ {16+12, 16+12, 16+13, 32+12, 32+13}, /* IdSel 19, slot 5 */ {16+14, 16+14, 16+15, 32+14, 32+15}, /* IdSel 20, slot 6 */ }; - common_fixup(7, 18, 5, irq_tab, 0); + common_fixup(5, 19, 5, irq_tab, 0); } /* - * Fixup configuration for ALCOR + * Fixup configuration for ALCOR and XLT (XL-300/366/433) * * Summary @ GRU_INT_REQ: * Bit Meaning @@ -1126,6 +1278,7 @@ static inline void noritake_fixup(void) * The device to slot mapping looks like: * * Slot Device + * 6 built-in TULIP (XLT only) * 7 PCI on board slot 0 * 8 PCI on board slot 3 * 9 PCI on board slot 4 @@ -1140,8 +1293,10 @@ static inline void noritake_fixup(void) */ static inline void alcor_fixup(void) { - static char irq_tab[6][5] __initlocaldata = { + static char irq_tab[7][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ + /* note: IDSEL 17 is XLT only */ + {16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */ { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 19, slot 3 */ {16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 20, slot 4 */ @@ -1149,62 +1304,6 @@ static inline void alcor_fixup(void) { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */ { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ }; - common_fixup(7, 12, 5, irq_tab, 0); -} - -/* - * Fixup configuration for ALPHA XLT (EV5/EV56) - * - * Summary @ GRU_INT_REQ: - * Bit Meaning - * 0 Interrupt Line A from slot 2 - * 1 Interrupt Line B from slot 2 - * 2 Interrupt Line C from slot 2 - * 3 Interrupt Line D from slot 2 - * 4 Interrupt Line A from slot 1 - * 5 Interrupt line B from slot 1 - * 6 Interrupt Line C from slot 1 - * 7 Interrupt Line D from slot 1 - * 8 Interrupt Line A from slot 0 - * 9 Interrupt Line B from slot 0 - *10 Interrupt Line C from slot 0 - *11 Interrupt Line D from slot 0 - *12 NCR810 SCSI in slot 9 - *13 DC-21040 (TULIP) in slot 6 - *14-19 Reserved - *20-23 Jumpers (interrupt) - *24-27 Module revision - *28-30 Reserved - *31 EISA interrupt - * - * The device to slot mapping looks like: - * - * Slot Device - * 6 TULIP - * 7 PCI on board slot 0 - * 8 none - * 9 SCSI - * 10 PCI-ISA bridge - * 11 PCI on board slot 2 - * 12 PCI on board slot 1 - * - * - * This two layered interrupt approach means that we allocate IRQ 16 and - * above for PCI interrupts. The IRQ relates to which bit the interrupt - * comes in on. This makes interrupt processing much easier. - */ -static inline void xlt_fixup(void) -{ - static char irq_tab[7][5] __initlocaldata = { - /*INT INTA INTB INTC INTD */ - {16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */ - { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ - { -1, -1, -1, -1, -1}, /* IdSel 19, none */ - {16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 20, SCSI */ - { -1, -1, -1, -1, -1}, /* IdSel 21, SIO */ - { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */ - { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ - }; common_fixup(6, 12, 5, irq_tab, 0); } @@ -1262,8 +1361,6 @@ static inline void xlt_fixup(void) * with the values in the sable_irq_to_mask[] and sable_mask_to_irq[] tables * in irq.c */ - -#ifdef CONFIG_ALPHA_SABLE static inline void sable_fixup(void) { static char irq_tab[9][5] __initlocaldata = { @@ -1280,7 +1377,6 @@ static inline void sable_fixup(void) }; common_fixup(0, 8, 5, irq_tab, 0); } -#endif /* * Fixup configuration for MIATA (EV56+PYXIS) @@ -1362,7 +1458,8 @@ static inline void miata_fixup(void) { -1, -1, -1, -1, -1}, /* IdSel 21, none */ {16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 22, slot 4 */ {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 23, slot 5 */ - /* The following are actually on bus 1, across the bridge */ + /* The following are actually on bus 1, which is */ + /* across the builtin PCI-PCI bridge */ {16+20, 16+20, 16+21, 16+22, 16+23}, /* IdSel 24, slot 1 */ {16+24, 16+24, 16+25, 16+26, 16+27}, /* IdSel 25, slot 2 */ {16+28, 16+28, 16+29, 16+30, 16+31}, /* IdSel 26, slot 3 */ @@ -1373,6 +1470,7 @@ static inline void miata_fixup(void) { -1, -1, -1, -1, -1}, /* IdSel 31, PCI-PCI */ }; common_fixup(3, 20, 5, irq_tab, 0); + SMC669_Init(); /* it might be a GL (fails harmlessly if not) */ es1888_init(); } #endif @@ -1399,7 +1497,6 @@ static inline void miata_fixup(void) *14 Interrupt Line B from slot 1 *15 Interrupt line B from slot 0 *16 Interrupt Line C from slot 3 - *17 Interrupt Line C from slot 2 *18 Interrupt Line C from slot 1 *19 Interrupt Line C from slot 0 @@ -1417,7 +1514,6 @@ static inline void miata_fixup(void) * */ -#ifdef CONFIG_ALPHA_SX164 static inline void sx164_fixup(void) { static char irq_tab[5][5] __initlocaldata = { @@ -1428,12 +1524,154 @@ static inline void sx164_fixup(void) { -1, -1, -1, -1, -1}, /* IdSel 8 SIO */ { 16+ 8, 16+ 8, 16+12, 16+16, 16+20} /* IdSel 9 slot 3 J15 */ }; - common_fixup(5, 9, 5, irq_tab, 0); + SMC669_Init(); +} + +/* + * Fixup configuration for DP264 (EV6+TSUNAMI) + * + * Summary @ TSUNAMI_CSR_DIM0: + * Bit Meaning + * 0-17 Unused + *18 Interrupt SCSI B (Adaptec 7895 builtin) + *19 Interrupt SCSI A (Adaptec 7895 builtin) + *20 Interrupt Line D from slot 2 PCI0 + *21 Interrupt Line C from slot 2 PCI0 + *22 Interrupt Line B from slot 2 PCI0 + *23 Interrupt Line A from slot 2 PCI0 + *24 Interrupt Line D from slot 1 PCI0 + *25 Interrupt Line C from slot 1 PCI0 + *26 Interrupt Line B from slot 1 PCI0 + *27 Interrupt Line A from slot 1 PCI0 + *28 Interrupt Line D from slot 0 PCI0 + *29 Interrupt Line C from slot 0 PCI0 + *30 Interrupt Line B from slot 0 PCI0 + *31 Interrupt Line A from slot 0 PCI0 + * + *32 Interrupt Line D from slot 3 PCI1 + *33 Interrupt Line C from slot 3 PCI1 + *34 Interrupt Line B from slot 3 PCI1 + *35 Interrupt Line A from slot 3 PCI1 + *36 Interrupt Line D from slot 2 PCI1 + *37 Interrupt Line C from slot 2 PCI1 + *38 Interrupt Line B from slot 2 PCI1 + *39 Interrupt Line A from slot 2 PCI1 + *40 Interrupt Line D from slot 1 PCI1 + *41 Interrupt Line C from slot 1 PCI1 + *42 Interrupt Line B from slot 1 PCI1 + *43 Interrupt Line A from slot 1 PCI1 + *44 Interrupt Line D from slot 0 PCI1 + *45 Interrupt Line C from slot 0 PCI1 + *46 Interrupt Line B from slot 0 PCI1 + *47 Interrupt Line A from slot 0 PCI1 + *48-52 Unused + *53 PCI0 NMI (from Cypress) + *54 PCI0 SMI INT (from Cypress) + *55 PCI0 ISA Interrupt (from Cypress) + *56-60 Unused + *61 PCI1 Bus Error + *62 PCI0 Bus Error + *63 Reserved + * + * IdSel + * 5 Cypress Bridge I/O + * 6 SCSI Adaptec builtin + * 7 64 bit PCI option slot 0 + * 8 64 bit PCI option slot 1 + * 9 64 bit PCI option slot 2 + * + */ +static inline void dp264_fixup(void) +{ + static char irq_tab[5][5] __initlocaldata = { + /*INT INTA INTB INTC INTD */ + { -1, -1, -1, -1, -1}, /* IdSel 5 ISA Bridge */ + { 16+ 2, 16+ 2, 16+ 2, 16+ 2, 16+ 2}, /* IdSel 6 SCSI builtin */ + { 16+15, 16+15, 16+14, 16+13, 16+12}, /* IdSel 7 slot 0 */ + { 16+11, 16+11, 16+10, 16+ 9, 16+ 8}, /* IdSel 8 slot 1 */ + { 16+ 7, 16+ 7, 16+ 6, 16+ 5, 16+ 4} /* IdSel 9 slot 2 */ + }; + common_fixup(5, 9, 5, irq_tab, 0); SMC669_Init(); } -#endif + +/* + * Fixup configuration for RAWHIDE + * + * Summary @ MCPCIA_PCI0_INT_REQ: + * Bit Meaning + *0 Interrupt Line A from slot 2 PCI0 + *1 Interrupt Line B from slot 2 PCI0 + *2 Interrupt Line C from slot 2 PCI0 + *3 Interrupt Line D from slot 2 PCI0 + *4 Interrupt Line A from slot 3 PCI0 + *5 Interrupt Line B from slot 3 PCI0 + *6 Interrupt Line C from slot 3 PCI0 + *7 Interrupt Line D from slot 3 PCI0 + *8 Interrupt Line A from slot 4 PCI0 + *9 Interrupt Line B from slot 4 PCI0 + *10 Interrupt Line C from slot 4 PCI0 + *11 Interrupt Line D from slot 4 PCI0 + *12 Interrupt Line A from slot 5 PCI0 + *13 Interrupt Line B from slot 5 PCI0 + *14 Interrupt Line C from slot 5 PCI0 + *15 Interrupt Line D from slot 5 PCI0 + *16 EISA interrupt (PCI 0) or SCSI interrupt (PCI 1) + *17-23 NA + * + * IdSel + * 1 EISA bridge (PCI bus 0 only) + * 2 PCI option slot 2 + * 3 PCI option slot 3 + * 4 PCI option slot 4 + * 5 PCI option slot 5 + * + */ + +static inline void rawhide_fixup(void) +{ + static char irq_tab[5][5] __initlocaldata = { + /*INT INTA INTB INTC INTD */ + { 16+16, 16+16, 16+16, 16+16, 16+16}, /* IdSel 1 SCSI PCI 1 only */ + { 16+ 0, 16+ 0, 16+ 1, 16+ 2, 16+ 3}, /* IdSel 2 slot 2 */ + { 16+ 4, 16+ 4, 16+ 5, 16+ 6, 16+ 7}, /* IdSel 3 slot 3 */ + { 16+ 8, 16+ 8, 16+ 9, 16+10, 16+11}, /* IdSel 4 slot 4 */ + { 16+12, 16+12, 16+13, 16+14, 16+15} /* IdSel 5 slot 5 */ + }; + common_fixup(1, 5, 5, irq_tab, 0); +} + +/* + * The Takara has PCI devices 1, 2, and 3 configured to slots 20, + * 19, and 18 respectively, in the default configuration. They can + * also be jumpered to slots 8, 7, and 6 respectively, which is fun + * because the SIO ISA bridge can also be slot 7. However, the SIO + * doesn't explicitly generate PCI-type interrupts, so we can + * assign it whatever the hell IRQ we like and it doesn't matter. + */ +static inline void takara_fixup(void) +{ + static char irq_tab[15][5] __initlocaldata = { + { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 6 == device 3 */ + { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 7 == device 2 */ + { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 8 == device 1 */ + { -1, -1, -1, -1, -1}, /* slot 9 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 10 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 11 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 12 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 13 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 14 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 15 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 16 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 17 == nothing */ + { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 18 == device 3 */ + { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 19 == device 2 */ + { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 20 == device 1 */ + }; + common_fixup(6, 20, 5, irq_tab, 0x26e); +} /* * Fixup configuration for all boards that route the PCI interrupts @@ -1462,6 +1700,7 @@ static inline void sio_fixup(void) * driven at all). */ static const char pirq_tab[][5] __initlocaldata = { + /*INT A B C D */ #ifdef CONFIG_ALPHA_P2K { 0, 0, -1, -1, -1}, /* idsel 6 (53c810) */ {-1, -1, -1, -1, -1}, /* idsel 7 (SIO: PCI/ISA bridge) */ @@ -1497,7 +1736,7 @@ static inline void sio_fixup(void) #if defined(CONFIG_ALPHA_BOOK1) /* for the AlphaBook1, NCR810 SCSI is 14, PCMCIA controller is 15 */ - const unsigned int route_tab = 0x0e0f0a0a; + const unsigned int new_route_tab = 0x0e0f0a0a; #elif defined(CONFIG_ALPHA_NONAME) /* @@ -1510,16 +1749,24 @@ static inline void sio_fixup(void) * they are co-indicated when the platform type "Noname" is * selected... :-( */ - const unsigned int route_tab = 0x0b0a0f09; + const unsigned int new_route_tab = 0x0b0a0f09; #else - const unsigned int route_tab = 0x0b0a090f; + const unsigned int new_route_tab = 0x0b0a090f; #endif - - unsigned int level_bits; + unsigned int route_tab, old_route_tab; + unsigned int level_bits, old_level_bits; unsigned char pin, slot; int pirq; + pcibios_read_config_dword(0, PCI_DEVFN(7, 0), 0x60, &old_route_tab); + DBG_DEVS(("sio_fixup: old pirq route table: 0x%08x\n", + old_route_tab)); +#if PCI_MODIFY + route_tab = new_route_tab; pcibios_write_config_dword(0, PCI_DEVFN(7, 0), 0x60, route_tab); +#else + route_tab = old_route_tab; +#endif /* * Go through all devices, fixing up irqs as we see fit: @@ -1576,20 +1823,33 @@ static inline void sio_fixup(void) * if it's a VGA, enable its BIOS ROM at C0000 */ if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { - pcibios_write_config_dword(dev->bus->number, + /* but if its a Cirrus 543x/544x DISABLE it, */ + /* since enabling ROM disables the memory... */ + if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) && + (dev->device >= 0x00a0) && + (dev->device <= 0x00ac)) { + pcibios_write_config_dword( + dev->bus->number, + dev->devfn, + PCI_ROM_ADDRESS, + 0x00000000); + } else { + pcibios_write_config_dword( + dev->bus->number, dev->devfn, PCI_ROM_ADDRESS, 0x000c0000 | PCI_ROM_ADDRESS_ENABLE); } + } if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { continue; /* for now, displays get no IRQ */ } if (pirq < 0) { - printk("bios32.sio_fixup: " + DBG_DEVS(("bios32.sio_fixup: " "weird, device %04x:%04x coming in on" " slot %d has no irq line!!\n", - dev->vendor, dev->device, slot); + dev->vendor, dev->device, slot)); continue; } @@ -1653,7 +1913,12 @@ static inline void sio_fixup(void) * * Note: we at least preserve any level-set bits on AlphaBook1 */ - level_bits |= ((inb(0x4d0) | (inb(0x4d1) << 8)) & 0x71ff); + old_level_bits = inb(0x4d0) | (inb(0x4d1) << 8); + DBG_DEVS(("sio_fixup: old irq level bits: 0x%04x\n", + old_level_bits)); + level_bits |= (old_level_bits & 0x71ff); + DBG_DEVS(("sio_fixup: new irq level bits: 0x%04x\n", + level_bits)); outb((level_bits >> 0) & 0xff, 0x4d0); outb((level_bits >> 8) & 0xff, 0x4d1); @@ -1685,14 +1950,38 @@ static inline void sio_fixup(void) extern void tga_console_init(void); #endif /* CONFIG_TGA_CONSOLE */ -unsigned long __init -pcibios_fixup(unsigned long mem_start, unsigned long mem_end) +void __init +pcibios_fixup(void) { + struct pci_bus *cur; + +#ifdef CONFIG_ALPHA_MCPCIA + /* must do massive setup for multiple PCI busses here... */ + DBG_DEVS(("pcibios_fixup: calling mcpcia_fixup()...\n")); + mcpcia_fixup(); +#endif /* MCPCIA */ + +#ifdef CONFIG_ALPHA_TSUNAMI + /* must do massive setup for multiple PCI busses here... */ + /* tsunami_fixup(); */ +#endif /* TSUNAMI */ + #if PCI_MODIFY && !defined(CONFIG_ALPHA_RUFFIAN) /* * Scan the tree, allocating PCI memory and I/O space. */ - layout_bus(&pci_root); + /* + * Sigh; check_region() will need changing to accept a HANDLE, + * if we allocate I/O space addresses on a per-bus basis. + * For now, make the I/O bases unique across all busses, so + * that check_region() will not get confused... ;-} + */ + io_base = DEFAULT_IO_BASE; + for (cur = &pci_root; cur; cur = cur->next) { + mem_base = DEFAULT_MEM_BASE; + DBG_DEVS(("pcibios_fixup: calling layout_bus()\n")); + layout_bus(cur); + } #endif /* @@ -1713,10 +2002,8 @@ pcibios_fixup(unsigned long mem_start, unsigned long mem_end) eb66_and_eb64p_fixup(); #elif defined(CONFIG_ALPHA_MIKASA) mikasa_fixup(); -#elif defined(CONFIG_ALPHA_ALCOR) +#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) alcor_fixup(); -#elif defined(CONFIG_ALPHA_XLT) - xlt_fixup(); #elif defined(CONFIG_ALPHA_SABLE) sable_fixup(); #elif defined(CONFIG_ALPHA_MIATA) @@ -1725,6 +2012,12 @@ pcibios_fixup(unsigned long mem_start, unsigned long mem_end) noritake_fixup(); #elif defined(CONFIG_ALPHA_SX164) sx164_fixup(); +#elif defined(CONFIG_ALPHA_DP264) + dp264_fixup(); +#elif defined(CONFIG_ALPHA_RAWHIDE) + rawhide_fixup(); +#elif defined(CONFIG_ALPHA_TAKARA) + takara_fixup(); #elif defined(CONFIG_ALPHA_RUFFIAN) /* no fixup needed */ #else @@ -1736,8 +2029,6 @@ pcibios_fixup(unsigned long mem_start, unsigned long mem_end) tga_console_init(); #endif #endif - - return mem_start; } @@ -1831,6 +2122,33 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn, return err; } +#if (defined(CONFIG_ALPHA_PC164) || \ + defined(CONFIG_ALPHA_LX164) || \ + defined(CONFIG_ALPHA_SX164) || \ + defined(CONFIG_ALPHA_EB164) || \ + defined(CONFIG_ALPHA_EB66P) || \ + defined(CONFIG_ALPHA_CABRIOLET)) && defined(CONFIG_ALPHA_SRM) + +/* + on the above machines, under SRM console, we must use the CSERVE PALcode + routine to manage the interrupt mask for us, otherwise, the kernel/HW get + out of sync with what the PALcode thinks it needs to deliver/ignore + */ +void +cserve_update_hw(unsigned long irq, unsigned long mask) +{ + extern void cserve_ena(unsigned long); + extern void cserve_dis(unsigned long); + + if (mask & (1UL << irq)) + /* disable */ + cserve_dis(irq - 16); + else + /* enable */ + cserve_ena(irq - 16); + return; +} +#endif /* (PC164 || LX164 || SX164 || EB164 || CABRIO) && SRM */ #ifdef CONFIG_ALPHA_MIATA /* @@ -1877,4 +2195,49 @@ es1888_init(void) } #endif /* CONFIG_ALPHA_MIATA */ +__initfunc(char *pcibios_setup(char *str)) +{ + return str; +} + +#ifdef CONFIG_ALPHA_SRM_SETUP +void reset_for_srm(void) +{ + extern void scrreset(void); + struct pci_dev *dev; + int i; + + /* reset any IRQs that we changed */ + for (i = 0; i < irq_reset_count; i++) { + dev = irq_dev_to_reset[i]; + + pcibios_write_config_byte(dev->bus->number, dev->devfn, + PCI_INTERRUPT_LINE, irq_to_reset[i]); +#if 1 + printk("reset_for_srm: bus %d slot 0x%x " + "SRM IRQ 0x%x changed back from 0x%x\n", + dev->bus->number, PCI_SLOT(dev->devfn), + irq_to_reset[i], dev->irq); +#endif + } + + /* reset any IO addresses that we changed */ + for (i = 0; i < io_reset_count; i++) { + dev = io_dev_to_reset[i]; + + pcibios_write_config_byte(dev->bus->number, dev->devfn, + io_reg_to_reset[i], io_to_reset[i]); +#if 1 + printk("reset_for_srm: bus %d slot 0x%x " + "SRM IO restored to 0x%x\n", + dev->bus->number, PCI_SLOT(dev->devfn), + io_to_reset[i]); +#endif +} + + /* reset the visible screen to the top of display memory */ + scrreset(); +} +#endif /* CONFIG_ALPHA_SRM_SETUP */ + #endif /* CONFIG_PCI */ diff --git a/arch/alpha/kernel/cia.c b/arch/alpha/kernel/cia.c index 4bebe2732..57fae7d87 100644 --- a/arch/alpha/kernel/cia.c +++ b/arch/alpha/kernel/cia.c @@ -6,8 +6,8 @@ * */ #include <linux/kernel.h> +#include <linux/config.h> #include <linux/types.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/sched.h> @@ -17,13 +17,14 @@ #include <asm/ptrace.h> #include <asm/mmu_context.h> -/* NOTE: Herein are back-to-back mb insns. They are magic. - A plausible explanation is that the i/o controler does not properly - handle the system transaction. Another involves timing. Ho hum. */ +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the i/o controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ extern struct hwrpb_struct *hwrpb; extern asmlinkage void wrmces(unsigned long mces); -extern int alpha_sys_type; /* * Machine check reasons. Defined according to PALcode sources @@ -56,13 +57,17 @@ extern int alpha_sys_type; # define DBGC(args) #endif -#define vulp volatile unsigned long * #define vuip volatile unsigned int * static volatile unsigned int CIA_mcheck_expected = 0; static volatile unsigned int CIA_mcheck_taken = 0; static unsigned int CIA_jd; +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int CIA_DMA_WIN_BASE = CIA_DMA_WIN_BASE_DEFAULT; +unsigned int CIA_DMA_WIN_SIZE = CIA_DMA_WIN_SIZE_DEFAULT; +unsigned long cia_sm_base_r1, cia_sm_base_r2, cia_sm_base_r3; +#endif /* SRM_SETUP */ /* * Given a bus, device, and function number, compute resulting @@ -271,7 +276,7 @@ static void conf_write(unsigned long addr, unsigned int value, } /* reset error status: */ - *(vulp)CIA_IOC_CIA_ERR = stat0; + *(vuip)CIA_IOC_CIA_ERR = stat0; mb(); wrmces(0x7); /* reset machine check */ value = 0xffffffff; @@ -442,6 +447,18 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end) printk("CIA_init: CIA_STAT was 0x%x\n", temp); temp = *(vuip)CIA_IOC_MCR; mb(); printk("CIA_init: CIA_MCR was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_CIA_CTRL; mb(); + printk("CIA_init: CIA_CTRL was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_ERR_MASK; mb(); + printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PCI_W0_BASE); mb(); + printk("CIA_init: W0_BASE was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PCI_W1_BASE); mb(); + printk("CIA_init: W1_BASE was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PCI_W2_BASE); mb(); + printk("CIA_init: W2_BASE was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PCI_W3_BASE); mb(); + printk("CIA_init: W3_BASE was 0x%x\n", temp); } #endif /* DEBUG_DUMP_REGS */ @@ -458,6 +475,70 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end) *(vuip)CIA_IOC_CIA_CTRL = cia_tmp; mb(); +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 0 for enabled and mapped to 0 */ + if (((*(vuip)CIA_IOC_PCI_W0_BASE & 3) == 1) && + (*(vuip)CIA_IOC_PCI_T0_BASE == 0)) + { + CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W0_BASE & 0xfff00000U; + CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W0_MASK & 0xfff00000U; + CIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("cia_init: using Window 0 settings\n"); + printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)CIA_IOC_PCI_W0_BASE, + *(vuip)CIA_IOC_PCI_W0_MASK, + *(vuip)CIA_IOC_PCI_T0_BASE); +#endif + } + else /* check window 1 for enabled and mapped to 0 */ + if (((*(vuip)CIA_IOC_PCI_W1_BASE & 3) == 1) && + (*(vuip)CIA_IOC_PCI_T1_BASE == 0)) + { + CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W1_BASE & 0xfff00000U; + CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W1_MASK & 0xfff00000U; + CIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("cia_init: using Window 1 settings\n"); + printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)CIA_IOC_PCI_W1_BASE, + *(vuip)CIA_IOC_PCI_W1_MASK, + *(vuip)CIA_IOC_PCI_T1_BASE); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if (((*(vuip)CIA_IOC_PCI_W2_BASE & 3) == 1) && + (*(vuip)CIA_IOC_PCI_T2_BASE == 0)) + { + CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W2_BASE & 0xfff00000U; + CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W2_MASK & 0xfff00000U; + CIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("cia_init: using Window 2 settings\n"); + printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)CIA_IOC_PCI_W2_BASE, + *(vuip)CIA_IOC_PCI_W2_MASK, + *(vuip)CIA_IOC_PCI_T2_BASE); +#endif + } + else /* check window 3 for enabled and mapped to 0 */ + if (((*(vuip)CIA_IOC_PCI_W3_BASE & 3) == 1) && + (*(vuip)CIA_IOC_PCI_T3_BASE == 0)) + { + CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W3_BASE & 0xfff00000U; + CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W3_MASK & 0xfff00000U; + CIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("cia_init: using Window 3 settings\n"); + printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)CIA_IOC_PCI_W3_BASE, + *(vuip)CIA_IOC_PCI_W3_MASK, + *(vuip)CIA_IOC_PCI_T3_BASE); +#endif + } + else /* we must use our defaults which were pre-initialized... */ +#endif /* SRM_SETUP */ + { /* * Set up the PCI->physical memory translation windows. * For now, windows 1,2 and 3 are disabled. In the future, we may @@ -472,6 +553,7 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end) *(vuip)CIA_IOC_PCI_W1_BASE = 0x0; *(vuip)CIA_IOC_PCI_W2_BASE = 0x0; *(vuip)CIA_IOC_PCI_W3_BASE = 0x0; + } /* * check ASN in HWRPB for validity, report if bad @@ -483,28 +565,54 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end) } /* - * Finally, clear the CIA_CFG register, which gets used + * Next, clear the CIA_CFG register, which gets used * for PCI Config Space accesses. That is the way * we want to use it, and we do not want to depend on * what ARC or SRM might have left behind... */ { -#if 0 - unsigned int cia_cfg = *(vuip)CIA_IOC_CFG; mb(); - if (cia_cfg) printk("CIA_init: CFG was 0x%x\n", cia_cfg); -#endif - *(vuip)CIA_IOC_CFG = 0; mb(); + unsigned int cia_cfg = *((vuip)CIA_IOC_CFG); mb(); + if (cia_cfg) { + printk("CIA_init: CFG was 0x%x\n", cia_cfg); + *((vuip)CIA_IOC_CFG) = 0; mb(); + } } -#if 0 { - unsigned int temp; - temp = *(vuip)CIA_IOC_CIA_CTRL; mb(); - printk("CIA_init: CIA_CTRL was 0x%x\n", temp); - temp = *(vuip)CIA_IOC_ERR_MASK; mb(); - printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp); - } + unsigned int cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM); + unsigned int cia_hae_io = *((vuip)CIA_IOC_HAE_IO); +#if 0 + printk("CIA_init: HAE_MEM was 0x%x\n", cia_hae_mem); + printk("CIA_init: HAE_IO was 0x%x\n", cia_hae_io); #endif +#ifdef CONFIG_ALPHA_SRM_SETUP + /* + sigh... For the SRM setup, unless we know apriori what the HAE + contents will be, we need to setup the arbitrary region bases + so we can test against the range of addresses and tailor the + region chosen for the SPARSE memory access. + + see include/asm-alpha/cia.h for the SPARSE mem read/write + */ + cia_sm_base_r1 = (cia_hae_mem ) & 0xe0000000UL; /* region 1 */ + cia_sm_base_r2 = (cia_hae_mem << 16) & 0xf8000000UL; /* region 2 */ + cia_sm_base_r3 = (cia_hae_mem << 24) & 0xfc000000UL; /* region 3 */ + + /* + Set the HAE cache, so that setup_arch() code + will use the SRM setting always. Our readb/writeb + code in cia.h expects never to have to change + the contents of the HAE. + */ + hae.cache = cia_hae_mem; +#else /* SRM_SETUP */ + *((vuip)CIA_IOC_HAE_MEM) = 0; mb(); + cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM); + *((vuip)CIA_IOC_HAE_IO) = 0; mb(); + cia_hae_io = *((vuip)CIA_IOC_HAE_IO); +#endif /* SRM_SETUP */ + } + return mem_start; } @@ -512,7 +620,7 @@ int cia_pci_clr_err(void) { CIA_jd = *(vuip)CIA_IOC_CIA_ERR; DBGM(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd)); - *(vulp)CIA_IOC_CIA_ERR = 0x0180; + *(vuip)CIA_IOC_CIA_ERR = 0x0180; mb(); return 0; } diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S index b139d5178..0bbc71926 100644 --- a/arch/alpha/kernel/entry.S +++ b/arch/alpha/kernel/entry.S @@ -4,6 +4,7 @@ * kernel entry-points */ +#include <linux/config.h> #include <asm/system.h> #define halt .long PAL_halt @@ -48,6 +49,8 @@ * JRP - Save regs 16-18 in a special area of the stack, so that * the palcode-provided values are available to the signal handler. */ +#if defined(CONFIG_ALPHA_TSUNAMI) +/* TSUNAMI has no HAE register to save/restore */ #define SAVE_ALL \ subq $30,184,$30; \ stq $0,0($30); \ @@ -55,6 +58,55 @@ stq $2,16($30); \ stq $3,24($30); \ stq $4,32($30); \ + stq $5,40($30); \ + stq $6,48($30); \ + stq $7,56($30); \ + stq $8,64($30); \ + stq $19,72($30); \ + stq $20,80($30); \ + stq $21,88($30); \ + stq $22,96($30); \ + stq $23,104($30); \ + stq $24,112($30); \ + stq $25,120($30); \ + stq $26,128($30); \ + stq $27,136($30); \ + stq $28,144($30); \ + stq $16,160($30); \ + stq $17,168($30); \ + stq $18,176($30) + +#define RESTORE_ALL \ + ldq $0,0($30); \ + ldq $1,8($30); \ + ldq $2,16($30); \ + ldq $3,24($30); \ + ldq $4,32($30); \ + ldq $5,40($30); \ + ldq $6,48($30); \ + ldq $7,56($30); \ + ldq $8,64($30); \ + ldq $19,72($30); \ + ldq $20,80($30); \ + ldq $21,88($30); \ + ldq $22,96($30); \ + ldq $23,104($30); \ + ldq $24,112($30); \ + ldq $25,120($30); \ + ldq $26,128($30); \ + ldq $27,136($30); \ + ldq $28,144($30); \ + addq $30,184,$30 + +#else /* TSUNAMI */ +#define SAVE_ALL \ + subq $30,184,$30; \ + stq $0,0($30); \ + stq $1,8($30); \ + stq $2,16($30); \ + stq $3,24($30); \ + stq $4,32($30); \ + stq $28,144($30); \ lda $2,hae; \ stq $5,40($30); \ stq $6,48($30); \ @@ -70,7 +122,6 @@ stq $25,120($30); \ stq $26,128($30); \ stq $27,136($30); \ - stq $28,144($30); \ stq $2,152($30); \ stq $16,160($30); \ stq $17,168($30); \ @@ -113,6 +164,8 @@ ldq $28,144($30); \ addq $30,184,$30 +#endif /* TSUNAMI */ + .text .set noat #if defined(__linux__) && !defined(__ELF__) @@ -508,6 +561,8 @@ sys_clone: alpha_switch_to: bsr $1,do_switch_stack call_pal PAL_swpctx + lda $16,-2($31) + call_pal PAL_tbi bsr $1,undo_switch_stack ret $31,($26),1 .end alpha_switch_to @@ -681,6 +736,19 @@ signal_return: br $31,restore_all .end entSys +#ifdef __SMP__ + .globl ret_from_smpfork +.align 3 +.ent ret_from_smpfork +ret_from_smpfork: + .set at + stq $31,scheduler_lock + mb /* ?????????????????? */ + br ret_from_sys_call + .set noat +.end ret_from_smpfork +#endif /* __SMP__ */ + .align 3 .ent reschedule reschedule: diff --git a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S index 4a3ec9e7c..a6bcd616d 100644 --- a/arch/alpha/kernel/head.S +++ b/arch/alpha/kernel/head.S @@ -32,6 +32,27 @@ __start: halt .end __start +#ifdef __SMP__ + .align 3 + .globl __start_cpu + .ent __start_cpu + /* on entry here from SRM console, the HWPCB of this processor */ + /* has been loaded, and $27 contains the task pointer */ +__start_cpu: + /* first order of business, load the GP */ + br $26,1f +1: ldgp $29,0($26) + /* We need to get current loaded up with our first task... */ + lda $8,0($27) + /* set FEN */ + lda $16,1($31) + call_pal PAL_wrfen + /* ... and then we can start the processor. */ + jsr $26,start_secondary + halt + .end __start_cpu +#endif /* __SMP__ */ + .align 3 .globl wrent .ent wrent diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index 41d5d5f01..bcac2da2b 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -30,6 +30,10 @@ #define vulp volatile unsigned long * #define vuip volatile unsigned int * +extern void timer_interrupt(struct pt_regs * regs); +extern void cserve_update_hw(unsigned long, unsigned long); +extern void handle_ipi(struct pt_regs *); + #define RTC_IRQ 8 #ifdef CONFIG_RTC #define TIMER_IRQ 0 /* timer is the pit */ @@ -45,12 +49,15 @@ #if defined(CONFIG_ALPHA_P2K) /* always mask out unused timer irq 0 and RTC irq 8 */ # define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0x101UL) -#elif defined(CONFIG_ALPHA_ALCOR) +#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) /* always mask out unused timer irq 0, "irqs" 20-30, and the EISA cascade: */ # define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0xfff000000001UL) #elif defined(CONFIG_ALPHA_RUFFIAN) /* must leave timer irq 0 in the mask */ # define PROBE_MASK ((1UL << NR_IRQS) - 1) +#elif NR_IRQS == 64 + /* always mask out unused timer irq 0: */ +# define PROBE_MASK (~1UL) #else /* always mask out unused timer irq 0: */ # define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~1UL) @@ -119,7 +126,7 @@ sable_update_hw(unsigned long irq, unsigned long mask) { /* The "irq" argument is really the mask bit number */ switch (irq) { - default: /* 16 ... 23 */ + case 16 ... 23: outb(mask >> 16, 0x53d); break; case 8 ... 15: @@ -135,7 +142,7 @@ static inline void noritake_update_hw(unsigned long irq, unsigned long mask) { switch (irq) { - default: /* 32 ... 47 */ + case 32 ... 47: outw(~(mask >> 32), 0x54c); break; case 16 ... 31: @@ -155,7 +162,7 @@ static inline void miata_update_hw(unsigned long irq, unsigned long mask) { switch (irq) { - default: /* 16 ... 47 */ + case 16 ... 47: /* Make CERTAIN none of the bogus ints get enabled... */ *(vulp)PYXIS_INT_MASK = ~((long)mask >> 16) & ~0x4000000000000e3bUL; @@ -178,7 +185,7 @@ static inline void alcor_and_xlt_update_hw(unsigned long irq, unsigned long mask) { switch (irq) { - default: /* 16 ... 47 */ + case 16 ... 47: /* On Alcor, at least, lines 20..30 are not connected and can generate spurrious interrupts if we turn them on while IRQ probing. So explicitly mask them out. */ @@ -202,7 +209,7 @@ static inline void mikasa_update_hw(unsigned long irq, unsigned long mask) { switch (irq) { - default: /* 16 ... 31 */ + case 16 ... 31: outw(~(mask >> 16), 0x536); /* note invert */ break; case 8 ... 15: /* ISA PIC2 */ @@ -214,7 +221,7 @@ mikasa_update_hw(unsigned long irq, unsigned long mask) } } -#ifdef CONFIG_ALPHA_RUFFIAN +#if defined(CONFIG_ALPHA_RUFFIAN) static inline void ruffian_update_hw(unsigned long irq, unsigned long mask) { @@ -223,8 +230,7 @@ ruffian_update_hw(unsigned long irq, unsigned long mask) /* Note inverted sense of mask bits: */ /* Make CERTAIN none of the bogus ints get enabled... */ *(vulp)PYXIS_INT_MASK = - ~((long)mask >> 16) & 0x00000000ffffffbfUL; - mb(); + ~((long)mask >> 16) & 0x00000000ffffffbfUL; mb(); /* ... and read it back to make sure it got written. */ *(vulp)PYXIS_INT_MASK; break; @@ -236,20 +242,23 @@ ruffian_update_hw(unsigned long irq, unsigned long mask) break; } } -#endif +#endif /* RUFFIAN */ -#ifdef CONFIG_ALPHA_SX164 +#if defined(CONFIG_ALPHA_SX164) static inline void sx164_update_hw(unsigned long irq, unsigned long mask) { switch (irq) { case 16 ... 39: - /* Make CERTAIN none of the bogus ints get enabled */ +#if defined(CONFIG_ALPHA_SRM) + cserve_update_hw(irq, mask); +#else + /* make CERTAIN none of the bogus ints get enabled */ *(vulp)PYXIS_INT_MASK = - ~((long)mask >> 16) & ~0x000000000000003bUL; - mb(); + ~((long)mask >> 16) & ~0x000000000000003bUL; mb(); /* ... and read it back to make sure it got written. */ *(vulp)PYXIS_INT_MASK; +#endif /* SRM */ break; case 8 ... 15: /* ISA PIC2 */ outb(mask >> 8, 0xA1); @@ -258,20 +267,23 @@ sx164_update_hw(unsigned long irq, unsigned long mask) outb(mask, 0x21); break; } -} -#endif -/* Unlabeled mechanisms based on the number of irqs. Someone should - probably document and name these. */ +} +#endif /* SX164 */ +#if defined(CONFIG_ALPHA_DP264) static inline void -update_hw_33(unsigned long irq, unsigned long mask) +dp264_update_hw(unsigned long irq, unsigned long mask) { switch (irq) { - default: /* 16 ... 32 */ - outl(mask >> 16, 0x804); + case 16 ... 63: + /* make CERTAIN none of the bogus ints get enabled */ + /* HACK ALERT! only CPU#0 is used currently */ + *(vulp)TSUNAMI_CSR_DIM0 = + ~(mask) & ~0x0000000000000000UL; mb(); + /* ... and read it back to make sure it got written. */ + *(vulp)TSUNAMI_CSR_DIM0; break; - case 8 ... 15: /* ISA PIC2 */ outb(mask >> 8, 0xA1); break; @@ -280,16 +292,24 @@ update_hw_33(unsigned long irq, unsigned long mask) break; } } +#endif /* DP264 */ +#if defined(CONFIG_ALPHA_RAWHIDE) static inline void -update_hw_32(unsigned long irq, unsigned long mask) +rawhide_update_hw(unsigned long irq, unsigned long mask) { switch (irq) { - default: /* 24 ... 31 */ - outb(mask >> 24, 0x27); + case 16 ... 39: /* PCI bus 0 with EISA bridge */ + *(vuip)MCPCIA_INT_MASK0(0) = + (~((mask) >> 16) & 0x00ffffffU) | 0x00ff0000U; mb(); + /* ... and read it back to make sure it got written. */ + *(vuip)MCPCIA_INT_MASK0(0); break; - case 16 ... 23: - outb(mask >> 16, 0x26); + case 40 ... 63: /* PCI bus 1 with builtin NCR810 SCSI */ + *(vuip)MCPCIA_INT_MASK0(1) = + (~((mask) >> 40) & 0x00ffffffU) | 0x00fe0000U; mb(); + /* ... and read it back to make sure it got written. */ + *(vuip)MCPCIA_INT_MASK0(1); break; case 8 ... 15: /* ISA PIC2 */ outb(mask >> 8, 0xA1); @@ -299,12 +319,29 @@ update_hw_32(unsigned long irq, unsigned long mask) break; } } +#endif /* RAWHIDE */ +/* + * HW update code for the following platforms: + * + * CABRIOLET (AlphaPC64) + * EB66P + * EB164 + * PC164 + * LX164 + */ static inline void -update_hw_16(unsigned long irq, unsigned long mask) +update_hw_35(unsigned long irq, unsigned long mask) { switch (irq) { - default: /* 8 ... 15, ISA PIC2 */ + case 16 ... 34: +#if defined(CONFIG_ALPHA_SRM) + cserve_update_hw(irq, mask); +#else /* SRM */ + outl(irq_mask >> 16, 0x804); +#endif /* SRM */ + break; + case 8 ... 15: /* ISA PIC2 */ outb(mask >> 8, 0xA1); break; case 0 ... 7: /* ISA PIC1 */ @@ -313,42 +350,38 @@ update_hw_16(unsigned long irq, unsigned long mask) } } -#if (defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)) \ - && defined(CONFIG_ALPHA_SRM) -/* - * On the pc164, we cannot take over the IRQs from the SRM, - * so we call down to do our dirty work. Too bad the SRM - * isn't consistent across platforms otherwise we could do - * this always. - */ - -extern void cserve_ena(unsigned long); -extern void cserve_dis(unsigned long); - -static inline void mask_irq(unsigned long irq) +static inline void +update_hw_32(unsigned long irq, unsigned long mask) { - irq_mask |= (1UL << irq); - cserve_dis(irq - 16); + switch (irq) { + case 24 ... 31: + outb(mask >> 24, 0x27); + break; + case 16 ... 23: + outb(mask >> 16, 0x26); + break; + case 8 ... 15: /* ISA PIC2 */ + outb(mask >> 8, 0xA1); + break; + case 0 ... 7: /* ISA PIC1 */ + outb(mask, 0x21); + break; } - -static inline void unmask_irq(unsigned long irq) -{ - irq_mask &= ~(1UL << irq); - cserve_ena(irq - 16); } -/* Since we are calling down to PALcode, no need to diddle IPL. */ -void disable_irq(unsigned int irq_nr) +static inline void +update_hw_16(unsigned long irq, unsigned long mask) { - mask_irq(IRQ_TO_MASK(irq_nr)); + switch (irq) { + case 8 ... 15: /* ISA PIC2 */ + outb(mask >> 8, 0xA1); + break; + case 0 ... 7: /* ISA PIC1 */ + outb(mask, 0x21); + break; } - -void enable_irq(unsigned int irq_nr) -{ - unmask_irq(IRQ_TO_MASK(irq_nr)); } -#else /* * We manipulate the hardware ourselves. */ @@ -369,9 +402,18 @@ static void update_hw(unsigned long irq, unsigned long mask) sx164_update_hw(irq, mask); #elif defined(CONFIG_ALPHA_RUFFIAN) ruffian_update_hw(irq, mask); -#elif NR_IRQS == 33 - update_hw_33(irq, mask); -#elif NR_IRQS == 32 +#elif defined(CONFIG_ALPHA_DP264) + dp264_update_hw(irq, mask); +#elif defined(CONFIG_ALPHA_RAWHIDE) + rawhide_update_hw(irq, mask); +#elif defined(CONFIG_ALPHA_CABRIOLET) || \ + defined(CONFIG_ALPHA_EB66P) || \ + defined(CONFIG_ALPHA_EB164) || \ + defined(CONFIG_ALPHA_PC164) || \ + defined(CONFIG_ALPHA_LX164) + update_hw_35(irq, mask); +#elif defined(CONFIG_ALPHA_EB66) || \ + defined(CONFIG_ALPHA_EB64P) update_hw_32(irq, mask); #elif NR_IRQS == 16 update_hw_16(irq, mask); @@ -396,8 +438,7 @@ void disable_irq(unsigned int irq_nr) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); mask_irq(IRQ_TO_MASK(irq_nr)); restore_flags(flags); } @@ -406,12 +447,10 @@ void enable_irq(unsigned int irq_nr) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); unmask_irq(IRQ_TO_MASK(irq_nr)); restore_flags(flags); } -#endif /* (PC164 || LX164) && SRM */ /* * Initial irq handlers. @@ -423,13 +462,14 @@ int get_irq_list(char *buf) { int i, len = 0; struct irqaction * action; + int cpu = smp_processor_id(); for (i = 0; i < NR_IRQS; i++) { action = irq_action[i]; if (!action) continue; len += sprintf(buf+len, "%2d: %10u %c %s", - i, kstat.irqs[0][i], + i, kstat.irqs[cpu][i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -463,16 +503,18 @@ static inline void ack_irq(int irq) #elif defined(CONFIG_ALPHA_RUFFIAN) if (irq < 16) { /* Ack PYXIS ISA interrupt. */ - *(vulp)PYXIS_INT_REQ = 1 << 7; - mb(); + *(vulp)PYXIS_INT_REQ = 1L << 7; mb(); + /* ... and read it back to make sure it got written. */ + *(vulp)PYXIS_INT_REQ; if (irq > 7) { outb(0x20, 0xa0); } outb(0x20, 0x20); } else { - /* Ack PYXIS interrupt. */ + /* Ack PYXIS PCI interrupt. */ *(vulp)PYXIS_INT_REQ = (1UL << (irq - 16)); - mb(); + /* ... and read it back to make sure it got written. */ + *(vulp)PYXIS_INT_REQ; } #else if (irq < 16) { @@ -488,7 +530,7 @@ static inline void ack_irq(int irq) /* on ALCOR/XLT, need to dismiss interrupt via GRU */ *(vuip)GRU_INT_CLEAR = 0x80000000; mb(); *(vuip)GRU_INT_CLEAR = 0x00000000; mb(); -#endif +#endif /* ALCOR || XLT */ } #endif } @@ -556,8 +598,7 @@ int request_irq(unsigned int irq, action->next = NULL; action->dev_id = dev_id; - save_flags(flags); - cli(); + save_and_cli(flags); *p = action; if (!shared) @@ -585,8 +626,7 @@ void free_irq(unsigned int irq, void *dev_id) continue; /* Found it - now free it */ - save_flags(flags); - cli(); + save_and_cli(flags); *p = action->next; if (!irq[irq_action]) mask_irq(IRQ_TO_MASK(irq)); @@ -607,7 +647,277 @@ unsigned int local_irq_count[NR_CPUS]; unsigned int local_bh_count[NR_CPUS]; #ifdef __SMP__ -#error "Me no hablo Alpha SMP" +/* 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; + +/* Global IRQ locking depth. */ +atomic_t global_irq_count = ATOMIC_INIT(0); + +/* This protects BH software state (masks, things like that). */ +atomic_t global_bh_lock = ATOMIC_INIT(0); +atomic_t global_bh_count = ATOMIC_INIT(0); + +static unsigned long previous_irqholder = NO_PROC_ID; + +#undef INIT_STUCK +#define INIT_STUCK 100000000 + +#undef STUCK +#define STUCK \ +if (!--stuck) {printk("wait_on_irq CPU#%d stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", cpu, where, previous_irqholder, local_count, atomic_read(&global_irq_count)); stuck = INIT_STUCK; } + +static inline void wait_on_irq(int cpu, unsigned long where) +{ + int stuck = INIT_STUCK; + int local_count = local_irq_count[cpu]; + + /* Are we the only one in an interrupt context? */ + while (local_count != atomic_read(&global_irq_count)) { + /* + * No such luck. Now we need to release the lock, + * _and_ release our interrupt context, because + * otherwise we'd have dead-locks and live-locks + * and other fun things. + */ + atomic_sub(local_count, &global_irq_count); + spin_unlock(&global_irq_lock); + + /* + * Wait for everybody else to go away and release + * their things before trying to get the lock again. + */ + for (;;) { + STUCK; + if (atomic_read(&global_irq_count)) + continue; + if (global_irq_lock.lock) + continue; + if (spin_trylock(&global_irq_lock)) + break; + } + atomic_add(local_count, &global_irq_count); + } +} + +#undef INIT_STUCK +#define INIT_STUCK 10000000 + +#undef STUCK +#define STUCK \ +if (!--stuck) {printk("get_irqlock stuck at %08lx, waiting for %08lx\n", where, previous_irqholder); stuck = INIT_STUCK;} + +static inline void get_irqlock(int cpu, unsigned long where) +{ + int stuck = INIT_STUCK; + + if (!spin_trylock(&global_irq_lock)) { + /* do we already hold the lock? */ + if ((unsigned char) cpu == global_irq_holder) { +#if 0 + printk("get_irqlock: already held at %08lx\n", + previous_irqholder); +#endif + return; + } + /* Uhhuh.. Somebody else got it. Wait.. */ + do { + do { + STUCK; + barrier(); + } while (global_irq_lock.lock); + } while (!spin_trylock(&global_irq_lock)); + } + /* + * Ok, we got the lock bit. + * But that's actually just the easy part.. Now + * we need to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu, where); + + /* + * Finally. + */ + global_irq_holder = cpu; + previous_irqholder = where; +} + +void __global_cli(void) +{ + int cpu = smp_processor_id(); + unsigned long where; + + __asm__("mov $26, %0" : "=r" (where)); + __cli(); + + if (!local_irq_count[cpu]) + get_irqlock(smp_processor_id(), where); +} + +void __global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count[cpu]) + release_irqlock(smp_processor_id()); + + __sti(); +} + +#if 0 +unsigned long __global_save_flags(void) +{ + return global_irq_holder == (unsigned char) smp_processor_id(); +} +#endif + +void __global_restore_flags(unsigned long flags) +{ + if (flags & 1) { + __global_cli(); + } else { + /* release_irqlock() */ + if (global_irq_holder == smp_processor_id()) { + global_irq_holder = NO_PROC_ID; + spin_unlock(&global_irq_lock); + } + if (!(flags & 2)) + __sti(); + } +} + +#undef INIT_STUCK +#define INIT_STUCK 200000000 + +#undef STUCK +#define STUCK \ +if (!--stuck) {printk("irq_enter stuck (irq=%d, cpu=%d, global=%d)\n",irq,cpu,global_irq_holder); stuck = INIT_STUCK;} + +#undef VERBOSE_IRQLOCK_DEBUGGING + +void irq_enter(int cpu, int irq) +{ +#ifdef VERBOSE_IRQLOCK_DEBUGGING + extern void smp_show_backtrace_all_cpus(void); +#endif + int stuck = INIT_STUCK; + + hardirq_enter(cpu); + barrier(); + while (global_irq_lock.lock) { + if ((unsigned char) cpu == global_irq_holder) { + int globl_locked = global_irq_lock.lock; + int globl_icount = atomic_read(&global_irq_count); + int local_count = local_irq_count[cpu]; + + /* It is very important that we load the state variables + * before we do the first call to printk() as printk() + * could end up changing them... + */ + +#if 0 + printk("CPU[%d]: BAD! Local IRQ's enabled," + " global disabled interrupt\n", cpu); +#endif + printk("CPU[%d]: where [%08lx] glocked[%d] gicnt[%d]" + " licnt[%d]\n", + cpu, previous_irqholder, globl_locked, + globl_icount, local_count); +#ifdef VERBOSE_IRQLOCK_DEBUGGING + printk("Performing backtrace on all cpus," + " write this down!\n"); + smp_show_backtrace_all_cpus(); +#endif + break; + } + STUCK; + barrier(); + } +} + +void irq_exit(int cpu, int irq) +{ + hardirq_exit(cpu); + release_irqlock(cpu); +} + +static void show(char * str) +{ +#if 0 + int i; + unsigned long *stack; +#endif + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [%d %d]\n", + atomic_read(&global_irq_count), local_irq_count[0], + local_irq_count[1]); + printk("bh: %d [%d %d]\n", + atomic_read(&global_bh_count), local_bh_count[0], + local_bh_count[1]); +#if 0 + stack = (unsigned long *) &str; + for (i = 40; i ; i--) { + unsigned long x = *++stack; + if (x > (unsigned long) &init_task_union && + x < (unsigned long) &vsprintf) { + printk("<[%08lx]> ", x); + } + } +#endif +} + +#define MAXCOUNT 100000000 + +static inline void wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if (!--count) { + show("wait_on_bh"); + count = ~0; + } + /* nothing .. wait for the other bh's to go away */ + } while (atomic_read(&global_bh_count) != 0); +} + +/* + * This is called when we want to synchronize with + * bottom half handlers. We need to wait until + * no other CPU is executing any bottom half handler. + * + * Don't wait if we're already running in an interrupt + * context or are inside a bh handler. + */ +void synchronize_bh(void) +{ + if (atomic_read(&global_bh_count)) { + int cpu = smp_processor_id(); + if (!local_irq_count[cpu] && !local_bh_count[cpu]) { + wait_on_bh(); + } + } +} + +/* There has to be a better way. */ +void synchronize_irq(void) +{ + int cpu = smp_processor_id(); + int local_count = local_irq_count[cpu]; + + if (local_count != atomic_read(&global_irq_count)) { + unsigned long flags; + + /* An infamously unpopular approach. */ + save_and_cli(flags); + restore_flags(flags); + } +} + #else #define irq_enter(cpu, irq) (++local_irq_count[cpu]) #define irq_exit(cpu, irq) (--local_irq_count[cpu]) @@ -647,7 +957,7 @@ static inline void handle_irq(int irq, struct pt_regs * regs) int cpu = smp_processor_id(); irq_enter(cpu, irq); - kstat.irqs[0][irq] += 1; + kstat.irqs[cpu][irq] += 1; if (!action) { unexpected_irq(irq, regs); } else { @@ -670,8 +980,9 @@ static inline void device_interrupt(int irq, int ack, struct pt_regs * regs) } irq_enter(cpu, irq); - kstat.irqs[0][irq] += 1; + kstat.irqs[cpu][irq] += 1; action = irq_action[irq]; + /* * For normal interrupts, we mask it out, and then ACK it. * This way another (more timing-critical) interrupt can @@ -691,6 +1002,10 @@ static inline void device_interrupt(int irq, int ack, struct pt_regs * regs) action = action->next; } while (action); unmask_irq(ack); + } else { +#if 1 + printk("device_interrupt: unexpected interrupt %d\n", irq); +#endif } irq_exit(cpu, irq); } @@ -711,6 +1026,8 @@ static inline void isa_device_interrupt(unsigned long vector, # define IACK_SC CIA_IACK_SC #elif defined(CONFIG_ALPHA_PYXIS) # define IACK_SC PYXIS_IACK_SC +#elif defined(CONFIG_ALPHA_TSUNAMI) +# define IACK_SC TSUNAMI_PCI0_IACK_SC #else /* * This is bogus but necessary to get it to compile @@ -729,7 +1046,7 @@ static inline void isa_device_interrupt(unsigned long vector, * interrupt that is pending. The PALcode sets up the * interrupts vectors such that irq level L generates vector L. */ - j = *(volatile int *) IACK_SC; + j = *(vuip) IACK_SC; j &= 0xff; if (j == 7) { if (!(inb(0x20) & 0x80)) { @@ -775,10 +1092,9 @@ alcor_and_xlt_device_interrupt(unsigned long vector, struct pt_regs *regs) unsigned int i; unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); - /* read the interrupt summary register of the GRU */ + /* Read the interrupt summary register of the GRU */ pld = (*(vuip)GRU_INT_REQ) & GRU_INT_REQ_BITS; #if 0 @@ -810,10 +1126,9 @@ cabriolet_and_eb66p_device_interrupt(unsigned long vector, unsigned int i; unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); - /* read the interrupt summary registers */ + /* Read the interrupt summary registers */ pld = inb(0x804) | (inb(0x805) << 8) | (inb(0x806) << 16); #if 0 @@ -843,10 +1158,9 @@ mikasa_device_interrupt(unsigned long vector, struct pt_regs *regs) unsigned int i; unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); - /* read the interrupt summary registers */ + /* Read the interrupt summary registers */ pld = (((unsigned long) (~inw(0x534)) & 0x0000ffffUL) << 16) | (((unsigned long) inb(0xa0)) << 8) | ((unsigned long) inb(0x20)); @@ -878,10 +1192,9 @@ eb66_and_eb64p_device_interrupt(unsigned long vector, struct pt_regs *regs) unsigned int i; unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); - /* read the interrupt summary registers */ + /* Read the interrupt summary registers */ pld = inb(0x26) | (inb(0x27) << 8); /* * Now, for every possible bit set, work through @@ -909,30 +1222,34 @@ miata_device_interrupt(unsigned long vector, struct pt_regs *regs) unsigned int i; unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); - /* read the interrupt summary register of PYXIS */ - pld = (*(vulp)PYXIS_INT_REQ); + /* Read the interrupt summary register of PYXIS */ + pld = *(vulp)PYXIS_INT_REQ; #if 0 printk("[0x%08lx/0x%08lx/0x%04x]", pld, *(vulp)PYXIS_INT_MASK, inb(0x20) | (inb(0xA0) << 8)); #endif - /* For now, AND off any bits we are not interested in. */ -#if defined(CONFIG_ALPHA_MIATA) - /* HALT (2), timer (6), ISA Bridge (7), 21142/3 (8), - then all the PCI slots/INTXs (12-31). */ +#ifdef CONFIG_ALPHA_MIATA + /* + * For now, AND off any bits we are not interested in: + * HALT (2), timer (6), ISA Bridge (7), 21142/3 (8) + * then all the PCI slots/INTXs (12-31). + */ /* Maybe HALT should only be used for SRM console boots? */ pld &= 0x00000000fffff1c4UL; #endif -#if defined(CONFIG_ALPHA_SX164) - /* HALT (2), timer (6), ISA Bridge (7), - then all the PCI slots/INTXs (8-23). */ - /* HALT should only be used for SRM console boots. */ +#ifdef CONFIG_ALPHA_SX164 + /* + * For now, AND off any bits we are not interested in: + * HALT (2), timer (6), ISA Bridge (7) + * then all the PCI slots/INTXs (8-23) + */ + /* Maybe HALT should only be used for SRM console boots? */ pld &= 0x0000000000ffffc0UL; -#endif +#endif /* SX164 */ /* * Now for every possible bit set, work through them and call @@ -962,10 +1279,9 @@ noritake_device_interrupt(unsigned long vector, struct pt_regs *regs) unsigned int i; unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); - /* read the interrupt summary registers of NORITAKE */ + /* Read the interrupt summary registers of NORITAKE */ pld = ((unsigned long) inw(0x54c) << 32) | ((unsigned long) inw(0x54a) << 16) | ((unsigned long) inb(0xa0) << 8) | @@ -991,16 +1307,76 @@ noritake_device_interrupt(unsigned long vector, struct pt_regs *regs) restore_flags(flags); } +#if defined(CONFIG_ALPHA_DP264) +/* we have to conditionally compile this because of TSUNAMI_xxx symbols */ +static inline void dp264_device_interrupt(unsigned long vector, + struct pt_regs * regs) +{ + unsigned long pld, tmp; + unsigned int i; + unsigned long flags; + + __save_and_cli(flags); + + /* Read the interrupt summary register of TSUNAMI */ + pld = (*(vulp)TSUNAMI_CSR_DIR0); + +#if 0 + printk("[0x%08lx/0x%08lx/0x%04x]", pld, + *(vulp)TSUNAMI_CSR_DIM0, + inb(0x20) | (inb(0xA0) << 8)); +#endif + + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + while (pld) { + i = ffz(~pld); + pld &= pld - 1; /* clear least bit set */ + if (i == 55) { + isa_device_interrupt(vector, regs); + } else { /* if not timer int */ + device_interrupt(16 + i, 16 + i, regs); + } +#if 0 + *(vulp)TSUNAMI_CSR_DIR0 = 1UL << i; mb(); + tmp = *(vulp)TSUNAMI_CSR_DIR0; +#endif + } + __restore_flags(flags); +} +#endif /* DP264 */ + +#if defined(CONFIG_ALPHA_RAWHIDE) +/* we have to conditionally compile this because of MCPCIA_xxx symbols */ +static inline void rawhide_device_interrupt(unsigned long vector, + struct pt_regs * regs) +{ +#if 0 + unsigned long pld; + unsigned int i; + unsigned long flags; + + __save_and_cli(flags); + + /* PLACEHOLDER, perhaps never used if we always do SRM */ + + __restore_flags(flags); +#endif +} +#endif /* RAWHIDE */ + #if defined(CONFIG_ALPHA_RUFFIAN) static inline void ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs) + { unsigned long pld; unsigned int i; unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); /* Read the interrupt summary register of PYXIS */ pld = *(vulp)PYXIS_INT_REQ; @@ -1010,16 +1386,16 @@ ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs) * then all the PCI slots/INTXs (12-31) * flash(5) :DWH: */ - pld &= 0x00000000ffffff9fUL; + pld &= 0x00000000ffffff9fUL;/* was ffff7f */ /* * Now for every possible bit set, work through them and call * the appropriate interrupt handler. */ + while (pld) { i = ffz(~pld); pld &= pld - 1; /* clear least bit set */ - if (i == 7) { /* Copy this bit from isa_device_interrupt cause we need to hook into int 0 for the timer. I @@ -1041,19 +1417,57 @@ ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs) } else { device_interrupt(j, j, regs); } - } else { + } else { /* if not timer int */ device_interrupt(16 + i, 16 + i, regs); } - *(vulp)PYXIS_INT_REQ = 1UL << i; - mb(); - *(vulp)PYXIS_INT_REQ; + *(vulp)PYXIS_INT_REQ = 1UL << i; mb(); + *(vulp)PYXIS_INT_REQ; /* read to force the write */ } - restore_flags(flags); } #endif /* RUFFIAN */ +static inline void takara_device_interrupt(unsigned long vector, + struct pt_regs * regs) +{ + unsigned long flags; + unsigned intstatus; + + save_and_cli(flags); + + /* + * The PALcode will have passed us vectors 0x800 or 0x810, + * which are fairly arbitrary values and serve only to tell + * us whether an interrupt has come in on IRQ0 or IRQ1. If + * it's IRQ1 it's a PCI interrupt; if it's IRQ0, it's + * probably ISA, but PCI interrupts can come through IRQ0 + * as well if the interrupt controller isn't in accelerated + * mode. + * + * OTOH, the accelerator thing doesn't seem to be working + * overly well, so what we'll do instead is try directly + * examining the Master Interrupt Register to see if it's a + * PCI interrupt, and if _not_ then we'll pass it on to the + * ISA handler. + */ + + intstatus = inw(0x500) & 15; + if (intstatus) { + /* + * This is a PCI interrupt. Check each bit and + * despatch an interrupt if it's set. + */ + if (intstatus & 8) device_interrupt(16+3, 16+3, regs); + if (intstatus & 4) device_interrupt(16+2, 16+2, regs); + if (intstatus & 2) device_interrupt(16+1, 16+1, regs); + if (intstatus & 1) device_interrupt(16+0, 16+0, regs); + } else + isa_device_interrupt (vector, regs); + + restore_flags(flags); +} + #endif /* CONFIG_PCI */ /* @@ -1085,9 +1499,11 @@ srm_device_interrupt(unsigned long vector, struct pt_regs * regs) int irq, ack; unsigned long flags; - save_flags(flags); - cli(); + __save_and_cli(flags); +#ifdef __SMP__ +if (smp_processor_id()) printk("srm_device_interrupt on other CPU\n"); +#endif ack = irq = (vector - 0x800) >> 4; @@ -1131,9 +1547,9 @@ srm_device_interrupt(unsigned long vector, struct pt_regs * regs) #ifdef CONFIG_ALPHA_NORITAKE /* - * I really hate to do this, but the NORITAKE SRM console reports - * PCI vectors *lower* than I expected from the bit numbering in - * the documentation. + * I really hate to do this, too, but the NORITAKE SRM console also + * reports PCI vectors *lower* than I expected from the bit numbers + * in the documentation. * But I really don't want to change the fixup code for allocation * of IRQs, nor the irq_mask maintenance stuff, both of which look * nice and clean now. @@ -1153,9 +1569,41 @@ srm_device_interrupt(unsigned long vector, struct pt_regs * regs) #endif #endif /* CONFIG_ALPHA_SABLE */ +#ifdef CONFIG_ALPHA_DP264 + /* + * the DP264 SRM console reports PCI interrupts with a vector + * 0x100 *higher* than one might expect, as PCI IRQ 0 (ie bit 0) + * shows up as IRQ 16, etc, etc. We adjust it down by 16 to have + * it line up with the actual bit numbers from the DIM registers, + * which is how we manage the interrupts/mask. Sigh... + */ + if (irq >= 32) + ack = irq = irq - 16; +#endif /* DP264 */ + +#ifdef CONFIG_ALPHA_RAWHIDE + /* + * the RAWHIDE SRM console reports PCI interrupts with a vector + * 0x80 *higher* than one might expect, as PCI IRQ 0 (ie bit 0) + * shows up as IRQ 24, etc, etc. We adjust it down by 8 to have + * it line up with the actual bit numbers from the REQ registers, + * which is how we manage the interrupts/mask. Sigh... + * + * also, PCI #1 interrupts are offset some more... :-( + */ + if (irq == 52) + ack = irq = 56; /* SCSI on PCI 1 is special */ + else { + if (irq >= 24) /* adjust all PCI interrupts down 8 */ + ack = irq = irq - 8; + if (irq >= 48) /* adjust PCI bus 1 interrupts down another 8 */ + ack = irq = irq - 8; + } +#endif /* RAWHIDE */ + device_interrupt(irq, ack, regs); - restore_flags(flags); + __restore_flags(flags); } /* @@ -1218,6 +1666,10 @@ extern void pyxis_machine_check(unsigned long vector, unsigned long la, struct pt_regs * regs); extern void t2_machine_check(unsigned long vector, unsigned long la, struct pt_regs * regs); +extern void tsunami_machine_check(unsigned long vector, unsigned long la, + struct pt_regs * regs); +extern void mcpcia_machine_check(unsigned long vector, unsigned long la, + struct pt_regs * regs); static void machine_check(unsigned long vector, unsigned long la, struct pt_regs *regs) @@ -1232,6 +1684,10 @@ machine_check(unsigned long vector, unsigned long la, struct pt_regs *regs) pyxis_machine_check(vector, la, regs); #elif defined(CONFIG_ALPHA_T2) t2_machine_check(vector, la, regs); +#elif defined(CONFIG_ALPHA_TSUNAMI) + tsunami_machine_check(vector, la, regs); +#elif defined(CONFIG_ALPHA_MCPCIA) + mcpcia_machine_check(vector, la, regs); #else printk("Machine check\n"); #endif @@ -1244,7 +1700,14 @@ do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, { switch (type) { case 0: +#ifdef __SMP__ +/* irq_enter(smp_processor_id(), 0); ??????? */ + handle_ipi(®s); +/* irq_exit(smp_processor_id(), 0); ??????? */ + return; +#else /* __SMP__ */ printk("Interprocessor interrupt? You must be kidding\n"); +#endif /* __SMP__ */ break; case 1: handle_irq(RTC_IRQ, ®s); @@ -1253,23 +1716,38 @@ do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, machine_check(vector, la_ptr, ®s); return; case 3: -#if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \ - defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM) +#if defined(CONFIG_ALPHA_JENSEN) || \ + defined(CONFIG_ALPHA_NONAME) || \ + defined(CONFIG_ALPHA_P2K) || \ + defined(CONFIG_ALPHA_SRM) srm_device_interrupt(vector, ®s); -#elif defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164) +#elif defined(CONFIG_ALPHA_MIATA) || \ + defined(CONFIG_ALPHA_SX164) miata_device_interrupt(vector, ®s); #elif defined(CONFIG_ALPHA_NORITAKE) noritake_device_interrupt(vector, ®s); -#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) +#elif defined(CONFIG_ALPHA_ALCOR) || \ + defined(CONFIG_ALPHA_XLT) alcor_and_xlt_device_interrupt(vector, ®s); -#elif defined(CONFIG_ALPHA_RUFFIAN) - ruffian_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_CABRIOLET) || \ + defined(CONFIG_ALPHA_EB66P) || \ + defined(CONFIG_ALPHA_EB164) || \ + defined(CONFIG_ALPHA_PC164) || \ + defined(CONFIG_ALPHA_LX164) + cabriolet_and_eb66p_device_interrupt(vector, ®s); #elif defined(CONFIG_ALPHA_MIKASA) mikasa_device_interrupt(vector, ®s); -#elif NR_IRQS == 33 - cabriolet_and_eb66p_device_interrupt(vector, ®s); -#elif NR_IRQS == 32 +#elif defined(CONFIG_ALPHA_EB66) || \ + defined(CONFIG_ALPHA_EB64P) eb66_and_eb64p_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_RUFFIAN) + ruffian_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_DP264) + dp264_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_RAWHIDE) + rawhide_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_TAKARA) + takara_device_interrupt(vector, ®s); #elif NR_IRQS == 16 isa_device_interrupt(vector, ®s); #endif @@ -1293,22 +1771,21 @@ static inline void sable_init_IRQ(void) outb(0x44, 0x535); /* enable cascades in master */ } -#ifdef CONFIG_ALPHA_SX164 +#if defined(CONFIG_ALPHA_SX164) static inline void sx164_init_IRQ(void) { +#if !defined(CONFIG_ALPHA_SRM) /* note invert on MASK bits */ *(vulp)PYXIS_INT_MASK = ~((long)irq_mask >> 16); mb(); -#if 0 - *(vulp)PYXIS_INT_HILO = 0x000000B2UL; mb(); /* ISA/NMI HI */ - *(vulp)PYXIS_RT_COUNT = 0UL; mb(); /* clear count */ -#endif + *(vulp)PYXIS_INT_MASK; +#endif /* !SRM */ enable_irq(16 + 6); /* enable timer */ enable_irq(16 + 7); /* enable ISA PIC cascade */ enable_irq(2); /* enable cascade */ } #endif /* SX164 */ -#ifdef CONFIG_ALPHA_RUFFIAN +#if defined(CONFIG_ALPHA_RUFFIAN) static inline void ruffian_init_IRQ(void) { /* invert 6&7 for i82371 */ @@ -1343,19 +1820,19 @@ static inline void ruffian_init_IRQ(void) } #endif /* RUFFIAN */ - #ifdef CONFIG_ALPHA_MIATA static inline void miata_init_IRQ(void) { /* note invert on MASK bits */ *(vulp)PYXIS_INT_MASK = ~((long)irq_mask >> 16); mb(); /* invert */ +#if 0 + /* these break on MiataGL so we'll try not to do it at all */ *(vulp)PYXIS_INT_HILO = 0x000000B2UL; mb(); /* ISA/NMI HI */ *(vulp)PYXIS_RT_COUNT = 0UL; mb(); /* clear count */ - *(vulp)PYXIS_INT_REQ = 0x4000000000000000UL; mb(); /* clear upper timer */ -#if 0 - *(vulp)PYXIS_INT_ROUTE = 0UL; mb(); /* all are level */ - *(vulp)PYXIS_INT_CNFG = 0UL; mb(); /* all clear */ #endif + /* clear upper timer */ + *(vulp)PYXIS_INT_REQ = 0x4000000000000000UL; mb(); + enable_irq(16 + 2); /* enable HALT switch - SRM only? */ enable_irq(16 + 6); /* enable timer */ enable_irq(16 + 7); /* enable ISA PIC cascade */ @@ -1381,7 +1858,7 @@ static inline void alcor_and_xlt_init_IRQ(void) enable_irq(16 + 31); /* enable (E)ISA PIC cascade */ enable_irq(2); /* enable cascade */ } -#endif +#endif /* ALCOR || XLT */ static inline void mikasa_init_IRQ(void) { @@ -1389,9 +1866,56 @@ static inline void mikasa_init_IRQ(void) enable_irq(2); /* enable cascade */ } -static inline void init_IRQ_33(void) +#if defined(CONFIG_ALPHA_DP264) +static inline void dp264_init_IRQ(void) +{ + /* note invert on MASK bits */ + *(vulp)TSUNAMI_CSR_DIM0 = + ~(irq_mask) & ~0x0000000000000000UL; mb(); + *(vulp)TSUNAMI_CSR_DIM0; + enable_irq(55); /* enable CYPRESS interrupt controller (ISA) */ + enable_irq(2); +} +#endif /* DP264 */ + +#if defined(CONFIG_ALPHA_RAWHIDE) +static inline void rawhide_init_IRQ(void) { + /* HACK ALERT! only PCI busses 0 and 1 are used currently, + and routing is only to CPU #1*/ + + *(vuip)MCPCIA_INT_MASK0(0) = + (~((irq_mask) >> 16) & 0x00ffffffU) | 0x00ff0000U; mb(); + /* ... and read it back to make sure it got written. */ + *(vuip)MCPCIA_INT_MASK0(0); + + *(vuip)MCPCIA_INT_MASK0(1) = + (~((irq_mask) >> 40) & 0x00ffffffU) | 0x00fe0000U; mb(); + /* ... and read it back to make sure it got written. */ + *(vuip)MCPCIA_INT_MASK0(1); + enable_irq(2); +} +#endif /* RAWHIDE */ + +static inline void takara_init_IRQ(void) +{ + unsigned int ctlreg = inl(0x500); + + ctlreg &= ~0x8000; /* return to non-accelerated mode */ + outw(ctlreg >> 16, 0x502); + outw(ctlreg & 0xFFFF, 0x500); + ctlreg = 0x05107c00; /* enable the PCI interrupt register */ + printk("Setting to 0x%08x\n", ctlreg); + outw(ctlreg >> 16, 0x502); + outw(ctlreg & 0xFFFF, 0x500); + enable_irq(2); +} + +static inline void init_IRQ_35(void) +{ +#if !defined(CONFIG_ALPHA_SRM) outl(irq_mask >> 16, 0x804); +#endif /* !SRM */ enable_irq(16 + 4); /* enable SIO cascade */ enable_irq(2); /* enable cascade */ } @@ -1413,13 +1937,20 @@ void __init init_IRQ(void) { wrent(entInt, 0); - dma_outb(0, DMA1_RESET_REG); - dma_outb(0, DMA2_RESET_REG); -#ifndef CONFIG_ALPHA_SX164 - dma_outb(0, DMA1_CLR_MASK_REG); - /* We need to figure out why this fails on the SX164. */ - dma_outb(0, DMA2_CLR_MASK_REG); -#endif + +/* FIXME FIXME FIXME FIXME FIXME */ +#if !defined(CONFIG_ALPHA_DP264) + /* we need to figure out why these fail on the DP264 */ + outb(0, DMA1_RESET_REG); + outb(0, DMA2_RESET_REG); +#endif /* !DP264 */ +/* FIXME FIXME FIXME FIXME FIXME */ +#if !defined(CONFIG_ALPHA_SX164) && !defined(CONFIG_ALPHA_DP264) + outb(0, DMA1_CLR_MASK_REG); + /* we need to figure out why this fails on the SX164 */ + outb(0, DMA2_CLR_MASK_REG); +#endif /* !SX164 && !DP264 */ +/* end FIXMEs */ #if defined(CONFIG_ALPHA_SABLE) sable_init_IRQ(); @@ -1431,17 +1962,21 @@ init_IRQ(void) noritake_init_IRQ(); #elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) alcor_and_xlt_init_IRQ(); -#elif (defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)) \ - && defined(CONFIG_ALPHA_SRM) - /* Disable all the PCI interrupts? Otherwise, everthing was - done by SRM already. */ #elif defined(CONFIG_ALPHA_MIKASA) mikasa_init_IRQ(); +#elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || \ + defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164) || \ + defined(CONFIG_ALPHA_EB164) + init_IRQ_35(); #elif defined(CONFIG_ALPHA_RUFFIAN) ruffian_init_IRQ(); -#elif NR_IRQS == 33 - init_IRQ_33(); -#elif NR_IRQS == 32 +#elif defined(CONFIG_ALPHA_DP264) + dp264_init_IRQ(); +#elif defined(CONFIG_ALPHA_RAWHIDE) + rawhide_init_IRQ(); +#elif defined(CONFIG_ALPHA_TAKARA) + takara_init_IRQ(); +#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P) init_IRQ_32(); #elif NR_IRQS == 16 init_IRQ_16(); diff --git a/arch/alpha/kernel/lca.c b/arch/alpha/kernel/lca.c index 2a39a1cf9..a0b8aea8d 100644 --- a/arch/alpha/kernel/lca.c +++ b/arch/alpha/kernel/lca.c @@ -6,8 +6,8 @@ * bios code. */ #include <linux/kernel.h> +#include <linux/config.h> #include <linux/types.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <asm/ptrace.h> @@ -44,6 +44,11 @@ #define MCHK_K_SIO_IOCHK 0x206 /* all platforms so far */ #define MCHK_K_DCSR 0x208 /* all but Noname */ +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int LCA_DMA_WIN_BASE = LCA_DMA_WIN_BASE_DEFAULT; +unsigned int LCA_DMA_WIN_SIZE = LCA_DMA_WIN_SIZE_DEFAULT; +#endif /* SRM_SETUP */ + /* * Given a bus, device, and function number, compute resulting * configuration space address and setup the LCA_IOC_CONF register @@ -100,11 +105,11 @@ static int mk_conf_addr(unsigned char bus, unsigned char device_fn, return -1; } - *((vulp) LCA_IOC_CONF) = 0; + *(vulp)LCA_IOC_CONF = 0; addr = (1 << (11 + device)) | (func << 8) | where; } else { /* type 1 configuration cycle: */ - *((vulp) LCA_IOC_CONF) = 1; + *(vulp)LCA_IOC_CONF = 1; addr = (bus << 16) | (device_fn << 8) | where; } *pci_addr = addr; @@ -130,7 +135,7 @@ static unsigned int conf_read(unsigned long addr) value = *(vuip)addr; draina(); - stat0 = *((unsigned long*)LCA_IOC_STAT0); + stat0 = *(vulp)LCA_IOC_STAT0; if (stat0 & LCA_IOC_STAT0_ERR) { code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) & LCA_IOC_STAT0_CODE_MASK); @@ -167,7 +172,7 @@ static void conf_write(unsigned long addr, unsigned int value) *(vuip)addr = value; draina(); - stat0 = *((unsigned long*)LCA_IOC_STAT0); + stat0 = *(vulp)LCA_IOC_STAT0; if (stat0 & LCA_IOC_STAT0_ERR) { code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) & LCA_IOC_STAT0_CODE_MASK); @@ -287,6 +292,40 @@ int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, unsigned long lca_init(unsigned long mem_start, unsigned long mem_end) { +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 0 for enabled and mapped to 0 */ + if ((*(vulp)LCA_IOC_W_BASE0 & (1UL<<33)) && + (*(vulp)LCA_IOC_T_BASE0 == 0)) + { + LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE0 & 0xffffffffUL; + LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK0 & 0xffffffffUL; + LCA_DMA_WIN_SIZE += 1; +#if 1 + printk("lca_init: using Window 0 settings\n"); + printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n", + *(vulp)LCA_IOC_W_BASE0, + *(vulp)LCA_IOC_W_MASK0, + *(vulp)LCA_IOC_T_BASE0); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if ((*(vulp)LCA_IOC_W_BASE1 & (1UL<<33)) && + (*(vulp)LCA_IOC_T_BASE1 == 0)) + { + LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE1 & 0xffffffffUL; + LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK1 & 0xffffffffUL; + LCA_DMA_WIN_SIZE += 1; +#if 1 + printk("lca_init: using Window 1 settings\n"); + printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n", + *(vulp)LCA_IOC_W_BASE1, + *(vulp)LCA_IOC_W_MASK1, + *(vulp)LCA_IOC_T_BASE1); +#endif + } + else /* we must use our defaults... */ +#endif /* SRM_SETUP */ + { /* * Set up the PCI->physical memory translation windows. * For now, window 1 is disabled. In the future, we may @@ -294,9 +333,11 @@ unsigned long lca_init(unsigned long mem_start, unsigned long mem_end) * goes at 1 GB and is 1 GB large. */ *(vulp)LCA_IOC_W_BASE1 = 0UL<<33; + *(vulp)LCA_IOC_W_BASE0 = 1UL<<33 | LCA_DMA_WIN_BASE; *(vulp)LCA_IOC_W_MASK0 = LCA_DMA_WIN_SIZE - 1; *(vulp)LCA_IOC_T_BASE0 = 0; + } /* * Disable PCI parity for now. The NCR53c810 chip has diff --git a/arch/alpha/kernel/mcpcia.c b/arch/alpha/kernel/mcpcia.c new file mode 100644 index 000000000..6a9dab59a --- /dev/null +++ b/arch/alpha/kernel/mcpcia.c @@ -0,0 +1,977 @@ +/* + * Code common to all MCbus-PCI adaptor chipsets + * + * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com). + * + */ +#include <linux/kernel.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/hwrpb.h> +#include <asm/ptrace.h> +#include <asm/mmu_context.h> +#include <asm/delay.h> + +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the i/o controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ + +extern struct hwrpb_struct *hwrpb; +extern asmlinkage void wrmces(unsigned long mces); + +/* + * BIOS32-style PCI interface: + */ + +#ifdef CONFIG_ALPHA_MCPCIA + +#undef DEBUG_CFG + +#ifdef DEBUG_CFG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + +#undef DEBUG_PCI + +#ifdef DEBUG_PCI +# define DBG_PCI(args) printk args +#else +# define DBG_PCI(args) +#endif + +#define DEBUG_MCHECK + +#ifdef DEBUG_MCHECK +# define DBG_MCK(args) printk args +# define DEBUG_MCHECK_DUMP +#else +# define DBG_MCK(args) +#endif + +#define vuip volatile unsigned int * +#define vulp volatile unsigned long * + +static volatile unsigned int MCPCIA_mcheck_expected[NR_CPUS]; +static volatile unsigned int MCPCIA_mcheck_taken[NR_CPUS]; +static unsigned int MCPCIA_jd[NR_CPUS]; + +#define MCPCIA_MAX_HOSES 2 +static int mcpcia_num_hoses = 0; + +static int pci_probe_enabled = 0; /* disable to start */ + +static struct linux_hose_info *mcpcia_root = NULL, *mcpcia_last_hose; + +struct linux_hose_info *bus2hose[256]; + +static inline unsigned long long_align(unsigned long addr) +{ + return ((addr + (sizeof(unsigned long) - 1)) & + ~(sizeof(unsigned long) - 1)); +} + +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int MCPCIA_DMA_WIN_BASE = MCPCIA_DMA_WIN_BASE_DEFAULT; +unsigned int MCPCIA_DMA_WIN_SIZE = MCPCIA_DMA_WIN_SIZE_DEFAULT; +unsigned long mcpcia_sm_base_r1, mcpcia_sm_base_r2, mcpcia_sm_base_r3; +#endif /* SRM_SETUP */ + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the MCPCIA_HAXR2 register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static unsigned int +conf_read(unsigned long addr, unsigned char type1, + struct linux_hose_info *hose) +{ + unsigned long flags; + unsigned long hoseno = hose->pci_hose_index; + unsigned int stat0, value, temp, cpu; + + cpu = smp_processor_id(); + + save_and_cli(flags); + + DBG_CFG(("conf_read(addr=0x%lx, type1=%d, hose=%d)\n", + addr, type1, hoseno)); + + /* reset status register to avoid losing errors: */ + stat0 = *(vuip)MCPCIA_CAP_ERR(hoseno); + *(vuip)MCPCIA_CAP_ERR(hoseno) = stat0; mb(); + temp = *(vuip)MCPCIA_CAP_ERR(hoseno); + DBG_CFG(("conf_read: MCPCIA CAP_ERR(%d) was 0x%x\n", hoseno, stat0)); + + mb(); + draina(); + MCPCIA_mcheck_expected[cpu] = 1; + MCPCIA_mcheck_taken[cpu] = 0; + mb(); + /* access configuration space: */ + value = *((vuip)addr); + mb(); + mb(); /* magic */ + if (MCPCIA_mcheck_taken[cpu]) { + MCPCIA_mcheck_taken[cpu] = 0; + value = 0xffffffffU; + mb(); + } + MCPCIA_mcheck_expected[cpu] = 0; + mb(); + + DBG_CFG(("conf_read(): finished\n")); + + restore_flags(flags); + return value; +} + + +static void +conf_write(unsigned long addr, unsigned int value, unsigned char type1, + struct linux_hose_info *hose) +{ + unsigned long flags; + unsigned long hoseno = hose->pci_hose_index; + unsigned int stat0, temp, cpu; + + cpu = smp_processor_id(); + + save_and_cli(flags); /* avoid getting hit by machine check */ + + /* reset status register to avoid losing errors: */ + stat0 = *(vuip)MCPCIA_CAP_ERR(hoseno); + *(vuip)MCPCIA_CAP_ERR(hoseno) = stat0; mb(); + temp = *(vuip)MCPCIA_CAP_ERR(hoseno); + DBG_CFG(("conf_write: MCPCIA CAP_ERR(%d) was 0x%x\n", hoseno, stat0)); + + draina(); + MCPCIA_mcheck_expected[cpu] = 1; + mb(); + /* access configuration space: */ + *((vuip)addr) = value; + mb(); + mb(); /* magic */ + temp = *(vuip)MCPCIA_CAP_ERR(hoseno); /* read to force the write */ + MCPCIA_mcheck_expected[cpu] = 0; + mb(); + + DBG_CFG(("conf_write(): finished\n")); + restore_flags(flags); +} + +static int mk_conf_addr(struct linux_hose_info *hose, + unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned long *pci_addr, + unsigned char *type1) +{ + unsigned long addr; + + if (!pci_probe_enabled) /* if doing standard pci_init(), ignore */ + return -1; + + DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," + " pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + /* type 1 configuration cycle for *ALL* busses */ + *type1 = 1; + + if (hose->pci_first_busno == bus) + bus = 0; + addr = (bus << 16) | (device_fn << 8) | (where); + addr <<= 5; /* swizzle for SPARSE */ + addr |= hose->pci_config_space; + + *pci_addr = addr; + DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + + +int hose_read_config_byte (struct linux_hose_info *hose, + unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long addr; + unsigned char type1; + + *value = 0xff; + + if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= 0x00; /* or in length */ + + *value = conf_read(addr, type1, hose) >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + + +int hose_read_config_word (struct linux_hose_info *hose, + unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned long addr; + unsigned char type1; + + *value = 0xffff; + + if (where & 0x1) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= 0x08; /* or in length */ + + *value = conf_read(addr, type1, hose) >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + + +int hose_read_config_dword (struct linux_hose_info *hose, + unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long addr; + unsigned char type1; + + *value = 0xffffffff; + + if (where & 0x3) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + addr |= 0x18; /* or in length */ + + *value = conf_read(addr, type1, hose); + return PCIBIOS_SUCCESSFUL; +} + + +int hose_write_config_byte (struct linux_hose_info *hose, + unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= 0x00; /* or in length */ + + conf_write(addr, value << ((where & 3) * 8), type1, hose); + return PCIBIOS_SUCCESSFUL; +} + + +int hose_write_config_word (struct linux_hose_info *hose, + unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= 0x08; /* or in length */ + + conf_write(addr, value << ((where & 3) * 8), type1, hose); + return PCIBIOS_SUCCESSFUL; +} + + +int hose_write_config_dword (struct linux_hose_info *hose, + unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= 0x18; /* or in length */ + + conf_write(addr, value << ((where & 3) * 8), type1, hose); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_read_config_byte (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char *value) +{ + return hose_read_config_byte(bus2hose[bus], bus, devfn, where, value); +} + +int pcibios_read_config_word (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short *value) +{ + return hose_read_config_word(bus2hose[bus], bus, devfn, where, value); +} + +int pcibios_read_config_dword (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int *value) +{ + return hose_read_config_dword(bus2hose[bus], bus, devfn, where, value); +} + +int pcibios_write_config_byte (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char value) +{ + return hose_write_config_byte(bus2hose[bus], bus, devfn, where, value); +} + +int pcibios_write_config_word (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short value) +{ + return hose_write_config_word(bus2hose[bus], bus, devfn, where, value); +} + +int pcibios_write_config_dword (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int value) +{ + return hose_write_config_dword(bus2hose[bus], bus, devfn, where, value); +} + +unsigned long mcpcia_init(unsigned long mem_start, unsigned long mem_end) +{ + struct linux_hose_info *hose; + unsigned int mcpcia_err; + unsigned int pci_rev; + int h; + + mem_start = long_align(mem_start); + + for (h = 0; h < NR_CPUS; h++) { + MCPCIA_mcheck_expected[h] = 0; + MCPCIA_mcheck_taken[h] = 0; + } + + /* first, find how many hoses we have */ + for (h = 0; h < MCPCIA_MAX_HOSES; h++) { + pci_rev = *(vuip)MCPCIA_REV(h); +#if 0 + printk("mcpcia_init: got 0x%x for PCI_REV for hose %d\n", + pci_rev, h); +#endif + if ((pci_rev >> 16) == PCI_CLASS_BRIDGE_HOST) { + mcpcia_num_hoses++; + + hose = (struct linux_hose_info *)mem_start; + mem_start = long_align(mem_start + sizeof(*hose)); + + memset(hose, 0, sizeof(*hose)); + + if (mcpcia_root) + mcpcia_last_hose->next = hose; + else + mcpcia_root = hose; + mcpcia_last_hose = hose; + + hose->pci_io_space = MCPCIA_IO(h); + hose->pci_mem_space = MCPCIA_DENSE(h); + hose->pci_config_space = MCPCIA_CONF(h); + hose->pci_sparse_space = MCPCIA_SPARSE(h); + hose->pci_hose_index = h; + hose->pci_first_busno = 255; + hose->pci_last_busno = 0; + } + } + +#if 1 + printk("mcpcia_init: found %d hoses\n", mcpcia_num_hoses); +#endif + + /* now do init for each hose */ + for (hose = mcpcia_root; hose; hose = hose->next) { + h = hose->pci_hose_index; +#if 0 +#define PRINTK printk +PRINTK("mcpcia_init: -------- hose %d --------\n",h); +PRINTK("mcpcia_init: MCPCIA_REV 0x%x\n", *(vuip)MCPCIA_REV(h)); +PRINTK("mcpcia_init: MCPCIA_WHOAMI 0x%x\n", *(vuip)MCPCIA_WHOAMI(h)); +PRINTK("mcpcia_init: MCPCIA_HAE_MEM 0x%x\n", *(vuip)MCPCIA_HAE_MEM(h)); +PRINTK("mcpcia_init: MCPCIA_HAE_IO 0x%x\n", *(vuip)MCPCIA_HAE_IO(h)); +PRINTK("mcpcia_init: MCPCIA_HAE_DENSE 0x%x\n", *(vuip)MCPCIA_HAE_DENSE(h)); +PRINTK("mcpcia_init: MCPCIA_INT_CTL 0x%x\n", *(vuip)MCPCIA_INT_CTL(h)); +PRINTK("mcpcia_init: MCPCIA_INT_REQ 0x%x\n", *(vuip)MCPCIA_INT_REQ(h)); +PRINTK("mcpcia_init: MCPCIA_INT_TARG 0x%x\n", *(vuip)MCPCIA_INT_TARG(h)); +PRINTK("mcpcia_init: MCPCIA_INT_ADR 0x%x\n", *(vuip)MCPCIA_INT_ADR(h)); +PRINTK("mcpcia_init: MCPCIA_INT_ADR_EXT 0x%x\n", *(vuip)MCPCIA_INT_ADR_EXT(h)); +PRINTK("mcpcia_init: MCPCIA_INT_MASK0 0x%x\n", *(vuip)MCPCIA_INT_MASK0(h)); +PRINTK("mcpcia_init: MCPCIA_INT_MASK1 0x%x\n", *(vuip)MCPCIA_INT_MASK1(h)); +PRINTK("mcpcia_init: MCPCIA_HBASE 0x%x\n", *(vuip)MCPCIA_HBASE(h)); +#endif + + /* + * Set up error reporting. Make sure CPU_PE is OFF in the mask. + */ +#if 0 + mcpcia_err = *(vuip)MCPCIA_ERR_MASK(h); + mcpcia_err &= ~4; + *(vuip)MCPCIA_ERR_MASK(h) = mcpcia_err; + mb(); + mcpcia_err = *(vuip)MCPCIA_ERR_MASK; +#endif + + mcpcia_err = *(vuip)MCPCIA_CAP_ERR(h); + mcpcia_err |= 0x0006; /* master/target abort */ + *(vuip)MCPCIA_CAP_ERR(h) = mcpcia_err; + mb() ; + mcpcia_err = *(vuip)MCPCIA_CAP_ERR(h); + +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 0 for enabled and mapped to 0 */ + if (((*(vuip)MCPCIA_W0_BASE(h) & 3) == 1) && + (*(vuip)MCPCIA_T0_BASE(h) == 0) && + ((*(vuip)MCPCIA_W0_MASK(h) & 0xfff00000U) > 0x0ff00000U)) + { + MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W0_BASE(h) & 0xfff00000U; + MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W0_MASK(h) & 0xfff00000U; + MCPCIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("mcpcia_init: using Window 0 settings\n"); + printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)MCPCIA_W0_BASE(h), + *(vuip)MCPCIA_W0_MASK(h), + *(vuip)MCPCIA_T0_BASE(h)); +#endif + } + else /* check window 1 for enabled and mapped to 0 */ + if (((*(vuip)MCPCIA_W1_BASE(h) & 3) == 1) && + (*(vuip)MCPCIA_T1_BASE(h) == 0) && + ((*(vuip)MCPCIA_W1_MASK(h) & 0xfff00000U) > 0x0ff00000U)) +{ + MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W1_BASE(h) & 0xfff00000U; + MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W1_MASK(h) & 0xfff00000U; + MCPCIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("mcpcia_init: using Window 1 settings\n"); + printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)MCPCIA_W1_BASE(h), + *(vuip)MCPCIA_W1_MASK(h), + *(vuip)MCPCIA_T1_BASE(h)); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if (((*(vuip)MCPCIA_W2_BASE(h) & 3) == 1) && + (*(vuip)MCPCIA_T2_BASE(h) == 0) && + ((*(vuip)MCPCIA_W2_MASK(h) & 0xfff00000U) > 0x0ff00000U)) + { + MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W2_BASE(h) & 0xfff00000U; + MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W2_MASK(h) & 0xfff00000U; + MCPCIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("mcpcia_init: using Window 2 settings\n"); + printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)MCPCIA_W2_BASE(h), + *(vuip)MCPCIA_W2_MASK(h), + *(vuip)MCPCIA_T2_BASE(h)); +#endif + } + else /* check window 3 for enabled and mapped to 0 */ + if (((*(vuip)MCPCIA_W3_BASE(h) & 3) == 1) && + (*(vuip)MCPCIA_T3_BASE(h) == 0) && + ((*(vuip)MCPCIA_W3_MASK(h) & 0xfff00000U) > 0x0ff00000U)) + { + MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W3_BASE(h) & 0xfff00000U; + MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W3_MASK(h) & 0xfff00000U; + MCPCIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("mcpcia_init: using Window 3 settings\n"); + printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)MCPCIA_W3_BASE(h), + *(vuip)MCPCIA_W3_MASK(h), + *(vuip)MCPCIA_T3_BASE(h)); +#endif + } + else /* we must use our defaults which were pre-initialized... */ +#endif /* SRM_SETUP */ + { + /* + * Set up the PCI->physical memory translation windows. + * For now, windows 1,2 and 3 are disabled. In the future, we may + * want to use them to do scatter/gather DMA. Window 0 + * goes at 1 GB and is 1 GB large. + */ + + *(vuip)MCPCIA_W0_BASE(h) = 1U | (MCPCIA_DMA_WIN_BASE & 0xfff00000U); + *(vuip)MCPCIA_W0_MASK(h) = (MCPCIA_DMA_WIN_SIZE - 1) & 0xfff00000U; + *(vuip)MCPCIA_T0_BASE(h) = 0; + + *(vuip)MCPCIA_W1_BASE(h) = 0x0 ; + *(vuip)MCPCIA_W2_BASE(h) = 0x0 ; + *(vuip)MCPCIA_W3_BASE(h) = 0x0 ; + + *(vuip)MCPCIA_HBASE(h) = 0x0 ; + mb(); + } + + /* + * check ASN in HWRPB for validity, report if bad + */ + if (hwrpb->max_asn != MAX_ASN) { + printk("mcpcia_init: max ASN from HWRPB is bad (0x%lx)\n", + hwrpb->max_asn); + hwrpb->max_asn = MAX_ASN; + } + +#if 0 + { + unsigned int mcpcia_int_ctl = *((vuip)MCPCIA_INT_CTL(h)); + printk("mcpcia_init: INT_CTL was 0x%x\n", mcpcia_int_ctl); + *(vuip)MCPCIA_INT_CTL(h) = 1U; mb(); + mcpcia_int_ctl = *(vuip)MCPCIA_INT_CTL(h); + } +#endif + + { + unsigned int mcpcia_hae_mem = *(vuip)MCPCIA_HAE_MEM(h); + unsigned int mcpcia_hae_io = *(vuip)MCPCIA_HAE_IO(h); +#if 0 + printk("mcpcia_init: HAE_MEM was 0x%x\n", mcpcia_hae_mem); + printk("mcpcia_init: HAE_IO was 0x%x\n", mcpcia_hae_io); +#endif +#ifdef CONFIG_ALPHA_SRM_SETUP + /* + sigh... For the SRM setup, unless we know apriori what the HAE + contents will be, we need to setup the arbitrary region bases + so we can test against the range of addresses and tailor the + region chosen for the SPARSE memory access. + + see include/asm-alpha/mcpcia.h for the SPARSE mem read/write + */ + mcpcia_sm_base_r1 = (mcpcia_hae_mem ) & 0xe0000000UL;/* reg 1 */ + mcpcia_sm_base_r2 = (mcpcia_hae_mem << 16) & 0xf8000000UL;/* reg 2 */ + mcpcia_sm_base_r3 = (mcpcia_hae_mem << 24) & 0xfc000000UL;/* reg 3 */ + /* + Set the HAE cache, so that setup_arch() code + will use the SRM setting always. Our readb/writeb + code in mcpcia.h expects never to have to change + the contents of the HAE. + */ + hae.cache = mcpcia_hae_mem; +#else /* SRM_SETUP */ + *(vuip)MCPCIA_HAE_MEM(h) = 0U; mb(); + mcpcia_hae_mem = *(vuip)MCPCIA_HAE_MEM(h); + *(vuip)MCPCIA_HAE_IO(h) = 0; mb(); + mcpcia_hae_io = *(vuip)MCPCIA_HAE_IO(h); +#endif /* SRM_SETUP */ + } + } /* end for-loop on hoses */ + return mem_start; +} + +int mcpcia_pci_clr_err(int h) +{ + unsigned int cpu = smp_processor_id(); + + MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(h); +#if 0 + DBG_MCK(("MCPCIA_pci_clr_err: MCPCIA CAP_ERR(%d) after read 0x%x\n", + h, MCPCIA_jd[cpu])); +#endif + *(vuip)MCPCIA_CAP_ERR(h) = 0xffffffff; mb(); /* clear them all */ + MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(h); + return 0; +} + +static void +mcpcia_print_uncorrectable(struct el_MCPCIA_uncorrected_frame_mcheck *logout) +{ + struct el_common_EV5_uncorrectable_mcheck *frame; + int i; + + frame = &logout->procdata; + + /* Print PAL fields */ + for (i = 0; i < 24; i += 2) { + printk("\tpal temp[%d-%d]\t\t= %16lx %16lx\n\r", + i, i+1, frame->paltemp[i], frame->paltemp[i+1]); + } + for (i = 0; i < 8; i += 2) { + printk("\tshadow[%d-%d]\t\t= %16lx %16lx\n\r", + i, i+1, frame->shadow[i], + frame->shadow[i+1]); + } + printk("\tAddr of excepting instruction\t= %16lx\n\r", + frame->exc_addr); + printk("\tSummary of arithmetic traps\t= %16lx\n\r", + frame->exc_sum); + printk("\tException mask\t\t\t= %16lx\n\r", + frame->exc_mask); + printk("\tBase address for PALcode\t= %16lx\n\r", + frame->pal_base); + printk("\tInterrupt Status Reg\t\t= %16lx\n\r", + frame->isr); + printk("\tCURRENT SETUP OF EV5 IBOX\t= %16lx\n\r", + frame->icsr); + printk("\tI-CACHE Reg %s parity error\t= %16lx\n\r", + (frame->ic_perr_stat & 0x800L) ? + "Data" : "Tag", + frame->ic_perr_stat); + printk("\tD-CACHE error Reg\t\t= %16lx\n\r", + frame->dc_perr_stat); + if (frame->dc_perr_stat & 0x2) { + switch (frame->dc_perr_stat & 0x03c) { + case 8: + printk("\t\tData error in bank 1\n\r"); + break; + case 4: + printk("\t\tData error in bank 0\n\r"); + break; + case 20: + printk("\t\tTag error in bank 1\n\r"); + break; + case 10: + printk("\t\tTag error in bank 0\n\r"); + break; + } + } + printk("\tEffective VA\t\t\t= %16lx\n\r", + frame->va); + printk("\tReason for D-stream\t\t= %16lx\n\r", + frame->mm_stat); + printk("\tEV5 SCache address\t\t= %16lx\n\r", + frame->sc_addr); + printk("\tEV5 SCache TAG/Data parity\t= %16lx\n\r", + frame->sc_stat); + printk("\tEV5 BC_TAG_ADDR\t\t\t= %16lx\n\r", + frame->bc_tag_addr); + printk("\tEV5 EI_ADDR: Phys addr of Xfer\t= %16lx\n\r", + frame->ei_addr); + printk("\tFill Syndrome\t\t\t= %16lx\n\r", + frame->fill_syndrome); + printk("\tEI_STAT reg\t\t\t= %16lx\n\r", + frame->ei_stat); + printk("\tLD_LOCK\t\t\t\t= %16lx\n\r", + frame->ld_lock); +} + +void mcpcia_machine_check(unsigned long type, unsigned long la_ptr, + struct pt_regs * regs) +{ +#if 0 + printk("mcpcia machine check ignored\n") ; +#else + struct el_common *mchk_header; + struct el_MCPCIA_uncorrected_frame_mcheck *mchk_logout; + unsigned int cpu = smp_processor_id(); + int h = 0; + + mchk_header = (struct el_common *)la_ptr; + mchk_logout = (struct el_MCPCIA_uncorrected_frame_mcheck *)la_ptr; + +#if 0 + DBG_MCK(("mcpcia_machine_check: type=0x%lx la_ptr=0x%lx\n", + type, la_ptr)); + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->size, mchk_header->proc_offset, + mchk_header->sys_offset)); +#endif + /* + * Check if machine check is due to a badaddr() and if so, + * ignore the machine check. + */ + mb(); + mb(); /* magic */ + if (MCPCIA_mcheck_expected[cpu]) { +#if 0 + DBG_MCK(("MCPCIA machine check expected\n")); +#endif + MCPCIA_mcheck_expected[cpu] = 0; + MCPCIA_mcheck_taken[cpu] = 1; + mb(); + mb(); /* magic */ + draina(); + mcpcia_pci_clr_err(h); + wrmces(0x7); + mb(); + } +#if 1 + else { + printk("MCPCIA machine check NOT expected on CPU %d\n", cpu); + DBG_MCK(("mcpcia_machine_check: type=0x%lx pc=0x%lx" + " code=0x%lx\n", + type, regs->pc, mchk_header->code)); + + MCPCIA_mcheck_expected[cpu] = 0; + MCPCIA_mcheck_taken[cpu] = 1; + mb(); + mb(); /* magic */ + draina(); + mcpcia_pci_clr_err(h); + wrmces(0x7); + mb(); +#ifdef DEBUG_MCHECK_DUMP + if (type == 0x620) + printk("MCPCIA machine check: system CORRECTABLE!\n"); + else if (type == 0x630) + printk("MCPCIA machine check: processor CORRECTABLE!\n"); + else + mcpcia_print_uncorrectable(mchk_logout); +#endif /* DEBUG_MCHECK_DUMP */ + } +#endif +#endif +} + +/*==========================================================================*/ + +#define PRIMARY(b) ((b)&0xff) +#define SECONDARY(b) (((b)>>8)&0xff) +#define SUBORDINATE(b) (((b)>>16)&0xff) + +static int +hose_scan_bridges(struct linux_hose_info *hose, unsigned char bus) +{ + unsigned int devfn, l, class; + unsigned char hdr_type = 0; + unsigned int found = 0; + + for (devfn = 0; devfn < 0xff; ++devfn) { + if (PCI_FUNC(devfn) == 0) { + hose_read_config_byte(hose, bus, devfn, + PCI_HEADER_TYPE, &hdr_type); + } else if (!(hdr_type & 0x80)) { + /* not a multi-function device */ + continue; + } + + /* Check if there is anything here. */ + hose_read_config_dword(hose, bus, devfn, PCI_VENDOR_ID, &l); + if (l == 0xffffffff || l == 0x00000000) { + hdr_type = 0; + continue; + } + + /* See if this is a bridge device. */ + hose_read_config_dword(hose, bus, devfn, + PCI_CLASS_REVISION, &class); + + if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) { + unsigned int busses; + + found++; + + hose_read_config_dword(hose, bus, devfn, + PCI_PRIMARY_BUS, &busses); + +DBG_PCI(("hose_scan_bridges: hose %d bus %d slot %d busses 0x%x\n", + hose->pci_hose_index, bus, PCI_SLOT(devfn), busses)); + /* + * do something with first_busno and last_busno + */ + if (hose->pci_first_busno > PRIMARY(busses)) { + hose->pci_first_busno = PRIMARY(busses); +DBG_PCI(("hose_scan_bridges: hose %d bus %d slot %d change first to %d\n", + hose->pci_hose_index, bus, PCI_SLOT(devfn), PRIMARY(busses))); + } + if (hose->pci_last_busno < SUBORDINATE(busses)) { + hose->pci_last_busno = SUBORDINATE(busses); +DBG_PCI(("hose_scan_bridges: hose %d bus %d slot %d change last to %d\n", + hose->pci_hose_index, bus, PCI_SLOT(devfn), SUBORDINATE(busses))); + } + /* + * Now scan everything underneath the bridge. + */ + hose_scan_bridges(hose, SECONDARY(busses)); + } + } + return found; +} + +static void +hose_reconfigure_bridges(struct linux_hose_info *hose, unsigned char bus) +{ + unsigned int devfn, l, class; + unsigned char hdr_type = 0; + + for (devfn = 0; devfn < 0xff; ++devfn) { + if (PCI_FUNC(devfn) == 0) { + hose_read_config_byte(hose, bus, devfn, + PCI_HEADER_TYPE, &hdr_type); + } else if (!(hdr_type & 0x80)) { + /* not a multi-function device */ + continue; + } + + /* Check if there is anything here. */ + hose_read_config_dword(hose, bus, devfn, PCI_VENDOR_ID, &l); + if (l == 0xffffffff || l == 0x00000000) { + hdr_type = 0; + continue; + } + + /* See if this is a bridge device. */ + hose_read_config_dword(hose, bus, devfn, + PCI_CLASS_REVISION, &class); + + if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) { + unsigned int busses; + + hose_read_config_dword(hose, bus, devfn, + PCI_PRIMARY_BUS, &busses); + + /* + * First reconfigure everything underneath the bridge. + */ + hose_reconfigure_bridges(hose, (busses >> 8) & 0xff); + + /* + * Unconfigure this bridges bus numbers, + * pci_scan_bus() will fix this up properly. + */ + busses &= 0xff000000; + hose_write_config_dword(hose, bus, devfn, + PCI_PRIMARY_BUS, busses); + } + } +} + +static void mcpcia_fixup_busno(struct linux_hose_info *hose, unsigned char bus) +{ + unsigned int nbus; + + /* + * First, scan for all bridge devices underneath this hose, + * to determine the first and last busnos. + */ + if (!hose_scan_bridges(hose, 0)) { + /* none found, exit */ + hose->pci_first_busno = bus; + hose->pci_last_busno = bus; + } else { + /* + * Reconfigure all bridge devices underneath this hose. + */ + hose_reconfigure_bridges(hose, hose->pci_first_busno); + } + + /* + * Now reconfigure the hose to it's new bus number and set up + * our bus2hose mapping for this hose. + */ + nbus = hose->pci_last_busno - hose->pci_first_busno; + + hose->pci_first_busno = bus; + +DBG_PCI(("mcpcia_fixup_busno: hose %d startbus %d nbus %d\n", + hose->pci_hose_index, bus, nbus)); + + do { + bus2hose[bus++] = hose; + } while (nbus-- > 0); +} + +static void mcpcia_probe(struct linux_hose_info *hose, + unsigned long *mem_start) +{ + static struct pci_bus *pchain = NULL; + struct pci_bus *pbus = &hose->pci_bus; + static unsigned char busno = 0; + + /* Hoses include child PCI bridges in bus-range property, + * but we don't scan each of those ourselves, Linux generic PCI + * probing code will find child bridges and link them into this + * hose's root PCI device hierarchy. + */ + + pbus->number = pbus->secondary = busno; + pbus->sysdata = hose; + + mcpcia_fixup_busno(hose, busno); + + pbus->subordinate = pci_scan_bus(pbus, mem_start); /* the original! */ + + /* + * Set the maximum subordinate bus of this hose. + */ + hose->pci_last_busno = pbus->subordinate; +#if 0 + hose_write_config_byte(hose, busno, 0, 0x41, hose->pci_last_busno); +#endif + busno = pbus->subordinate + 1; + + /* + * Fixup the chain of primary PCI busses. + */ + if (pchain) { + pchain->next = &hose->pci_bus; + pchain = pchain->next; + } else { + pchain = &pci_root; + memcpy(pchain, &hose->pci_bus, sizeof(pci_root)); + } +} + +void mcpcia_fixup(void) +{ + struct linux_hose_info *hose; + + /* turn on Config space access finally! */ + pci_probe_enabled = 1; + + /* for each hose, probe and setup the devices on the hose */ + for (hose = mcpcia_root; hose; hose = hose->next) { + mcpcia_probe(hose, &memory_start); + } +} +#endif /* CONFIG_ALPHA_MCPCIA */ diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index f8146c54f..401a7af46 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -66,10 +66,49 @@ asmlinkage int sys_sethae(unsigned long hae, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) { +#if !defined(CONFIG_ALPHA_TSUNAMI) (®s)->hae = hae; +#endif return 0; } +#ifdef __SMP__ +/* This is being executed in task 0 'user space'. */ +#define resched_needed() 1 +int cpu_idle(void *unused) +{ + extern volatile int smp_commenced; + + current->priority = -100; + while (1) { + /* + * tq_scheduler currently assumes we're running in a process + * context (ie that we hold the kernel lock..) + */ + if (tq_scheduler) { + lock_kernel(); + run_task_queue(&tq_scheduler); + unlock_kernel(); + } + /* endless idle loop with no priority at all */ + current->counter = -100; + if (!smp_commenced || resched_needed()) { + schedule(); + } + } +} + +asmlinkage int sys_idle(void) +{ + if(current->pid != 0) + return -EPERM; + + cpu_idle(NULL); + return 0; +} + +#else /* __SMP__ */ + asmlinkage int sys_idle(void) { int ret = -EPERM; @@ -88,6 +127,12 @@ out: unlock_kernel(); return ret; } +#endif /* __SMP__ */ + +#if defined(CONFIG_ALPHA_SRM_SETUP) +extern void reset_for_srm(void); +extern unsigned long srm_hae; +#endif static void finish_shutdown(void) { @@ -96,8 +141,8 @@ static void finish_shutdown(void) unsigned long flags; /* i'm not sure if i really need to disable interrupts here */ - save_flags(flags); - cli(); + save_and_cli(flags); + /* reset periodic interrupt frequency */ CMOS_WRITE(0x26, RTC_FREQ_SELECT); @@ -131,6 +176,10 @@ void machine_restart(char * __unused) /* flags |= 0x0000000000030000UL; *//* this is "warm bootstrap" */ cpup->flags = flags; mb(); +#if defined(CONFIG_ALPHA_SRM_SETUP) + reset_for_srm(); + set_hae(srm_hae); +#endif #endif /* SRM */ finish_shutdown(); @@ -150,6 +199,10 @@ void machine_halt(void) flags |= 0x0000000000040000UL; /* this is "remain halted" */ cpup->flags = flags; mb(); +#if defined(CONFIG_ALPHA_SRM_SETUP) + reset_for_srm(); + set_hae(srm_hae); +#endif finish_shutdown(); #endif /* SRM */ @@ -228,6 +281,7 @@ int alpha_clone(unsigned long clone_flags, unsigned long usp, } extern void ret_from_sys_call(void); +extern void ret_from_smpfork(void); /* * Copy an alpha thread.. * @@ -258,7 +312,11 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, stack = ((struct switch_stack *) regs) - 1; childstack = ((struct switch_stack *) childregs) - 1; *childstack = *stack; +#ifdef __SMP__ + childstack->r26 = (unsigned long) ret_from_smpfork; +#else childstack->r26 = (unsigned long) ret_from_sys_call; +#endif p->tss.usp = usp; p->tss.ksp = (unsigned long) childstack; p->tss.pal_flags = 1; /* set FEN, clear everything else */ diff --git a/arch/alpha/kernel/pyxis.c b/arch/alpha/kernel/pyxis.c index b4c5d188e..de3814b66 100644 --- a/arch/alpha/kernel/pyxis.c +++ b/arch/alpha/kernel/pyxis.c @@ -7,7 +7,6 @@ #include <linux/config.h> /* CONFIG_ALPHA_RUFFIAN. */ #include <linux/kernel.h> #include <linux/types.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/sched.h> @@ -17,13 +16,12 @@ #include <asm/ptrace.h> #include <asm/mmu_context.h> -/* NOTE: Herein are back-to-back mb insns. They are magic. - A plausible explanation is that the i/o controler does not properly +/* NOTE: Herein are back-to-back mb instructions. They are magic. + One plausible explanation is that the I/O controller does not properly handle the system transaction. Another involves timing. Ho hum. */ extern struct hwrpb_struct *hwrpb; extern asmlinkage void wrmces(unsigned long mces); -extern int alpha_sys_type; /* * BIOS32-style PCI interface: @@ -38,6 +36,7 @@ extern int alpha_sys_type; #define DEBUG_MCHECK #ifdef DEBUG_MCHECK # define DBG_MCK(args) printk args +#define DEBUG_MCHECK_DUMP #else # define DBG_MCK(args) #endif @@ -49,6 +48,11 @@ static volatile unsigned int PYXIS_mcheck_expected = 0; static volatile unsigned int PYXIS_mcheck_taken = 0; static unsigned int PYXIS_jd; +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int PYXIS_DMA_WIN_BASE = PYXIS_DMA_WIN_BASE_DEFAULT; +unsigned int PYXIS_DMA_WIN_SIZE = PYXIS_DMA_WIN_SIZE_DEFAULT; +unsigned long pyxis_sm_base_r1, pyxis_sm_base_r2, pyxis_sm_base_r3; +#endif /* SRM_SETUP */ /* * Given a bus, device, and function number, compute resulting @@ -129,24 +133,23 @@ static int mk_conf_addr(unsigned char bus, unsigned char device_fn, static unsigned int conf_read(unsigned long addr, unsigned char type1) { unsigned long flags; - unsigned int stat0, value; + unsigned int stat0, value, temp; unsigned int pyxis_cfg = 0; /* to keep gcc quiet */ - save_flags(flags); /* avoid getting hit by machine check */ - cli(); + save_and_cli(flags); /* avoid getting hit by machine check */ DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); /* reset status register to avoid losing errors: */ stat0 = *(vuip)PYXIS_ERR; - *(vuip)PYXIS_ERR = stat0; - mb(); + *(vuip)PYXIS_ERR = stat0; mb(); + temp = *(vuip)PYXIS_ERR; /* re-read to force write */ DBG(("conf_read: PYXIS ERR was 0x%x\n", stat0)); /* if Type1 access, must set PYXIS CFG */ if (type1) { pyxis_cfg = *(vuip)PYXIS_CFG; - *(vuip)PYXIS_CFG = pyxis_cfg | 1; - mb(); + *(vuip)PYXIS_CFG = pyxis_cfg | 1; mb(); + temp = *(vuip)PYXIS_CFG; /* re-read to force write */ DBG(("conf_read: TYPE1 access\n")); } @@ -166,36 +169,11 @@ static unsigned int conf_read(unsigned long addr, unsigned char type1) } PYXIS_mcheck_expected = 0; mb(); - /* - * david.rusling@reo.mts.dec.com. This code is needed for the - * EB64+ as it does not generate a machine check (why I don't - * know). When we build kernels for one particular platform - * then we can make this conditional on the type. - */ -#if 0 - draina(); - - /* now look for any errors */ - stat0 = *(vuip)PYXIS_IOC_PYXIS_ERR; - DBG(("conf_read: PYXIS ERR after read 0x%x\n", stat0)); - if (stat0 & 0x8280U) { /* is any error bit set? */ - /* if not NDEV, print status */ - if (!(stat0 & 0x0080)) { - printk("PYXIS.c:conf_read: got stat0=%x\n", stat0); - } - - /* reset error status: */ - *(vulp)PYXIS_IOC_PYXIS_ERR = stat0; - mb(); - wrmces(0x7); /* reset machine check */ - value = 0xffffffff; - } -#endif /* if Type1 access, must reset IOC CFG so normal IO space ops work */ if (type1) { - *(vuip)PYXIS_CFG = pyxis_cfg & ~1; - mb(); + *(vuip)PYXIS_CFG = pyxis_cfg & ~1; mb(); + temp = *(vuip)PYXIS_CFG; /* re-read to force write */ } DBG(("conf_read(): finished\n")); @@ -209,22 +187,21 @@ static void conf_write(unsigned long addr, unsigned int value, unsigned char type1) { unsigned long flags; - unsigned int stat0; + unsigned int stat0, temp; unsigned int pyxis_cfg = 0; /* to keep gcc quiet */ - save_flags(flags); /* avoid getting hit by machine check */ - cli(); + save_and_cli(flags); /* avoid getting hit by machine check */ /* reset status register to avoid losing errors: */ stat0 = *(vuip)PYXIS_ERR; - *(vuip)PYXIS_ERR = stat0; - mb(); + *(vuip)PYXIS_ERR = stat0; mb(); + temp = *(vuip)PYXIS_ERR; /* re-read to force write */ DBG(("conf_write: PYXIS ERR was 0x%x\n", stat0)); /* if Type1 access, must set PYXIS CFG */ if (type1) { pyxis_cfg = *(vuip)PYXIS_CFG; - *(vuip)PYXIS_CFG = pyxis_cfg | 1; - mb(); + *(vuip)PYXIS_CFG = pyxis_cfg | 1; mb(); + temp = *(vuip)PYXIS_CFG; /* re-read to force write */ DBG(("conf_read: TYPE1 access\n")); } @@ -235,13 +212,14 @@ static void conf_write(unsigned long addr, unsigned int value, *(vuip)addr = value; mb(); mb(); /* magic */ + temp = *(vuip)PYXIS_ERR; /* do a PYXIS read to force the write */ PYXIS_mcheck_expected = 0; mb(); /* if Type1 access, must reset IOC CFG so normal IO space ops work */ if (type1) { - *(vuip)PYXIS_CFG = pyxis_cfg & ~1; - mb(); + *(vuip)PYXIS_CFG = pyxis_cfg & ~1; mb(); + temp = *(vuip)PYXIS_CFG; /* re-read to force write */ } DBG(("conf_write(): finished\n")); @@ -367,19 +345,105 @@ unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end) { unsigned int pyxis_err ; +#if 0 +printk("pyxis_init: PYXIS_ERR_MASK 0x%x\n", *(vuip)PYXIS_ERR_MASK); +printk("pyxis_init: PYXIS_ERR 0x%x\n", *(vuip)PYXIS_ERR); + +printk("pyxis_init: PYXIS_INT_REQ 0x%lx\n", *(vulp)PYXIS_INT_REQ); +printk("pyxis_init: PYXIS_INT_MASK 0x%lx\n", *(vulp)PYXIS_INT_MASK); +printk("pyxis_init: PYXIS_INT_ROUTE 0x%lx\n", *(vulp)PYXIS_INT_ROUTE); +printk("pyxis_init: PYXIS_INT_HILO 0x%lx\n", *(vulp)PYXIS_INT_HILO); +printk("pyxis_init: PYXIS_INT_CNFG 0x%x\n", *(vuip)PYXIS_INT_CNFG); +printk("pyxis_init: PYXIS_RT_COUNT 0x%lx\n", *(vulp)PYXIS_RT_COUNT); +#endif + /* - * Set up error reporting. + * Set up error reporting. Make sure CPU_PE is OFF in the mask. */ + pyxis_err = *(vuip)PYXIS_ERR_MASK; + pyxis_err &= ~4; + *(vuip)PYXIS_ERR_MASK = pyxis_err; mb(); + pyxis_err = *(vuip)PYXIS_ERR_MASK; /* re-read to force write */ + pyxis_err = *(vuip)PYXIS_ERR ; pyxis_err |= 0x180; /* master/target abort */ - *(vuip)PYXIS_ERR = pyxis_err ; - mb() ; - pyxis_err = *(vuip)PYXIS_ERR ; - -#ifdef CONFIG_ALPHA_RUFFIAN - printk("pyxis_init: Skipping window register rewrites --" + *(vuip)PYXIS_ERR = pyxis_err; mb(); + pyxis_err = *(vuip)PYXIS_ERR; /* re-read to force write */ + +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 0 for enabled and mapped to 0 */ + if (((*(vuip)PYXIS_W0_BASE & 3) == 1) && + (*(vuip)PYXIS_T0_BASE == 0) && + ((*(vuip)PYXIS_W0_MASK & 0xfff00000U) > 0x0ff00000U)) + { + PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W0_BASE & 0xfff00000U; + PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W0_MASK & 0xfff00000U; + PYXIS_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("pyxis_init: using Window 0 settings\n"); + printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W0_BASE, + *(vuip)PYXIS_W0_MASK, + *(vuip)PYXIS_T0_BASE); +#endif + } + else /* check window 1 for enabled and mapped to 0 */ + if (((*(vuip)PYXIS_W1_BASE & 3) == 1) && + (*(vuip)PYXIS_T1_BASE == 0) && + ((*(vuip)PYXIS_W1_MASK & 0xfff00000U) > 0x0ff00000U)) +{ + PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W1_BASE & 0xfff00000U; + PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W1_MASK & 0xfff00000U; + PYXIS_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("pyxis_init: using Window 1 settings\n"); + printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W1_BASE, + *(vuip)PYXIS_W1_MASK, + *(vuip)PYXIS_T1_BASE); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if (((*(vuip)PYXIS_W2_BASE & 3) == 1) && + (*(vuip)PYXIS_T2_BASE == 0) && + ((*(vuip)PYXIS_W2_MASK & 0xfff00000U) > 0x0ff00000U)) + { + PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W2_BASE & 0xfff00000U; + PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W2_MASK & 0xfff00000U; + PYXIS_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("pyxis_init: using Window 2 settings\n"); + printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W2_BASE, + *(vuip)PYXIS_W2_MASK, + *(vuip)PYXIS_T2_BASE); +#endif + } + else /* check window 3 for enabled and mapped to 0 */ + if (((*(vuip)PYXIS_W3_BASE & 3) == 1) && + (*(vuip)PYXIS_T3_BASE == 0) && + ((*(vuip)PYXIS_W3_MASK & 0xfff00000U) > 0x0ff00000U)) + { + PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W3_BASE & 0xfff00000U; + PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W3_MASK & 0xfff00000U; + PYXIS_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("pyxis_init: using Window 3 settings\n"); + printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W3_BASE, + *(vuip)PYXIS_W3_MASK, + *(vuip)PYXIS_T3_BASE); +#endif + } + else /* we must use our defaults which were pre-initialized... */ +#endif /* SRM_SETUP */ + { +#if defined(CONFIG_ALPHA_RUFFIAN) +#if 1 + printk("pyxis_init: skipping window register rewrites... " " trust DeskStation firmware!\n"); -#else +#endif +#else /* RUFFIAN */ /* * Set up the PCI->physical memory translation windows. * For now, windows 1,2 and 3 are disabled. In the future, we may @@ -395,7 +459,8 @@ unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end) *(vuip)PYXIS_W2_BASE = 0x0 ; *(vuip)PYXIS_W3_BASE = 0x0 ; mb(); -#endif +#endif /* RUFFIAN */ + } /* * check ASN in HWRPB for validity, report if bad @@ -407,18 +472,21 @@ unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end) } /* - * Finally, clear the PYXIS_CFG register, which gets used + * Next, clear the PYXIS_CFG register, which gets used * for PCI Config Space accesses. That is the way * we want to use it, and we do not want to depend on * what ARC or SRM might have left behind... */ { - unsigned int pyxis_cfg; + unsigned int pyxis_cfg, temp; pyxis_cfg = *(vuip)PYXIS_CFG; mb(); -#if 0 + if (pyxis_cfg != 0) { +#if 1 printk("PYXIS_init: CFG was 0x%x\n", pyxis_cfg); #endif *(vuip)PYXIS_CFG = 0; mb(); + temp = *(vuip)PYXIS_CFG; /* re-read to force write */ + } } { @@ -428,10 +496,48 @@ unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end) printk("PYXIS_init: HAE_MEM was 0x%x\n", pyxis_hae_mem); printk("PYXIS_init: HAE_IO was 0x%x\n", pyxis_hae_io); #endif - *(vuip)PYXIS_HAE_MEM = 0; mb(); - pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM; +#ifdef CONFIG_ALPHA_SRM_SETUP + /* + * sigh... For the SRM setup, unless we know apriori what the HAE + * contents will be, we need to setup the arbitrary region bases + * so we can test against the range of addresses and tailor the + * region chosen for the SPARSE memory access. + * + * see include/asm-alpha/pyxis.h for the SPARSE mem read/write + */ + pyxis_sm_base_r1 = (pyxis_hae_mem ) & 0xe0000000UL;/* region 1 */ + pyxis_sm_base_r2 = (pyxis_hae_mem << 16) & 0xf8000000UL;/* region 2 */ + pyxis_sm_base_r3 = (pyxis_hae_mem << 24) & 0xfc000000UL;/* region 3 */ + + /* + Set the HAE cache, so that setup_arch() code + will use the SRM setting always. Our readb/writeb + code in pyxis.h expects never to have to change + the contents of the HAE. + */ + hae.cache = pyxis_hae_mem; +#else /* SRM_SETUP */ + *(vuip)PYXIS_HAE_MEM = 0U; mb(); + pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM; /* re-read to force write */ *(vuip)PYXIS_HAE_IO = 0; mb(); - pyxis_hae_io = *(vuip)PYXIS_HAE_IO; + pyxis_hae_io = *(vuip)PYXIS_HAE_IO; /* re-read to force write */ +#endif /* SRM_SETUP */ + } + + /* + * Finally, check that the PYXIS_CTRL1 has IOA_BEN set for + * enabling byte/word PCI bus space(s) access. + */ + { + unsigned int ctrl1; + ctrl1 = *(vuip) PYXIS_CTRL1; + if (!(ctrl1 & 1)) { +#if 1 + printk("PYXIS_init: enabling byte/word PCI space\n"); +#endif + *(vuip) PYXIS_CTRL1 = ctrl1 | 1; mb(); + ctrl1 = *(vuip)PYXIS_CTRL1; /* re-read to force write */ + } } return mem_start; @@ -441,9 +547,8 @@ int pyxis_pci_clr_err(void) { PYXIS_jd = *(vuip)PYXIS_ERR; DBG(("PYXIS_pci_clr_err: PYXIS ERR after read 0x%x\n", PYXIS_jd)); - *(vuip)PYXIS_ERR = 0x0180; - mb(); - PYXIS_jd = *(vuip)PYXIS_ERR; + *(vuip)PYXIS_ERR = 0x0180; mb(); + PYXIS_jd = *(vuip)PYXIS_ERR; /* re-read to force write */ return 0; } @@ -486,7 +591,7 @@ void pyxis_machine_check(unsigned long vector, unsigned long la_ptr, */ mb(); mb(); /* magic */ - if (PYXIS_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) { + if (PYXIS_mcheck_expected) { DBG(("PYXIS machine check expected\n")); PYXIS_mcheck_expected = 0; PYXIS_mcheck_taken = 1; @@ -502,7 +607,8 @@ void pyxis_machine_check(unsigned long vector, unsigned long la_ptr, printk("PYXIS machine check NOT expected\n") ; DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n", vector, la_ptr)); - DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x" + " sysoffset 0x%x\n", regs->pc, mchk_header->size, mchk_header->proc_offset, mchk_header->sys_offset)); PYXIS_mcheck_expected = 0; diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 9ce9c1f17..bcab8667f 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -22,6 +22,7 @@ #include <linux/delay.h> #include <linux/config.h> /* CONFIG_ALPHA_LCA etc */ #include <linux/ioport.h> +#include <linux/mc146818rtc.h> #ifdef CONFIG_RTC #include <linux/timex.h> @@ -34,11 +35,24 @@ #include <asm/dma.h> #include <asm/io.h> +extern void setup_smp(void); +extern char *smp_info(void); + +#if 1 +# define DBG_SRM(args) printk args +#else +# define DBG_SRM(args) +#endif + struct hae hae = { 0, (unsigned long*) HAE_ADDRESS }; +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned long srm_hae; +#endif + struct hwrpb_struct *hwrpb; unsigned char aux_device_present = 0xaa; @@ -106,12 +120,13 @@ static void init_pit (void) outb(LATCH & 0xff, 0x40); /* LSB */ outb(LATCH >> 8, 0x40); /* MSB */ request_region(0x40, 0x20, "timer"); /* reserve pit */ -#else -#ifndef CONFIG_ALPHA_RUFFIAN +#else /* RTC */ +#if !defined(CONFIG_ALPHA_RUFFIAN) + /* Ruffian depends on the system timer established in MILO!! */ outb(0x36, 0x43); /* counter 0: system timer */ outb(0x00, 0x40); outb(0x00, 0x40); -#endif +#endif /* RUFFIAN */ request_region(0x70, 0x10, "timer"); /* reserve rtc */ #endif /* RTC */ @@ -148,9 +163,21 @@ void setup_arch(char **cmdline_p, init_pit(); + if ((CMOS_READ(RTC_FREQ_SELECT) & 0x3f) != 0x26) { + printk("setup_arch: setting RTC_FREQ to 1024/sec\n"); + CMOS_WRITE(0x26, RTC_FREQ_SELECT); + } + hwrpb = (struct hwrpb_struct*)(IDENT_ADDR + INIT_HWRPB->phys_addr); +#if !defined(CONFIG_ALPHA_TSUNAMI) +#ifdef CONFIG_ALPHA_SRM_SETUP + srm_hae = *hae.reg; /* save SRM setting for restoration */ + DBG_SRM(("setup_arch: old HAE base: 0x%016lx\n", srm_hae)); +#endif /* SRM_SETUP */ set_hae(hae.cache); /* sync HAE register w/hae_cache */ +#endif /* !TSUNAMI */ + wrmces(0x7); /* reset enable correctable error reports */ ROOT_DEV = to_kdev_t(0x0802); /* sda2 */ @@ -185,12 +212,54 @@ void setup_arch(char **cmdline_p, *memory_start_p = pyxis_init(*memory_start_p, *memory_end_p); #elif defined(CONFIG_ALPHA_T2) *memory_start_p = t2_init(*memory_start_p, *memory_end_p); +#elif defined(CONFIG_ALPHA_TSUNAMI) + *memory_start_p = tsunami_init(*memory_start_p, *memory_end_p); +#elif defined(CONFIG_ALPHA_MCPCIA) + *memory_start_p = mcpcia_init(*memory_start_p, *memory_end_p); +#endif + +#ifdef __SMP__ + setup_smp(); #endif } #define N(a) (sizeof(a)/sizeof(a[0])) +/* A change was made to the HWRPB via an ECO and the following code tracks + * a part of the ECO. The HWRPB version must be 5 or higher or the ECO + * was not implemented in the console firmware. If its at rev 5 or greater + * we can get the platform ascii string name from the HWRPB. Thats what this + * function does. It checks the rev level and if the string is in the HWRPB + * it returns the addtess of the string ... a pointer to the platform name. + * + * Returns: + * - Pointer to a ascii string if its in the HWRPB + * - Pointer to a blank string if the data is not in the HWRPB. + */ +static char * +platform_string(void) +{ + struct dsr_struct *dsr; + static char unk_system_string[] = "N/A"; + + /* Go to the console for the string pointer. + * If the rpb_vers is not 5 or greater the rpb + * is old and does not have this data in it. + */ + if (hwrpb->revision < 5) + return (unk_system_string); + else { + /* The Dynamic System Recognition struct + * has the system platform name starting + * after the character count of the string. + */ + dsr = ((struct dsr_struct *) + ((char *)hwrpb + hwrpb->dsr_offset)); + return ((char *)dsr + (dsr->sysname_off + + sizeof(long))); + } +} static void get_sysnames(long type, long variation, @@ -222,6 +291,10 @@ get_sysnames(long type, long variation, static char * eb66_names[] = {"EB66", "EB66+"}; static int eb66_indices[] = {0,0,1}; + static char * rawhide_names[] = {"Dodge", "Wrangler", "Durango", + "Tincup", "DaVinci"}; + static int rawhide_indices[] = {0,0,0,1,1,2,2,3,3,4,4}; + long member; /* Restore real CABRIO and EB66+ family names, ie EB64+ and EB66 */ @@ -249,7 +322,9 @@ get_sysnames(long type, long variation, member = (variation >> 10) & 0x3f; /* member ID is a bit-field */ - switch (type) { + switch (type) { /* select by family */ + default: /* default to variation "0" for now */ + break; case ST_DEC_EB164: if (member < N(eb164_indices)) *variation_name = eb164_names[eb164_indices[member]]; @@ -266,7 +341,11 @@ get_sysnames(long type, long variation, if (member < N(eb66_indices)) *variation_name = eb66_names[eb66_indices[member]]; break; - } + case ST_DEC_RAWHIDE: + if (member < N(rawhide_indices)) + *variation_name = rawhide_names[rawhide_indices[member]]; + break; + } /* end family switch */ } /* @@ -315,7 +394,12 @@ int get_cpuinfo(char *buffer) "max. addr. space #\t: %ld\n" "BogoMIPS\t\t: %lu.%02lu\n" "kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n" - "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n", + "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n" + "platform string\t\t: %s\n" +#ifdef __SMP__ + "%s" +#endif + , cpu_name, cpu->variation, cpu->revision, (char*)cpu->serial_no, @@ -329,5 +413,10 @@ int get_cpuinfo(char *buffer) hwrpb->max_asn, loops_per_sec / 500000, (loops_per_sec / 5000) % 100, unaligned[0].count, unaligned[0].pc, unaligned[0].va, - unaligned[1].count, unaligned[1].pc, unaligned[1].va); + unaligned[1].count, unaligned[1].pc, unaligned[1].va, + platform_string() +#ifdef __SMP__ + , smp_info() +#endif + ); } diff --git a/arch/alpha/kernel/smc37c669.c b/arch/alpha/kernel/smc37c669.c index 6724372fe..bcc4ed212 100644 --- a/arch/alpha/kernel/smc37c669.c +++ b/arch/alpha/kernel/smc37c669.c @@ -3,8 +3,6 @@ */ #include <linux/kernel.h> -#include <linux/bios32.h> -#include <linux/pci.h> #include <linux/malloc.h> #include <linux/mm.h> #include <linux/init.h> @@ -996,7 +994,7 @@ static SMC37c669_CONFIG_REGS *SMC37c669 __initdata = NULL; ** and standard ISA IRQs. ** */ -static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_table __initdata; +static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_table __initdata = 0; /* ** The following definition is for the default IRQ @@ -1022,7 +1020,7 @@ __initdata = ** ISA DMA channels. ** */ -static SMC37c669_DRQ_TRANSLATION_ENTRY *SMC37c669_drq_table __initdata; +static SMC37c669_DRQ_TRANSLATION_ENTRY *SMC37c669_drq_table __initdata = 0; /* ** The following definition is the default DRQ diff --git a/arch/alpha/kernel/smc37c93x.c b/arch/alpha/kernel/smc37c93x.c index a75998d7e..b0284b032 100644 --- a/arch/alpha/kernel/smc37c93x.c +++ b/arch/alpha/kernel/smc37c93x.c @@ -5,8 +5,6 @@ #include <linux/config.h> #include <linux/kernel.h> -#include <linux/bios32.h> -#include <linux/pci.h> #include <linux/malloc.h> #include <linux/mm.h> #include <linux/init.h> diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c new file mode 100644 index 000000000..fed91a1c1 --- /dev/null +++ b/arch/alpha/kernel/smp.c @@ -0,0 +1,1097 @@ +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/kernel_stat.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/tasks.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/init.h> + +#include <asm/hwrpb.h> +#include <asm/ptrace.h> +#include <asm/atomic.h> + +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/pgtable.h> +#include <asm/spinlock.h> +#include <asm/hardirq.h> +#include <asm/softirq.h> + +#define __KERNEL_SYSCALLS__ +#include <asm/unistd.h> + +struct ipi_msg_flush_tb_struct ipi_msg_flush_tb; + +struct cpuinfo_alpha cpu_data[NR_CPUS]; + +/* Processor holding kernel spinlock */ +klock_info_t klock_info = { KLOCK_CLEAR, 0 }; + +spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; + +unsigned int boot_cpu_id = 0; +static int smp_activated = 0; + +int smp_found_config = 0; /* Have we found an SMP box */ +static int max_cpus = -1; + +unsigned int cpu_present_map = 0; + +int smp_num_cpus = 1; +int smp_num_probed = 0; /* Internal processor count */ + +int smp_threads_ready = 0; +volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; +volatile unsigned long smp_spinning[NR_CPUS] = { 0, }; + +unsigned int prof_multiplier[NR_CPUS]; +unsigned int prof_counter[NR_CPUS]; + +volatile int ipi_bits[NR_CPUS]; + +unsigned long boot_cpu_palrev; + +volatile int smp_commenced = 0; +volatile int smp_processors_ready = 0; + +volatile int cpu_number_map[NR_CPUS]; +volatile int cpu_logical_map[NR_CPUS]; + +extern int cpu_idle(void *unused); +extern void calibrate_delay(void); +extern struct hwrpb_struct *hwrpb; +extern struct thread_struct * original_pcb_ptr; +extern void __start_cpu(unsigned long); + +static void smp_setup_percpu_timer(void); +static void secondary_cpu_start(int, struct task_struct *); +static void send_cpu_msg(char *, int); + +/* process bootcommand SMP options, like "nosmp" and "maxcpus=" */ +__initfunc(void smp_setup(char *str, int *ints)) +{ + if (ints && ints[0] > 0) + max_cpus = ints[1]; + else + max_cpus = 0; +} + +void smp_store_cpu_info(int id) +{ + /* This is it on Alpha, so far. */ + cpu_data[id].loops_per_sec = loops_per_sec; +} + +void smp_commence(void) +{ + /* Lets the callin's below out of their loop. */ + mb(); + smp_commenced = 1; +} + +void smp_callin(void) +{ + int cpuid = hard_smp_processor_id(); + +#if 0 + printk("CALLIN %d state 0x%lx\n", cpuid, current->state); +#endif +#ifdef HUH + local_flush_cache_all(); + local_flush_tlb_all(); +#endif +#if 0 + set_irq_udt(mid_xlate[boot_cpu_id]); +#endif + + /* Get our local ticker going. */ + smp_setup_percpu_timer(); + +#if 0 + calibrate_delay(); +#endif + smp_store_cpu_info(cpuid); +#ifdef HUH + local_flush_cache_all(); + local_flush_tlb_all(); +#endif + + /* Allow master to continue. */ + set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]); +#ifdef HUH + local_flush_cache_all(); + local_flush_tlb_all(); +#endif + +#ifdef NOT_YET + while(!task[cpuid] || current_set[cpuid] != task[cpuid]) + barrier(); +#endif /* NOT_YET */ + +#if 0 + /* Fix idle thread fields. */ + __asm__ __volatile__("ld [%0], %%g6\n\t" + : : "r" (¤t_set[cpuid]) + : "memory" /* paranoid */); + current->mm->mmap->vm_page_prot = PAGE_SHARED; + current->mm->mmap->vm_start = PAGE_OFFSET; + current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; +#endif + +#ifdef HUH + local_flush_cache_all(); + local_flush_tlb_all(); +#endif +#if 0 + __sti(); +#endif +} + +asmlinkage int start_secondary(void *unused) +{ + extern asmlinkage void entInt(void); + extern void paging_init_secondary(void); + + wrmces(7); + paging_init_secondary(); + trap_init(); + wrent(entInt, 0); + + smp_callin(); + while (!smp_commenced) + barrier(); +#if 1 +printk("start_secondary: commencing CPU %d current %p\n", + hard_smp_processor_id(), current); +#endif + return cpu_idle(NULL); +} + +/* + * Cycle through the processors sending START msgs to boot each. + */ +void smp_boot_cpus(void) +{ + int cpucount = 0; + int i, first, prev; + + printk("smp_boot_cpus: Entering SMP Mode...\n"); + +#if 0 + __sti(); +#endif + + for(i=0; i < NR_CPUS; i++) { + cpu_number_map[i] = -1; + cpu_logical_map[i] = -1; + prof_counter[i] = 1; + prof_multiplier[i] = 1; + ipi_bits[i] = 0; + } + + cpu_number_map[boot_cpu_id] = 0; + cpu_logical_map[0] = boot_cpu_id; + current->processor = boot_cpu_id; /* ??? */ + klock_info.akp = boot_cpu_id; + + smp_store_cpu_info(boot_cpu_id); +#ifdef NOT_YET + printk("CPU%d: ", boot_cpu_id); + print_cpu_info(&cpu_data[boot_cpu_id]); + set_irq_udt(mid_xlate[boot_cpu_id]); +#endif /* NOT_YET */ + smp_setup_percpu_timer(); +#ifdef HUH + local_flush_cache_all(); +#endif + if (smp_num_probed == 1) + return; /* Not an MP box. */ + +#if NOT_YET + /* + * If SMP should be disabled, then really disable it! + */ + if (!max_cpus) + { + smp_found_config = 0; + printk(KERN_INFO "SMP mode deactivated.\n"); + } +#endif /* NOT_YET */ + + for (i = 0; i < NR_CPUS; i++) { + + if (i == boot_cpu_id) + continue; + + if (cpu_present_map & (1 << i)) { + struct task_struct *idle; + int timeout; + + /* Cook up an idler for this guy. */ + kernel_thread(start_secondary, NULL, CLONE_PID); + idle = task[++cpucount]; + if (!idle) + panic("No idle process for CPU %d", i); + idle->processor = i; + +#if 0 +printk("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n", + i, idle->state, idle->flags); +#endif + + /* whirrr, whirrr, whirrrrrrrrr... */ +#ifdef HUH + local_flush_cache_all(); +#endif + secondary_cpu_start(i, idle); + + /* wheee... it's going... wait for 5 secs...*/ + for (timeout = 0; timeout < 50000; timeout++) { + if (cpu_callin_map[i]) + break; + udelay(100); + } + if (cpu_callin_map[i]) { + /* Another "Red Snapper". */ + cpu_number_map[i] = cpucount; + cpu_logical_map[cpucount] = i; + } else { + cpucount--; + printk("smp_boot_cpus: Processor %d" + " is stuck 0x%lx.\n", i, idle->flags); + } + } + if (!(cpu_callin_map[i])) { + cpu_present_map &= ~(1 << i); + cpu_number_map[i] = -1; + } + } +#ifdef HUH + local_flush_cache_all(); +#endif + if (cpucount == 0) { + printk("smp_boot_cpus: ERROR - only one Processor found.\n"); + cpu_present_map = (1 << smp_processor_id()); + } else { + unsigned long bogosum = 0; + for (i = 0; i < NR_CPUS; i++) { + if (cpu_present_map & (1 << i)) + bogosum += cpu_data[i].loops_per_sec; + } + printk("smp_boot_cpus: Total of %d Processors activated" + " (%lu.%02lu BogoMIPS).\n", + cpucount + 1, + (bogosum + 2500)/500000, + ((bogosum + 2500)/5000)%100); + smp_activated = 1; + smp_num_cpus = cpucount + 1; + } + + /* Setup CPU list for IRQ distribution scheme. */ + first = prev = -1; + for (i = 0; i < NR_CPUS; i++) { + if (cpu_present_map & (1 << i)) { + if (first == -1) + first = i; + if (prev != -1) + cpu_data[i].next = i; + prev = i; + } + } + cpu_data[prev].next = first; + + /* Ok, they are spinning and ready to go. */ + smp_processors_ready = 1; +} + +__initfunc(void ioapic_pirq_setup(char *str, int *ints)) +{ + /* this is prolly INTEL-specific */ +} + +static void smp_setup_percpu_timer(void) +{ + int cpu = smp_processor_id(); + + prof_counter[cpu] = prof_multiplier[cpu] = 1; +#ifdef NOT_YET + load_profile_irq(mid_xlate[cpu], lvl14_resolution); + if (cpu == boot_cpu_id) + enable_pil_irq(14); +#endif +} + +extern void update_one_process(struct task_struct *p, unsigned long ticks, + unsigned long user, unsigned long system, + int cpu); + +void smp_percpu_timer_interrupt(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + +#ifdef NOT_YET + clear_profile_irq(mid_xlate[cpu]); +#ifdef CONFIG_PROFILE + if(!user_mode(regs)) + sparc_do_profile(regs->pc); +#endif +#endif + + if (!--prof_counter[cpu]) { + int user = user_mode(regs); + if (current->pid) { + update_one_process(current, 1, user, !user, cpu); + + if (--current->counter < 0) { + current->counter = 0; + need_resched = 1; + } + + spin_lock(&ticker_lock); + if (user) { + if (current->priority < DEF_PRIORITY) { + kstat.cpu_nice++; + kstat.per_cpu_nice[cpu]++; + } else { + kstat.cpu_user++; + kstat.per_cpu_user[cpu]++; + } + } else { + kstat.cpu_system++; + kstat.per_cpu_system[cpu]++; + } + spin_unlock(&ticker_lock); + } + prof_counter[cpu] = prof_multiplier[cpu]; + } +} + +int setup_profiling_timer(unsigned int multiplier) +{ +#ifdef NOT_YET + int i; + unsigned long flags; + + /* Prevent level14 ticker IRQ flooding. */ + if((!multiplier) || (lvl14_resolution / multiplier) < 500) + return -EINVAL; + + save_and_cli(flags); + for(i = 0; i < NR_CPUS; i++) { + if(cpu_present_map & (1 << i)) { + load_profile_irq(mid_xlate[i], lvl14_resolution / multip +lier); + prof_multiplier[i] = multiplier; + } + } + restore_flags(flags); + + return 0; + +#endif + return -EINVAL; +} + +/* Only broken Intel needs this, thus it should not even be referenced + * globally... + */ +__initfunc(void initialize_secondary(void)) +{ + printk("initialize_secondary: entry\n"); +} + +static void +secondary_cpu_start(int cpuid, struct task_struct *idle) +{ + struct percpu_struct *cpu; + int timeout; + + cpu = (struct percpu_struct *) + ((char*)hwrpb + + hwrpb->processor_offset + + cpuid * hwrpb->processor_size); + + /* set context to idle thread this CPU will use when running */ + /* assumption is that the idle thread is all set to go... ??? */ + memcpy(&cpu->hwpcb[0], &idle->tss, sizeof(struct pcb_struct)); + cpu->hwpcb[4] = cpu->hwpcb[0]; /* UNIQUE set to KSP ??? */ +#if 0 +printk("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n", + cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb); +printk("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n", + cpuid, idle->state, idle->tss.pal_flags); +#endif + + /* setup HWRPB fields that SRM uses to activate secondary CPU */ + hwrpb->CPU_restart = __start_cpu; + hwrpb->CPU_restart_data = (unsigned long) idle; + + /* recalculate and update the HWRPB checksum */ + { + unsigned long sum, *lp1, *lp2; + sum = 0; + lp1 = (unsigned long *)hwrpb; + lp2 = &hwrpb->chksum; + while (lp1 < lp2) + sum += *lp1++; + *lp2 = sum; + } + + /* + * Send a "start" command to the specified processor. + */ + + /* SRM III 3.4.1.3 */ + cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */ + cpu->flags &= ~1;/* turn off Bootstrap In Progress */ + mb(); + + send_cpu_msg("START\r\n", cpuid); + + /* now, we wait... */ + for (timeout = 10000; !(cpu->flags & 1); timeout--) { + if (timeout <= 0) { + printk("Processor %d failed to start\n", cpuid); + /* needed for pset_info to work */ +#if 0 + ipc_processor_enable(cpu_to_processor(cpunum)); +#endif + return; + } + udelay(1000); + } +#if 0 + printk("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid); +#endif +} + +static void +send_cpu_msg(char *str, int cpuid) +{ + struct percpu_struct *cpu; + register char *cp1, *cp2; + unsigned long cpumask; + int timeout; + + + cpu = (struct percpu_struct *) + ((char*)hwrpb + + hwrpb->processor_offset + + cpuid * hwrpb->processor_size); + + cpumask = (1L << cpuid); + for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) { + if (timeout <= 0) { + printk("Processor %x not ready\n", cpuid); + return; + } + udelay(1000); + } + + cp1 = (char *) &cpu->ipc_buffer[1]; + cp2 = str; + while (*cp2) *cp1++ = *cp2++; + *(unsigned int *)&cpu->ipc_buffer[0] = cp2 - str; /* hack */ + + /* atomic test and set */ + set_bit(cpuid, &hwrpb->rxrdy); + + for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) { + if (timeout <= 0) { + printk("Processor %x not ready\n", cpuid); + return; + } + udelay(1000); + } +} + +/* + * setup_smp() + * + * called from arch/alpha/kernel/setup.c:setup_arch() when __SMP__ defined + */ +__initfunc(void setup_smp(void)) +{ + struct percpu_struct *cpubase, *cpu; + int i; + + boot_cpu_id = hard_smp_processor_id(); + if (boot_cpu_id != 0) { + printk("setup_smp: boot_cpu_id != 0 (%d).\n", boot_cpu_id); + } + + if (hwrpb->nr_processors > 1) { +#if 0 +printk("setup_smp: nr_processors 0x%lx\n", + hwrpb->nr_processors); +#endif + cpubase = (struct percpu_struct *) + ((char*)hwrpb + hwrpb->processor_offset); + boot_cpu_palrev = cpubase->pal_revision; + + for (i = 0; i < hwrpb->nr_processors; i++ ) { + cpu = (struct percpu_struct *) + ((char *)cpubase + i*hwrpb->processor_size); + if ((cpu->flags & 0x1cc) == 0x1cc) { + smp_num_probed++; + /* assume here that "whami" == index */ + cpu_present_map |= (1 << i); + if (i != boot_cpu_id) + cpu->pal_revision = boot_cpu_palrev; + } +#if 0 +printk("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n", + i, cpu->flags, cpu->type); + printk("setup_smp: CPU %d: PAL rev 0x%lx\n", + i, cpu->pal_revision); +#endif + } + } else { + smp_num_probed = 1; + cpu_present_map = (1 << boot_cpu_id); + } + printk("setup_smp: %d CPUs probed, cpu_present_map 0x%x," + " boot_cpu_id %d\n", + smp_num_probed, cpu_present_map, boot_cpu_id); +} + +static void +secondary_console_message(void) +{ + int mycpu, i, cnt; + unsigned long txrdy = hwrpb->txrdy; + char *cp1, *cp2, buf[80]; + struct percpu_struct *cpu; + + mycpu = hard_smp_processor_id(); + +#if 0 +printk("secondary_console_message: TXRDY 0x%lx.\n", txrdy); +#endif + for (i = 0; i < NR_CPUS; i++) { + if (txrdy & (1L << i)) { +#if 0 +printk("secondary_console_message: TXRDY contains CPU %d.\n", i); +#endif + cpu = (struct percpu_struct *) + ((char*)hwrpb + + hwrpb->processor_offset + + i * hwrpb->processor_size); +#if 1 + printk("secondary_console_message: on %d from %d" + " HALT_REASON 0x%lx FLAGS 0x%lx\n", + mycpu, i, cpu->halt_reason, cpu->flags); +#endif + cnt = cpu->ipc_buffer[0] >> 32; + if (cnt <= 0 || cnt >= 80) + strcpy(buf,"<<< BOGUS MSG >>>"); + else { + cp1 = (char *) &cpu->ipc_buffer[11]; + cp2 = buf; + while (cnt--) { + if (*cp1 == '\r' || *cp1 == '\n') { + *cp2++ = ' '; cp1++; + } else + *cp2++ = *cp1++; + } + *cp2 = 0; + } +#if 1 + printk("secondary_console_message: on %d message is '%s'\n", + mycpu, buf); +#endif + } + } + hwrpb->txrdy = 0; + return; +} + +static int +halt_on_panic(unsigned int this_cpu) +{ + halt(); + return 0; +} + +static int +local_flush_tlb_all(unsigned int this_cpu) +{ + tbia(); + clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask); + mb(); + return 0; +} + +static int +local_flush_tlb_mm(unsigned int this_cpu) +{ + struct mm_struct * mm = ipi_msg_flush_tb.p.flush_mm; + if (mm != current->mm) + flush_tlb_other(mm); + else + flush_tlb_current(mm); + clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask); + mb(); + return 0; +} + +static int +local_flush_tlb_page(unsigned int this_cpu) +{ + struct vm_area_struct * vma = ipi_msg_flush_tb.p.flush_vma; + struct mm_struct * mm = vma->vm_mm; + + if (mm != current->mm) + flush_tlb_other(mm); + else + flush_tlb_current_page(mm, vma, ipi_msg_flush_tb.flush_addr); + clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask); + mb(); + return 0; +} + +static int +wrapper_local_flush_tlb_page(unsigned int this_cpu) +{ +#if 0 + int cpu = smp_processor_id(); + + if (cpu) { + printk("wrapper: ipi_msg_flush_tb.flush_addr 0x%lx [%d]\n", + ipi_msg_flush_tb.flush_addr, atomic_read(&global_irq_count)); + } +#endif + local_flush_tlb_page(this_cpu); + return 0; +} + +static int +unknown_ipi(unsigned int this_cpu) +{ + printk("unknown_ipi() on cpu %d: ", this_cpu); + return 1; +} + +enum ipi_message_type { + CPU_STOP, + TLB_ALL, + TLB_MM, + TLB_PAGE, + TLB_RANGE +}; + +static int (* ipi_func[32])(unsigned int) = { + halt_on_panic, + local_flush_tlb_all, + local_flush_tlb_mm, + wrapper_local_flush_tlb_page, + local_flush_tlb_mm, /* a.k.a. local_flush_tlb_range */ + unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, + unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, + unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, + unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, + unknown_ipi, unknown_ipi, unknown_ipi +}; + +void +handle_ipi(struct pt_regs *regs) +{ + int this_cpu = smp_processor_id(); + volatile int * pending_ipis = &ipi_bits[this_cpu]; + int ops; + + mb(); +#if 0 + printk("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n", + this_cpu, *pending_ipis, regs->pc); +#endif + while ((ops = *pending_ipis)) { + int first; + for (first = 0; (ops & 1) == 0; ++first, ops >>= 1) + ; /* look for the first thing to do */ + clear_bit(first, pending_ipis); + mb(); + if ((*ipi_func[first])(this_cpu)) + printk("%d\n", first); + mb(); + } + if (hwrpb->txrdy) + secondary_console_message(); +} + +void +send_ipi_message(long to_whom, enum ipi_message_type operation) +{ + int i; + unsigned int j; + + for (i = 0, j = 1; i < NR_CPUS; ++i, j += j) { + if ((to_whom & j) == 0) + continue; + set_bit(operation, &ipi_bits[i]); + mb(); + wripir(i); + } +} + +static char smp_buf[256]; + +char *smp_info(void) +{ + sprintf(smp_buf, "CPUs probed %d active %d map 0x%x AKP %d\n", + smp_num_probed, smp_num_cpus, cpu_present_map, + klock_info.akp); + + return smp_buf; +} + +/* wrapper for call from panic() */ +void +smp_message_pass(int target, int msg, unsigned long data, int wait) +{ + int me = smp_processor_id(); + + if (msg != MSG_STOP_CPU) + goto barf; + + send_ipi_message(CPU_STOP, cpu_present_map ^ (1 << me)); + return; +barf: + printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me); + panic("Bogon SMP message pass."); +} + +void +flush_tlb_all(void) +{ + unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id()); + int timeout = 10000; + +#if 1 + if (!kernel_lock_held()) { + printk("flush_tlb_all: kernel_flag %d (cpu %d akp %d)!\n", + klock_info.kernel_flag, smp_processor_id(), klock_info.akp); + } +#endif + ipi_msg_flush_tb.flush_tb_mask = to_whom; + send_ipi_message(to_whom, TLB_ALL); + tbia(); + + while (ipi_msg_flush_tb.flush_tb_mask) { + if (--timeout < 0) { + printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n", + smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask); + ipi_msg_flush_tb.flush_tb_mask = 0; + break; + } + udelay(100); + ; /* Wait for all clear from other CPUs. */ + } +} + +void +flush_tlb_mm(struct mm_struct *mm) +{ + unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id()); + int timeout = 10000; + +#if 1 + if (!kernel_lock_held()) { + printk("flush_tlb_mm: kernel_flag %d (cpu %d akp %d)!\n", + klock_info.kernel_flag, smp_processor_id(), klock_info.akp); + } +#endif + ipi_msg_flush_tb.p.flush_mm = mm; + ipi_msg_flush_tb.flush_tb_mask = to_whom; + send_ipi_message(to_whom, TLB_MM); + + if (mm != current->mm) + flush_tlb_other(mm); + else + flush_tlb_current(mm); + + while (ipi_msg_flush_tb.flush_tb_mask) { + if (--timeout < 0) { + printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n", + smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask); + ipi_msg_flush_tb.flush_tb_mask = 0; + break; + } + udelay(100); + ; /* Wait for all clear from other CPUs. */ + } +} + +void +flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) +{ + int cpu = smp_processor_id(); + unsigned int to_whom = cpu_present_map ^ (1 << cpu); + struct mm_struct * mm = vma->vm_mm; + int timeout = 10000; + +#if 1 + if (!kernel_lock_held()) { + printk("flush_tlb_page: kernel_flag %d (cpu %d akp %d)!\n", + klock_info.kernel_flag, cpu, klock_info.akp); + } +#endif + ipi_msg_flush_tb.p.flush_vma = vma; + ipi_msg_flush_tb.flush_addr = addr; + ipi_msg_flush_tb.flush_tb_mask = to_whom; + send_ipi_message(to_whom, TLB_PAGE); + + if (mm != current->mm) + flush_tlb_other(mm); + else + flush_tlb_current_page(mm, vma, addr); + + while (ipi_msg_flush_tb.flush_tb_mask) { + if (--timeout < 0) { + printk("flush_tlb_page: STUCK on CPU %d [0x%x,0x%lx,%d,%d]\n", + cpu, ipi_msg_flush_tb.flush_tb_mask, addr, + klock_info.akp, global_irq_holder); + ipi_msg_flush_tb.flush_tb_mask = 0; + break; + } + udelay(100); + ; /* Wait for all clear from other CPUs. */ + } +} + +void +flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) +{ +#if 0 + flush_tlb_mm(mm); +#else + unsigned int to_whom; + int timeout; + unsigned long where; + + __asm__("mov $26, %0" : "=r" (where)); + + timeout = 10000; + to_whom = cpu_present_map ^ (1 << smp_processor_id()); + +#if 1 + if (!kernel_lock_held()) { + printk("flush_tlb_range: kernel_flag %d (cpu %d akp %d) @ 0x%lx\n", + klock_info.kernel_flag, smp_processor_id(), klock_info.akp, + where); + } +#endif + ipi_msg_flush_tb.p.flush_mm = mm; + ipi_msg_flush_tb.flush_tb_mask = to_whom; + send_ipi_message(to_whom, TLB_MM); + + if (mm != current->mm) + flush_tlb_other(mm); + else + flush_tlb_current(mm); + + while (ipi_msg_flush_tb.flush_tb_mask) { + if (--timeout < 0) { + printk("flush_tlb_range: STUCK on CPU %d mask 0x%x\n", + smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask); + ipi_msg_flush_tb.flush_tb_mask = 0; + break; + } + udelay(100); + ; /* Wait for all clear from other CPUs. */ + } +#endif +} + +#ifdef DEBUG_KERNEL_LOCK +void ___lock_kernel(klock_info_t *klip, int cpu, long ipl) +{ + long regx; + int stuck_lock; + unsigned long inline_pc; + + __asm__("mov $26, %0" : "=r" (inline_pc)); + + try_again: + + stuck_lock = 1<<26; + + __asm__ __volatile__( + "1: ldl_l %1,%0;" + " blbs %1,6f;" + " or %1,1,%1;" + " stl_c %1,%0;" + " beq %1,6f;" + "4: mb\n" + ".section .text2,\"ax\"\n" + "6: mov %5,$16;" + " call_pal %4;" + "7: ldl %1,%0;" + " blt %2,4b # debug\n" + " subl %2,1,%2 # debug\n" + " blbs %1,7b;" + " bis $31,7,$16;" + " call_pal %4;" + " br 1b\n" + ".previous" + : "=m,=m" (__dummy_lock(klip)), "=&r,=&r" (regx), + "=&r,=&r" (stuck_lock) + : "0,0" (__dummy_lock(klip)), "i,i" (PAL_swpipl), + "i,r" (ipl), "2,2" (stuck_lock) + : "$0", "$1", "$16", "$22", "$23", "$24", "$25", "memory"); + + if (stuck_lock < 0) { + printk("___kernel_lock stuck at %lx(%d) held %lx(%d)\n", + inline_pc, cpu, klip->pc, klip->cpu); + goto try_again; + } else { + klip->pc = inline_pc; + klip->cpu = cpu; + } +} +#endif + +#ifdef DEBUG_SPINLOCK +void spin_lock(spinlock_t * lock) +{ + long tmp; + long stuck; + unsigned long inline_pc; + + __asm__("mov $26, %0" : "=r" (inline_pc)); + + try_again: + + stuck = 0x10000000; /* was 4G, now 256M */ + + /* Use sub-sections to put the actual loop at the end + of this object file's text section so as to perfect + branch prediction. */ + __asm__ __volatile__( + "1: ldq_l %0,%1\n" + " subq %2,1,%2\n" + " blbs %0,2f\n" + " or %0,1,%0\n" + " stq_c %0,%1\n" + " beq %0,3f\n" + "4: mb\n" + ".section .text2,\"ax\"\n" + "2: ldq %0,%1\n" + " subq %2,1,%2\n" + "3: blt %2,4b\n" + " blbs %0,2b\n" + " br 1b\n" + ".previous" + : "=r" (tmp), + "=m" (__dummy_lock(lock)), + "=r" (stuck) + : "2" (stuck)); + + if (stuck < 0) { + printk("spinlock stuck at %lx (cur=%lx, own=%lx)\n", + inline_pc, +#if 0 + lock->previous, lock->task +#else + (unsigned long) current, lock->task +#endif + ); + goto try_again; + } else { + lock->previous = (unsigned long) inline_pc; + lock->task = (unsigned long) current; + } +} +#endif /* DEBUG_SPINLOCK */ + +#ifdef DEBUG_RWLOCK +void write_lock(rwlock_t * lock) +{ + long regx, regy; + int stuck_lock, stuck_reader; + unsigned long inline_pc; + + __asm__("mov $26, %0" : "=r" (inline_pc)); + + try_again: + + stuck_lock = 1<<26; + stuck_reader = 1<<26; + + __asm__ __volatile__( + "1: ldl_l %1,%0;" + " blbs %1,6f;" + " or %1,1,%2;" + " stl_c %2,%0;" + " beq %2,6f;" + " blt %1,8f;" + "4: mb\n" + ".section .text2,\"ax\"\n" + "6: ldl %1,%0;" + " blt %3,4b # debug\n" + " subl %3,1,%3 # debug\n" + " blbs %1,6b;" + " br 1b;" + "8: ldl %1,%0;" + " blt %4,4b # debug\n" + " subl %4,1,%4 # debug\n" + " blt %1,8b;" + "9: br 4b\n" + ".previous" + : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy) + , "=&r" (stuck_lock), "=&r" (stuck_reader) + : "0" (__dummy_lock(lock)) + , "3" (stuck_lock), "4" (stuck_reader) + ); + + if (stuck_lock < 0) { + printk("write_lock stuck at %lx\n", inline_pc); + goto try_again; + } + if (stuck_reader < 0) { + printk("write_lock stuck on readers at %lx\n", inline_pc); + goto try_again; + } +} + +void _read_lock(rwlock_t * lock) +{ + long regx; + int stuck_lock; + unsigned long inline_pc; + + __asm__("mov $26, %0" : "=r" (inline_pc)); + + try_again: + + stuck_lock = 1<<26; + + __asm__ __volatile__( + "1: ldl_l %1,%0;" + " blbs %1,6f;" + " subl %1,2,%1;" + " stl_c %1,%0;" + " beq %1,6f;" + "4: mb\n" + ".section .text2,\"ax\"\n" + "6: ldl %1,%0;" + " blt %2,4b # debug\n" + " subl %2,1,%2 # debug\n" + " blbs %1,6b;" + " br 1b\n" + ".previous" + : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (stuck_lock) + : "0" (__dummy_lock(lock)), "2" (stuck_lock) + ); + + if (stuck_lock < 0) { + printk("_read_lock stuck at %lx\n", inline_pc); + goto try_again; + } +} +#endif /* DEBUG_RWLOCK */ diff --git a/arch/alpha/kernel/t2.c b/arch/alpha/kernel/t2.c index 398aaebb5..69ca71404 100644 --- a/arch/alpha/kernel/t2.c +++ b/arch/alpha/kernel/t2.c @@ -8,8 +8,8 @@ * */ #include <linux/kernel.h> +#include <linux/config.h> #include <linux/types.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/sched.h> @@ -19,17 +19,14 @@ #include <asm/ptrace.h> #include <asm/mmu_context.h> -/* NOTE: Herein are back-to-back mb insns. They are magic. - A plausable explanation is that the i/o controler does not properly - handle the system transaction. Another involves timing. Ho hum. */ +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the i/o controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ extern struct hwrpb_struct *hwrpb; extern asmlinkage void wrmces(unsigned long mces); -extern asmlinkage unsigned long whami(void); -extern int alpha_sys_type; - -#define CPUID whami() - /* * Machine check reasons. Defined according to PALcode sources @@ -62,10 +59,14 @@ extern int alpha_sys_type; #define vulp volatile unsigned long * #define vuip volatile unsigned int * -static volatile unsigned int T2_mcheck_expected = 0; -static volatile unsigned int T2_mcheck_taken = 0; -static unsigned long T2_jd; +static volatile unsigned int T2_mcheck_expected[NR_CPUS]; +static volatile unsigned int T2_mcheck_taken[NR_CPUS]; +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int T2_DMA_WIN_BASE = T2_DMA_WIN_BASE_DEFAULT; +unsigned int T2_DMA_WIN_SIZE = T2_DMA_WIN_SIZE_DEFAULT; +unsigned long t2_sm_base; +#endif /* SRM_SETUP */ /* * Given a bus, device, and function number, compute resulting @@ -145,8 +146,10 @@ static int mk_conf_addr(unsigned char bus, unsigned char device_fn, static unsigned int conf_read(unsigned long addr, unsigned char type1) { unsigned long flags; - unsigned int stat0, value; - unsigned int t2_cfg = 0; /* to keep gcc quiet */ + unsigned int stat0, value, cpu; + unsigned long t2_cfg = 0; /* to keep gcc quiet */ + + cpu = smp_processor_id(); save_flags(flags); /* avoid getting hit by machine check */ cli(); @@ -155,43 +158,41 @@ static unsigned int conf_read(unsigned long addr, unsigned char type1) #if 0 /* reset status register to avoid losing errors: */ - stat0 = *(vuip)T2_IOCSR; - *(vuip)T2_IOCSR = stat0; + stat0 = *(vulp)T2_IOCSR; + *(vulp)T2_IOCSR = stat0; mb(); DBG(("conf_read: T2 IOCSR was 0x%x\n", stat0)); +#endif /* if Type1 access, must set T2 CFG */ if (type1) { - t2_cfg = *(vuip)T2_IOC_CFG; + t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL; + *(vulp)T2_HAE_3 = 0x40000000UL | t2_cfg; mb(); - *(vuip)T2_IOC_CFG = t2_cfg | 1; DBG(("conf_read: TYPE1 access\n")); } mb(); draina(); -#endif - T2_mcheck_expected = 1; - T2_mcheck_taken = 0; + T2_mcheck_expected[cpu] = 1; + T2_mcheck_taken[cpu] = 0; mb(); /* access configuration space: */ value = *(vuip)addr; mb(); mb(); /* magic */ - if (T2_mcheck_taken) { - T2_mcheck_taken = 0; + if (T2_mcheck_taken[cpu]) { + T2_mcheck_taken[cpu] = 0; value = 0xffffffffU; mb(); } - T2_mcheck_expected = 0; + T2_mcheck_expected[cpu] = 0; mb(); -#if 0 - /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + /* if Type1 access, must reset T2 CFG so normal IO space ops work */ if (type1) { - *(vuip)T2_IOC_CFG = t2_cfg & ~1; + *(vulp)T2_HAE_3 = t2_cfg; mb(); } -#endif DBG(("conf_read(): finished\n")); restore_flags(flags); @@ -203,44 +204,45 @@ static void conf_write(unsigned long addr, unsigned int value, unsigned char type1) { unsigned long flags; - unsigned int stat0; - unsigned int t2_cfg = 0; /* to keep gcc quiet */ + unsigned int stat0, cpu; + unsigned long t2_cfg = 0; /* to keep gcc quiet */ + + cpu = smp_processor_id(); save_flags(flags); /* avoid getting hit by machine check */ cli(); #if 0 /* reset status register to avoid losing errors: */ - stat0 = *(vuip)T2_IOCSR; - *(vuip)T2_IOCSR = stat0; + stat0 = *(vulp)T2_IOCSR; + *(vulp)T2_IOCSR = stat0; mb(); DBG(("conf_write: T2 ERR was 0x%x\n", stat0)); +#endif /* if Type1 access, must set T2 CFG */ if (type1) { - t2_cfg = *(vuip)T2_IOC_CFG; + t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL; + *(vulp)T2_HAE_3 = t2_cfg | 0x40000000UL; mb(); - *(vuip)T2_IOC_CFG = t2_cfg | 1; DBG(("conf_write: TYPE1 access\n")); } + mb(); draina(); -#endif - T2_mcheck_expected = 1; + T2_mcheck_expected[cpu] = 1; mb(); /* access configuration space: */ *(vuip)addr = value; mb(); mb(); /* magic */ - T2_mcheck_expected = 0; + T2_mcheck_expected[cpu] = 0; mb(); -#if 0 - /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + /* if Type1 access, must reset T2 CFG so normal IO space ops work */ if (type1) { - *(vuip)T2_IOC_CFG = t2_cfg & ~1; + *(vulp)T2_HAE_3 = t2_cfg; mb(); } -#endif DBG(("conf_write(): finished\n")); restore_flags(flags); } @@ -362,17 +364,21 @@ int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, unsigned long t2_init(unsigned long mem_start, unsigned long mem_end) { - unsigned int t2_err; - struct percpu_struct *cpu; - int i; + unsigned long t2_err; + unsigned int i; + + for (i = 0; i < NR_CPUS; i++) { + T2_mcheck_expected[i] = 0; + T2_mcheck_taken[i] = 0; + } #if 0 /* * Set up error reporting. */ - t2_err = *(vuip)T2_IOCSR ; + t2_err = *(vulp)T2_IOCSR ; t2_err |= (0x1 << 7) ; /* master abort */ - *(vuip)T2_IOC_T2_ERR = t2_err ; + *(vulp)T2_IOCSR = t2_err ; mb() ; #endif @@ -388,6 +394,42 @@ unsigned long t2_init(unsigned long mem_start, unsigned long mem_end) *(vulp)T2_TBASE2); #endif +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 1 for enabled and mapped to 0 */ + if (((*(vulp)T2_WBASE1 & (3UL<<18)) == (2UL<<18)) && + (*(vulp)T2_TBASE1 == 0)) + { + T2_DMA_WIN_BASE = *(vulp)T2_WBASE1 & 0xfff00000UL; + T2_DMA_WIN_SIZE = *(vulp)T2_WMASK1 & 0xfff00000UL; + T2_DMA_WIN_SIZE += 0x00100000UL; +/* DISABLE window 2!! ?? */ +#if 1 + printk("t2_init: using Window 1 settings\n"); + printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n", + *(vulp)T2_WBASE1, + *(vulp)T2_WMASK1, + *(vulp)T2_TBASE1); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if (((*(vulp)T2_WBASE2 & (3UL<<18)) == (2UL<<18)) && + (*(vulp)T2_TBASE2 == 0)) + { + T2_DMA_WIN_BASE = *(vulp)T2_WBASE2 & 0xfff00000UL; + T2_DMA_WIN_SIZE = *(vulp)T2_WMASK2 & 0xfff00000UL; + T2_DMA_WIN_SIZE += 0x00100000UL; +/* DISABLE window 1!! ?? */ +#if 1 + printk("t2_init: using Window 2 settings\n"); + printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n", + *(vulp)T2_WBASE2, + *(vulp)T2_WMASK2, + *(vulp)T2_TBASE2); +#endif + } + else /* we must use our defaults... */ +#endif /* SRM_SETUP */ + { /* * Set up the PCI->physical memory translation windows. * For now, window 2 is disabled. In the future, we may @@ -396,13 +438,13 @@ unsigned long t2_init(unsigned long mem_start, unsigned long mem_end) */ /* WARNING!! must correspond to the DMA_WIN params!!! */ - *(vuip)T2_WBASE1 = 0x400807ffU; - *(vuip)T2_WMASK1 = 0x3ff00000U; - *(vuip)T2_TBASE1 = 0; - - *(vuip)T2_WBASE2 = 0x0; + *(vulp)T2_WBASE1 = 0x400807ffU; + *(vulp)T2_WMASK1 = 0x3ff00000U; + *(vulp)T2_TBASE1 = 0; - *(vuip)T2_HBASE = 0x0; + *(vulp)T2_WBASE2 = 0x0; + *(vulp)T2_HBASE = 0x0; + } /* * check ASN in HWRPB for validity, report if bad @@ -420,41 +462,43 @@ unsigned long t2_init(unsigned long mem_start, unsigned long mem_end) * what ARC or SRM might have left behind... */ { -#if 0 - printk("T2_init: HAE1 was 0x%lx\n", *(vulp)T2_HAE_1); - printk("T2_init: HAE2 was 0x%lx\n", *(vulp)T2_HAE_2); - printk("T2_init: HAE3 was 0x%lx\n", *(vulp)T2_HAE_3); - printk("T2_init: HAE4 was 0x%lx\n", *(vulp)T2_HAE_4); + unsigned long t2_hae_1 = *(vulp)T2_HAE_1; + unsigned long t2_hae_2 = *(vulp)T2_HAE_2; + unsigned long t2_hae_3 = *(vulp)T2_HAE_3; + unsigned long t2_hae_4 = *(vulp)T2_HAE_4; +#if 1 + printk("T2_init: HAE1 was 0x%lx\n", t2_hae_1); + printk("T2_init: HAE2 was 0x%lx\n", t2_hae_2); + printk("T2_init: HAE3 was 0x%lx\n", t2_hae_3); + printk("T2_init: HAE4 was 0x%lx\n", t2_hae_4); #endif +#ifdef CONFIG_ALPHA_SRM_SETUP + /* + * sigh... For the SRM setup, unless we know apriori what the HAE + * contents will be, we need to setup the arbitrary region bases + * so we can test against the range of addresses and tailor the + * region chosen for the SPARSE memory access. + * + * see include/asm-alpha/t2.h for the SPARSE mem read/write + */ + t2_sm_base = (t2_hae_1 << 27) & 0xf8000000UL; + /* + Set the HAE cache, so that setup_arch() code + will use the SRM setting always. Our readb/writeb + code in .h expects never to have to change + the contents of the HAE. + */ + hae.cache = t2_hae_1; +#else /* SRM_SETUP */ + *(vulp)T2_HAE_1 = 0; mb(); + *(vulp)T2_HAE_2 = 0; mb(); + *(vulp)T2_HAE_3 = 0; mb(); #if 0 - *(vuip)T2_HAE_1 = 0; mb(); - *(vuip)T2_HAE_2 = 0; mb(); - *(vuip)T2_HAE_3 = 0; mb(); - *(vuip)T2_HAE_4 = 0; mb(); + *(vulp)T2_HAE_4 = 0; mb(); /* do not touch this */ #endif +#endif /* SRM_SETUP */ } -#if 1 - if (hwrpb->nr_processors > 1) { - printk("T2_init: nr_processors 0x%lx\n", - hwrpb->nr_processors); - printk("T2_init: processor_size 0x%lx\n", - hwrpb->processor_size); - printk("T2_init: processor_offset 0x%lx\n", - hwrpb->processor_offset); - - cpu = (struct percpu_struct *) - ((char*)hwrpb + hwrpb->processor_offset); - - for (i = 0; i < hwrpb->nr_processors; i++ ) { - printk("T2_init: CPU 0x%x: flags 0x%lx type 0x%lx\n", - i, cpu->flags, cpu->type); - cpu = (struct percpu_struct *) - ((char *)cpu + hwrpb->processor_size); - } - } -#endif - return mem_start; } @@ -469,17 +513,19 @@ static struct sable_cpu_csr *sable_cpu_regs[4] = { int t2_clear_errors(void) { + unsigned int cpu = smp_processor_id(); + DBGMC(("???????? t2_clear_errors\n")); - sable_cpu_regs[CPUID]->sic &= ~SIC_SEIC; + sable_cpu_regs[cpu]->sic &= ~SIC_SEIC; /* * clear cpu errors */ - sable_cpu_regs[CPUID]->bcce |= sable_cpu_regs[CPUID]->bcce; - sable_cpu_regs[CPUID]->cbe |= sable_cpu_regs[CPUID]->cbe; - sable_cpu_regs[CPUID]->bcue |= sable_cpu_regs[CPUID]->bcue; - sable_cpu_regs[CPUID]->dter |= sable_cpu_regs[CPUID]->dter; + sable_cpu_regs[cpu]->bcce |= sable_cpu_regs[cpu]->bcce; + sable_cpu_regs[cpu]->cbe |= sable_cpu_regs[cpu]->cbe; + sable_cpu_regs[cpu]->bcue |= sable_cpu_regs[cpu]->bcue; + sable_cpu_regs[cpu]->dter |= sable_cpu_regs[cpu]->dter; *(vulp)T2_CERR1 |= *(vulp)T2_CERR1; *(vulp)T2_PERR1 |= *(vulp)T2_PERR1; @@ -499,6 +545,7 @@ void t2_machine_check(unsigned long vector, unsigned long la_ptr, const char * reason; char buf[128]; long i; + unsigned int cpu = smp_processor_id(); DBGMC(("t2_machine_check: vector=0x%lx la_ptr=0x%lx\n", vector, la_ptr)); @@ -516,7 +563,7 @@ void t2_machine_check(unsigned long vector, unsigned long la_ptr, DBGMC((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", regs->pc, mchk_header->elfl_size, mchk_header->elfl_procoffset, mchk_header->elfl_sysoffset)); - DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected)); + DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected[cpu])); #ifdef DEBUG_DUMP { @@ -537,11 +584,11 @@ void t2_machine_check(unsigned long vector, unsigned long la_ptr, */ mb(); mb(); /* magic */ - if (T2_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) { + if (T2_mcheck_expected[cpu]) { DBGMC(("T2 machine check expected\n")); - T2_mcheck_taken = 1; + T2_mcheck_taken[cpu] = 1; t2_clear_errors(); - T2_mcheck_expected = 0; + T2_mcheck_expected[cpu] = 0; mb(); mb(); /* magic */ wrmces(rdmces()|1);/* ??? */ diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index 9d8b56dc3..0456eb171 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -82,6 +82,16 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs) __u32 now; long nticks; +#ifdef __SMP__ + extern void smp_percpu_timer_interrupt(struct pt_regs *); + extern unsigned int boot_cpu_id; + /* when SMP, do this for *all* CPUs, + but only do the rest for the boot CPU */ + smp_percpu_timer_interrupt(regs); + if (smp_processor_id() != boot_cpu_id) + return; +#endif + /* * Estimate how many ticks have passed since the last update. * Round the result, .5 to even. When we loose ticks due to diff --git a/arch/alpha/kernel/tsunami.c b/arch/alpha/kernel/tsunami.c new file mode 100644 index 000000000..3d0fdde89 --- /dev/null +++ b/arch/alpha/kernel/tsunami.c @@ -0,0 +1,503 @@ +/* + * Code common to all TSUNAMI chips. + * + * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com). + * + */ +#include <linux/kernel.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/hwrpb.h> +#include <asm/ptrace.h> +#include <asm/mmu_context.h> + +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the i/o controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ + +extern struct hwrpb_struct *hwrpb; +extern asmlinkage void wrmces(unsigned long mces); + +/* + * BIOS32-style PCI interface: + */ + +#ifdef CONFIG_ALPHA_TSUNAMI + +#ifdef DEBUG +# define DBG(args) printk args +#else +# define DBG(args) +#endif + +#define DEBUG_MCHECK +#ifdef DEBUG_MCHECK +# define DBG_MCK(args) printk args +#define DEBUG_MCHECK_DUMP +#else +# define DBG_MCK(args) +#endif + +#define vuip volatile unsigned int * +#define vulp volatile unsigned long * + +static volatile unsigned int TSUNAMI_mcheck_expected[NR_CPUS]; +static volatile unsigned int TSUNAMI_mcheck_taken[NR_CPUS]; +static unsigned int TSUNAMI_jd[NR_CPUS]; + +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int TSUNAMI_DMA_WIN_BASE = TSUNAMI_DMA_WIN_BASE_DEFAULT; +unsigned int TSUNAMI_DMA_WIN_SIZE = TSUNAMI_DMA_WIN_SIZE_DEFAULT; +#endif /* SRM_SETUP */ + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Note that all config space accesses use Type 1 address format. + * + * Note also that type 1 is determined by non-zero bus number. + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ +static int mk_conf_addr(unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned long *pci_addr, + unsigned char *type1) +{ + unsigned long addr; + + DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + if (bus == 0) { + *type1 = 0; + } else { + /* type 1 configuration cycle: */ + *type1 = 1; + } + addr = (bus << 16) | (device_fn << 8) | (where); + *pci_addr = addr; + DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long addr; + unsigned char type1; + unsigned char result; + + *value = 0xff; + + if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + __asm__ __volatile__ ( + "ldbu %0,%1" + : "=r" (result) + : "m" (*(unsigned char *)(addr+TSUNAMI_PCI0_CONF))); + + *value = result; + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned long addr; + unsigned char type1; + unsigned short result; + + *value = 0xffff; + + if (where & 0x1) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + + __asm__ __volatile__ ( + "ldwu %0,%1" + : "=r" (result) + : "m" (*(unsigned short *)(addr+TSUNAMI_PCI0_CONF))); + + *value = result; + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long addr; + unsigned char type1; + unsigned int result; + + *value = 0xffffffff; + if (where & 0x3) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + + __asm__ __volatile__ ( + "ldl %0,%1" + : "=r" (result) + : "m" (*(unsigned int *)(addr+TSUNAMI_PCI0_CONF))); + + *value = result; + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + __asm__ __volatile__ ( + "stb %1,%0\n\t" + "mb" + : : "m" (*(unsigned char *)(addr+TSUNAMI_PCI0_CONF)), + "r" (value)); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long addr; + unsigned char type1; + + if (where & 0x1) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + __asm__ __volatile__ ( + "stw %1,%0\n\t" + "mb" + : : "m" (*(unsigned short *)(addr+TSUNAMI_PCI0_CONF)), + "r" (value)); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long addr; + unsigned char type1; + + if (where & 0x3) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + __asm__ __volatile__ ( + "stl %1,%0\n\t" + "mb" + : : "m" (*(unsigned int *)(addr+TSUNAMI_PCI0_CONF)), + "r" (value)); + + return PCIBIOS_SUCCESSFUL; +} + + +unsigned long tsunami_init(unsigned long mem_start, unsigned long mem_end) +{ + unsigned long tsunami_err; + unsigned int i; + +#if 0 +printk("tsunami_init: CChip registers:\n"); +printk("tsunami_init: CSR_CSC 0x%lx\n", *(vulp)TSUNAMI_CSR_CSC); +printk("tsunami_init: CSR_MTR 0x%lx\n", *(vulp)TSUNAMI_CSR_MTR); +printk("tsunami_init: CSR_MISC 0x%lx\n", *(vulp)TSUNAMI_CSR_MISC); +printk("tsunami_init: CSR_DIM0 0x%lx\n", *(vulp)TSUNAMI_CSR_DIM0); +printk("tsunami_init: CSR_DIM1 0x%lx\n", *(vulp)TSUNAMI_CSR_DIM1); +printk("tsunami_init: CSR_DIR0 0x%lx\n", *(vulp)TSUNAMI_CSR_DIR0); +printk("tsunami_init: CSR_DIR1 0x%lx\n", *(vulp)TSUNAMI_CSR_DIR1); +printk("tsunami_init: CSR_DRIR 0x%lx\n", *(vulp)TSUNAMI_CSR_DRIR); + +printk("tsunami_init: DChip registers:\n"); +printk("tsunami_init: CSR_DSC 0x%lx\n", *(vulp)TSUNAMI_CSR_DSC); +printk("tsunami_init: CSR_STR 0x%lx\n", *(vulp)TSUNAMI_CSR_STR); +printk("tsunami_init: CSR_DREV 0x%lx\n", *(vulp)TSUNAMI_CSR_DREV); + +printk("tsunami_init: PChip registers:\n"); +printk("tsunami_init: PCHIP0_WSBA0 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA0); +printk("tsunami_init: PCHIP0_WSBA1 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA1); +printk("tsunami_init: PCHIP0_WSBA2 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA2); +printk("tsunami_init: PCHIP0_WSBA3 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA3); +printk("tsunami_init: PCHIP0_WSM0 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM0); +printk("tsunami_init: PCHIP0_WSM1 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM1); +printk("tsunami_init: PCHIP0_WSM2 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM2); +printk("tsunami_init: PCHIP0_WSM3 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM3); +printk("tsunami_init: PCHIP0_TBA0 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA0); +printk("tsunami_init: PCHIP0_TBA1 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA1); +printk("tsunami_init: PCHIP0_TBA2 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA2); +printk("tsunami_init: PCHIP0_TBA3 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA3); + +printk("tsunami_init: PCHIP0_PCTL 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PCTL); +printk("tsunami_init: PCHIP0_PLAT 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PLAT); +printk("tsunami_init: PCHIP0_PERROR 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PERROR); +printk("tsunami_init: PCHIP0_PERRMASK 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PERRMASK); + +#endif + + for (i = 0; i < NR_CPUS; i++) { + TSUNAMI_mcheck_expected[i] = 0; + TSUNAMI_mcheck_taken[i] = 0; + } +#ifdef NOT_YET + /* + * Set up error reporting. Make sure CPU_PE is OFF in the mask. + */ + tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERRMASK; + tsunami_err &= ~20; + *(vulp)TSUNAMI_PCHIP0_PERRMASK = tsunami_err; + mb(); + tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERRMASK; + + tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERROR ; + tsunami_err |= 0x40; /* master/target abort */ + *(vulp)TSUNAMI_PCHIP0_PERROR = tsunami_err ; + mb() ; + tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERROR ; +#endif /* NOT_YET */ + +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 0 for enabled and mapped to 0 */ + if (((*(vulp)TSUNAMI_PCHIP0_WSBA0 & 3) == 1) && + (*(vulp)TSUNAMI_PCHIP0_TBA0 == 0) && + ((*(vulp)TSUNAMI_PCHIP0_WSM0 & 0xfff00000U) > 0x0ff00000U)) + { + TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA0 & 0xfff00000U; + TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM0 & 0xfff00000U; + TSUNAMI_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("tsunami_init: using Window 0 settings\n"); + printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vulp)TSUNAMI_PCHIP0_WSBA0, + *(vulp)TSUNAMI_PCHIP0_WSM0, + *(vulp)TSUNAMI_PCHIP0_TBA0); +#endif + } + else /* check window 1 for enabled and mapped to 0 */ + if (((*(vulp)TSUNAMI_PCHIP0_WSBA1 & 3) == 1) && + (*(vulp)TSUNAMI_PCHIP0_TBA1 == 0) && + ((*(vulp)TSUNAMI_PCHIP0_WSM1 & 0xfff00000U) > 0x0ff00000U)) +{ + TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA1 & 0xfff00000U; + TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM1 & 0xfff00000U; + TSUNAMI_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("tsunami_init: using Window 1 settings\n"); + printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vulp)TSUNAMI_PCHIP0_WSBA1, + *(vulp)TSUNAMI_PCHIP0_WSM1, + *(vulp)TSUNAMI_PCHIP0_TBA1); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if (((*(vulp)TSUNAMI_PCHIP0_WSBA2 & 3) == 1) && + (*(vulp)TSUNAMI_PCHIP0_TSB2 == 0) && + ((*(vulp)TSUNAMI_PCHIP0_WSM2 & 0xfff00000U) > 0x0ff00000U)) + { + TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA2 & 0xfff00000U; + TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM2 & 0xfff00000U; + TSUNAMI_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("tsunami_init: using Window 2 settings\n"); + printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vulp)TSUNAMI_PCHIP0_WSBA2, + *(vulp)TSUNAMI_PCHIP0_WSM2, + *(vulp)TSUNAMI_PCHIP0_TSB2); +#endif + } + else /* check window 3 for enabled and mapped to 0 */ + if (((*(vulp)TSUNAMI_PCHIP0_WSBA3 & 3) == 1) && + (*(vulp)TSUNAMI_PCHIP0_TBA3 == 0) && + ((*(vulp)TSUNAMI_PCHIP0_WSM3 & 0xfff00000U) > 0x0ff00000U)) + { + TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA3 & 0xfff00000U; + TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM3 & 0xfff00000U; + TSUNAMI_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("tsunami_init: using Window 3 settings\n"); + printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vulp)TSUNAMI_PCHIP0_WSBA3, + *(vulp)TSUNAMI_PCHIP0_WSM3, + *(vulp)TSUNAMI_PCHIP0_TBA3); +#endif + } + else /* we must use our defaults which were pre-initialized... */ +#endif /* SRM_SETUP */ + { + /* + * Set up the PCI->physical memory translation windows. + * For now, windows 1,2 and 3 are disabled. In the future, we may + * want to use them to do scatter/gather DMA. Window 0 + * goes at 1 GB and is 1 GB large. + */ + + *(vulp)TSUNAMI_PCHIP0_WSBA0 = 1L | (TSUNAMI_DMA_WIN_BASE & 0xfff00000U); + *(vulp)TSUNAMI_PCHIP0_WSM0 = (TSUNAMI_DMA_WIN_SIZE - 1) & 0xfff00000UL; + *(vulp)TSUNAMI_PCHIP0_TBA0 = 0UL; + + *(vulp)TSUNAMI_PCHIP0_WSBA1 = 0UL; + *(vulp)TSUNAMI_PCHIP0_WSBA2 = 0UL; + *(vulp)TSUNAMI_PCHIP0_WSBA3 = 0UL; + mb(); + } + + /* + * check ASN in HWRPB for validity, report if bad + */ + if (hwrpb->max_asn != MAX_ASN) { + printk("TSUNAMI_init: max ASN from HWRPB is bad (0x%lx)\n", + hwrpb->max_asn); + hwrpb->max_asn = MAX_ASN; + } + + return mem_start; +} + +int tsunami_pci_clr_err(void) +{ + unsigned int cpu = smp_processor_id(); + + TSUNAMI_jd[cpu] = *((vulp)TSUNAMI_PCHIP0_PERROR); + DBG(("TSUNAMI_pci_clr_err: PERROR after read 0x%x\n", TSUNAMI_jd[cpu])); + *((vulp)TSUNAMI_PCHIP0_PERROR) = 0x040; mb(); + TSUNAMI_jd[cpu] = *((vulp)TSUNAMI_PCHIP0_PERROR); + return 0; +} + +void tsunami_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ +#if 1 + printk("TSUNAMI machine check ignored\n") ; +#else + struct el_common *mchk_header; + struct el_TSUNAMI_sysdata_mcheck *mchk_sysdata; + unsigned int cpu = smp_processor_id(); + + mchk_header = (struct el_common *)la_ptr; + + mchk_sysdata = + (struct el_TSUNAMI_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset); + +#if 0 + DBG_MCK(("tsunami_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->size, mchk_header->proc_offset, + mchk_header->sys_offset)); + DBG_MCK(("tsunami_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", + TSUNAMI_mcheck_expected[cpu], mchk_sysdata->epic_dcsr, + mchk_sysdata->epic_pear)); +#endif +#ifdef DEBUG_MCHECK_DUMP + { + unsigned long *ptr; + int i; + + ptr = (unsigned long *)la_ptr; + for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { + printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); + } + } +#endif /* DEBUG_MCHECK_DUMP */ + /* + * Check if machine check is due to a badaddr() and if so, + * ignore the machine check. + */ + mb(); + mb(); /* magic */ + if (TSUNAMI_mcheck_expected[cpu]) { + DBG(("TSUNAMI machine check expected\n")); + TSUNAMI_mcheck_expected[cpu] = 0; + TSUNAMI_mcheck_taken[cpu] = 1; + mb(); + mb(); /* magic */ + draina(); + tsunami_pci_clr_err(); + wrmces(0x7); + mb(); + } +#if 1 + else { + printk("TSUNAMI machine check NOT expected\n") ; + DBG_MCK(("tsunami_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->size, mchk_header->proc_offset, + mchk_header->sys_offset)); + TSUNAMI_mcheck_expected[cpu] = 0; + TSUNAMI_mcheck_taken[cpu] = 1; + mb(); + mb(); /* magic */ + draina(); + tsunami_pci_clr_err(); + wrmces(0x7); + mb(); + } +#endif +#endif +} + +#endif /* CONFIG_ALPHA_TSUNAMI */ diff --git a/arch/alpha/lib/checksum.c b/arch/alpha/lib/checksum.c index f95b535ca..5165279f0 100644 --- a/arch/alpha/lib/checksum.c +++ b/arch/alpha/lib/checksum.c @@ -37,6 +37,27 @@ unsigned short int csum_tcpudp_magic(unsigned long saddr, ((unsigned long) proto << 8)); } +unsigned int csum_tcpudp_nofold(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + unsigned long result; + + result = (saddr + daddr + sum + + ((unsigned long) ntohs(len) << 16) + + ((unsigned long) proto << 8)); + + /* Fold down to 32-bits so we don't loose in the typedef-less + network stack. */ + /* 64 to 33 */ + result = (result & 0xffffffff) + (result >> 32); + /* 33 to 32 */ + result = (result & 0xffffffff) + (result >> 32); + return result; +} + /* * Do a 64-bit checksum on an arbitrary memory area.. * diff --git a/arch/alpha/lib/copy_user.S b/arch/alpha/lib/copy_user.S index da57fd6d1..aa309b9f5 100644 --- a/arch/alpha/lib/copy_user.S +++ b/arch/alpha/lib/copy_user.S @@ -27,11 +27,18 @@ */ /* Allow an exception for an insn; exit if we get one. */ -#define EX(x,y...) \ +#define EXI(x,y...) \ 99: x,##y; \ .section __ex_table,"a"; \ .gprel32 99b; \ - lda $31, $exit-99b($31); \ + lda $31, $exitin-99b($31); \ + .previous + +#define EXO(x,y...) \ + 99: x,##y; \ + .section __ex_table,"a"; \ + .gprel32 99b; \ + lda $31, $exitout-99b($31); \ .previous .set noat @@ -45,14 +52,14 @@ __copy_user: subq $3,8,$3 .align 5 $37: - EX( ldq_u $1,0($7) ) - EX( ldq_u $2,0($6) ) + EXI( ldq_u $1,0($7) ) + EXO( ldq_u $2,0($6) ) extbl $1,$7,$1 mskbl $2,$6,$2 insbl $1,$6,$1 addq $3,1,$3 bis $1,$2,$1 - EX( stq_u $1,0($6) ) + EXO( stq_u $1,0($6) ) subq $0,1,$0 addq $6,1,$6 addq $7,1,$7 @@ -63,10 +70,10 @@ $36: bic $0,7,$4 beq $1,$43 beq $4,$48 - EX( ldq_u $3,0($7) ) + EXI( ldq_u $3,0($7) ) .align 5 $50: - EX( ldq_u $2,8($7) ) + EXI( ldq_u $2,8($7) ) subq $4,8,$4 extql $3,$7,$3 extqh $2,$7,$1 @@ -81,13 +88,13 @@ $48: beq $0,$41 .align 5 $57: - EX( ldq_u $1,0($7) ) - EX( ldq_u $2,0($6) ) + EXI( ldq_u $1,0($7) ) + EXO( ldq_u $2,0($6) ) extbl $1,$7,$1 mskbl $2,$6,$2 insbl $1,$6,$1 bis $1,$2,$1 - EX( stq_u $1,0($6) ) + EXO( stq_u $1,0($6) ) subq $0,1,$0 addq $6,1,$6 addq $7,1,$7 @@ -98,7 +105,7 @@ $43: beq $4,$65 .align 5 $66: - EX( ldq $1,0($7) ) + EXI( ldq $1,0($7) ) subq $4,8,$4 stq $1,0($6) addq $7,8,$7 @@ -107,15 +114,31 @@ $66: bne $4,$66 $65: beq $0,$41 - EX( ldq $2,0($7) ) - EX( ldq $1,0($6) ) + EXI( ldq $2,0($7) ) + EXO( ldq $1,0($6) ) mskql $2,$0,$2 mskqh $1,$0,$1 bis $2,$1,$2 - EX( stq $2,0($6) ) + EXO( stq $2,0($6) ) bis $31,$31,$0 $41: $35: -$exit: +$exitout: ret $31,($28),1 + +$exitin: + /* A stupid byte-by-byte zeroing of the rest of the output + buffer. This cures security holes by never leaving + random kernel data around to be copied elsewhere. */ + + mov $0,$1 +$101: + EXO ( ldq_u $2,0($6) ) + subq $1,1,$1 + mskbl $2,$6,$2 + EXO ( stq_u $2,0($6) ) + addq $6,1,$6 + bgt $1,$101 + ret $31,($28),1 + .end __copy_user diff --git a/arch/alpha/lib/csum_partial_copy.c b/arch/alpha/lib/csum_partial_copy.c index 1328eeaba..713081330 100644 --- a/arch/alpha/lib/csum_partial_copy.c +++ b/arch/alpha/lib/csum_partial_copy.c @@ -365,6 +365,12 @@ csum_partial_copy_from_user(const char *src, char *dst, int len, } unsigned int +csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum) +{ + return do_csum_partial_copy_from_user(src, dst, len, sum, NULL); +} + +unsigned int csum_partial_copy (const char *src, char *dst, int len, unsigned int sum) { unsigned int ret; diff --git a/arch/alpha/lib/memcpy.c b/arch/alpha/lib/memcpy.c index bcfac1020..dc708c73e 100644 --- a/arch/alpha/lib/memcpy.c +++ b/arch/alpha/lib/memcpy.c @@ -104,7 +104,7 @@ static inline void __memcpy_aligned(unsigned long d, unsigned long s, long n) DO_REST_ALIGNED(d,s,n); } -void * __memcpy(void * dest, const void *src, size_t n) +void * memcpy(void * dest, const void *src, size_t n) { if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) { __memcpy_aligned((unsigned long) dest, (unsigned long) src, n); @@ -114,22 +114,5 @@ void * __memcpy(void * dest, const void *src, size_t n) return dest; } -/* - * Broken compiler uses "bcopy" to do internal - * assignments. Silly OSF/1 BSDism. - */ -char * bcopy(const char * src, char * dest, size_t n) -{ - __memcpy(dest, src, n); - return dest; -} - -/* - * gcc-2.7.1 and newer generate calls to memset and memcpy. So we - * need to define that here: - */ -#ifdef __ELF__ - asm (".weak memcpy; memcpy = __memcpy"); -#else - asm (".weakext memcpy, __memcpy"); -#endif +/* For backward modules compatibility, define __memcpy. */ +asm("__memcpy = memcpy; .globl __memcpy"); diff --git a/arch/alpha/math-emu/ieee-math.c b/arch/alpha/math-emu/ieee-math.c index 59d7dfa6d..b3d896389 100644 --- a/arch/alpha/math-emu/ieee-math.c +++ b/arch/alpha/math-emu/ieee-math.c @@ -733,19 +733,23 @@ ieee_CVTQT (int f, unsigned long a, unsigned long *b) * FPCR_INV if invalid operation occurred, etc. */ unsigned long -ieee_CVTTQ (int f, unsigned long a, unsigned long *b) +ieee_CVTTQ (int f, unsigned long a, unsigned long *pb) { unsigned int midway; - unsigned long ov, uv, res = 0; + unsigned long ov, uv, res, b; fpclass_t a_type; EXTENDED temp; - *b = 0; a_type = extend_ieee(a, &temp, DOUBLE); + + b = 0x7fffffffffffffff; + res = FPCR_INV; if (a_type == NaN || a_type == INFTY) - return FPCR_INV; + goto out; + + res = 0; if (a_type == QNaN) - return 0; + goto out; if (temp.e > 0) { ov = 0; @@ -757,7 +761,7 @@ ieee_CVTTQ (int f, unsigned long a, unsigned long *b) if (ov || (temp.f[1] & 0xffc0000000000000)) res |= FPCR_IOV | FPCR_INE; } - if (temp.e < 0) { + else if (temp.e < 0) { while (temp.e < 0) { ++temp.e; uv = temp.f[0] & 1; /* save sticky bit */ @@ -765,7 +769,8 @@ ieee_CVTTQ (int f, unsigned long a, unsigned long *b) temp.f[0] |= uv; } } - *b = ((temp.f[1] << 9) | (temp.f[0] >> 55)) & 0x7fffffffffffffff; + b = (temp.f[1] << 9) | (temp.f[0] >> 55); + /* * Notice: the fraction is only 52 bits long. Thus, rounding * cannot possibly result in an integer overflow. @@ -776,18 +781,18 @@ ieee_CVTTQ (int f, unsigned long a, unsigned long *b) midway = (temp.f[0] & 0x003fffffffffffff) == 0; if ((midway && (temp.f[0] & 0x0080000000000000)) || !midway) - ++*b; + ++b; } break; case ROUND_PINF: if ((temp.f[0] & 0x007fffffffffffff) != 0) - ++*b; + ++b; break; case ROUND_NINF: if ((temp.f[0] & 0x007fffffffffffff) != 0) - --*b; + --b; break; case ROUND_CHOP: @@ -798,8 +803,11 @@ ieee_CVTTQ (int f, unsigned long a, unsigned long *b) res |= FPCR_INE; if (temp.s) { - *b = -*b; + b = -b; } + +out: + *pb = b; return res; } diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c index e44639fb2..603b33149 100644 --- a/arch/alpha/mm/fault.c +++ b/arch/alpha/mm/fault.c @@ -14,13 +14,52 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/mmu_context.h> +#ifdef __SMP__ +unsigned long last_asn[NR_CPUS] = { /* gag */ + ASN_FIRST_VERSION + (0 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (1 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (2 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (3 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (4 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (5 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (6 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (7 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (8 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (9 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (10 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (11 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (12 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (13 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (14 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (15 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (16 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (17 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (18 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (19 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (20 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (21 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (22 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (23 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (24 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (25 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (26 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (27 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (28 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (29 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (30 << WIDTH_HARDWARE_ASN), + ASN_FIRST_VERSION + (31 << WIDTH_HARDWARE_ASN) +}; +#else unsigned long asn_cache = ASN_FIRST_VERSION; +#endif /* __SMP__ */ #ifndef BROKEN_ASN /* @@ -30,7 +69,8 @@ unsigned long asn_cache = ASN_FIRST_VERSION; */ void get_new_asn_and_reload(struct task_struct *tsk, struct mm_struct *mm) { - get_new_mmu_context(tsk, mm, asn_cache); + mm->context = 0; + get_new_mmu_context(tsk, mm); reload_context(tsk); } #endif @@ -84,6 +124,7 @@ asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, } } + lock_kernel(); down(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) @@ -112,7 +153,7 @@ good_area: } handle_mm_fault(current, vma, address, cause > 0); up(&mm->mmap_sem); - return; + goto out; /* * Something tried to access memory that isn't in our memory map.. @@ -123,16 +164,17 @@ bad_area: if (user_mode(regs)) { force_sig(SIGSEGV, current); - return; + goto out; } /* Are we prepared to handle this fault as an exception? */ if ((fixup = search_exception_table(regs->pc)) != 0) { unsigned long newpc; newpc = fixup_exception(dpf_reg, fixup, regs->pc); - printk("%s: Exception at [<%lx>] (%lx)\n", current->comm, regs->pc, newpc); + printk("%s: Exception at [<%lx>] (%lx)\n", + current->comm, regs->pc, newpc); regs->pc = newpc; - return; + goto out; } /* @@ -143,4 +185,7 @@ bad_area: "virtual address %016lx\n", address); die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16); do_exit(SIGKILL); + out: + unlock_kernel(); } + diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index 67faa97d4..7562f6709 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -26,6 +26,8 @@ extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); +struct thread_struct * original_pcb_ptr; + /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a @@ -81,15 +83,22 @@ void show_mem(void) extern unsigned long free_area_init(unsigned long, unsigned long); -static void load_PCB(struct thread_struct * pcb) +static struct thread_struct * load_PCB(struct thread_struct * pcb) { + struct thread_struct *old_pcb; + __asm__ __volatile__( - "stq $30,0(%0)\n\t" - "bis %0,%0,$16\n\t" - "call_pal %1" - : /* no outputs */ + "stq $30,0(%1)\n\t" + "bis %1,%1,$16\n\t" +#ifdef CONFIG_ALPHA_DP264 + "zap $16,0xe0,$16\n\t" +#endif /* DP264 */ + "call_pal %2\n\t" + "bis $0,$0,%0" + : "=r" (old_pcb) : "r" (pcb), "i" (PAL_swpctx) : "$0", "$1", "$16", "$22", "$23", "$24", "$25"); + return old_pcb; } /* @@ -107,7 +116,8 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) start_mem = free_area_init(start_mem, end_mem); /* find free clusters, update mem_map[] accordingly */ - memdesc = (struct memdesc_struct *) (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB); + memdesc = (struct memdesc_struct *) + (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB); cluster = memdesc->cluster; for (i = memdesc->numclusters ; i > 0; i--, cluster++) { unsigned long pfn, nr; @@ -129,16 +139,47 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) memset((void *) ZERO_PAGE, 0, PAGE_SIZE); memset(swapper_pg_dir, 0, PAGE_SIZE); newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT; - pgd_val(swapper_pg_dir[1023]) = (newptbr << 32) | pgprot_val(PAGE_KERNEL); + pgd_val(swapper_pg_dir[1023]) = + (newptbr << 32) | pgprot_val(PAGE_KERNEL); init_task.tss.ptbr = newptbr; init_task.tss.pal_flags = 1; /* set FEN, clear everything else */ init_task.tss.flags = 0; - load_PCB(&init_task.tss); + original_pcb_ptr = + phys_to_virt((unsigned long)load_PCB(&init_task.tss)); +#if 0 +printk("OKSP 0x%lx OPTBR 0x%lx\n", + original_pcb_ptr->ksp, original_pcb_ptr->ptbr); +#endif - flush_tlb_all(); + tbia(); return start_mem; } +#ifdef __SMP__ +/* + * paging_init_secondary(), called ONLY by secondary CPUs, + * sets up current->tss contents appropriately and does a load_PCB. + * note that current should be pointing at the idle thread task struct + * for this CPU. + */ +void paging_init_secondary(void) +{ + current->tss.ptbr = init_task.tss.ptbr; + current->tss.pal_flags = 1; + current->tss.flags = 0; + +#if 0 +printk("paging_init_secondary: KSP 0x%lx PTBR 0x%lx\n", + current->tss.ksp, current->tss.ptbr); +#endif + + load_PCB(¤t->tss); + tbia(); + + return; +} +#endif /* __SMP__ */ + void mem_init(unsigned long start_mem, unsigned long end_mem) { unsigned long tmp; diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 5c1efb76a..0c39121be 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -12,6 +12,9 @@ # # Copyright (C) 1995, 1996 by Russell King +CFLAGS_PROC := +ASFLAGS_PROC := + ifeq ($(CONFIG_CPU_ARM2),y) PROCESSOR = armo ASFLAGS_PROC += -m2 @@ -65,7 +68,6 @@ endif # ZRELADDR - Compressed kernel relocating address (point at which uncompressed kernel is loaded). # -HEAD := arch/arm/kernel/head-$(PROCESSOR).o arch/arm/kernel/init_task.o COMPRESSED_HEAD = head.o ifeq ($(PROCESSOR),armo) @@ -79,11 +81,13 @@ endif ifeq ($(CONFIG_ARCH_A5K),y) MACHINE = a5k +ARCHDIR = arc COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o endif ifeq ($(CONFIG_ARCH_ARC),y) MACHINE = arc +ARCHDIR = arc COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o endif @@ -96,6 +100,7 @@ endif ifeq ($(CONFIG_ARCH_RPC),y) MACHINE = rpc +ARCHDIR = rpc COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o ZTEXTADDR = 0x10008000 ZRELADDR = 0x10008000 @@ -103,6 +108,14 @@ endif ifeq ($(CONFIG_ARCH_EBSA110),y) MACHINE = ebsa110 +ARCHDIR = ebsa110 +ZTEXTADDR = 0x00008000 +ZRELADDR = 0x00008000 +endif + +ifeq ($(CONFIG_ARCH_EBSA285),y) +MACHINE = ebsa285 +ARCHDIR = ebsa285 ZTEXTADDR = 0x00008000 ZRELADDR = 0x00008000 endif @@ -129,35 +142,55 @@ ifeq ($(CONFIG_FRAME_POINTER),y) CFLAGS := $(CFLAGS:-fomit-frame-pointer=) endif CFLAGS := $(CFLAGS_PROC) $(CFLAGS) -pipe -ASFLAGS := $(ASFLAGS_PROC) $(ASFLAGS) -D__ASSEMBLY__ +ASFLAGS := $(ASFLAGS_PROC) $(ASFLAGS) LINKFLAGS = -T $(TOPDIR)/arch/arm/vmlinux.lds -e stext -Ttext $(TEXTADDR) ZLINKFLAGS = -Ttext $(ZTEXTADDR) SUBDIRS := $(SUBDIRS:drivers=) arch/arm/lib arch/arm/kernel arch/arm/mm arch/arm/drivers +HEAD := arch/arm/kernel/head-$(PROCESSOR).o arch/arm/kernel/init_task.o CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES) LIBS := arch/arm/lib/lib.a $(LIBS) $(GCCLIB) -DRIVERS := arch/arm/drivers/block/block.a \ - arch/arm/drivers/char/char.a \ - drivers/misc/misc.a \ - arch/arm/drivers/net/net.a +BLOCK_DRIVERS := arch/arm/drivers/block/block.a +CDROM_DRIVERS := drivers/cdrom/cdrom.a +CHAR_DRIVERS := arch/arm/drivers/char/char.a +MISC_DRIVERS := drivers/misc/misc.a +NET_DRIVERS := drivers/net/net.a +PARIDE_DRIVERS := drivers/block/paride/paride.a +PCI_DRIVERS := drivers/pci/pci.a +SCSI_DRIVERS := drivers/scsi/scsi.a +SOUND_DRIVERS := drivers/sound/sound.a -ifeq ($(CONFIG_SCSI),y) -DRIVERS := $(DRIVERS) arch/arm/drivers/scsi/scsi.a +ifeq ($(CONFIG_ARCH_ACORN),y) +BLOCK_DRIVERS += drivers/acorn/block/acorn-block.a +CHAR_DRIVERS += drivers/acorn/char/acorn-char.a +NET_DRIVERS += drivers/acorn/net/acorn-net.a drivers/net/net.a +SCSI_DRIVERS += drivers/acorn/scsi/acorn-scsi.a endif +DRIVERS := $(BLOCK_DRIVERS) $(CHAR_DRIVERS) $(MISC_DRIVERS) $(NET_DRIVERS) + +ifeq ($(CONFIG_SCSI),y) +DRIVERS := $(DRIVERS) $(SCSI_DRIVERS) +endif ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),) -DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a +DRIVERS := $(DRIVERS) $(CDROM_DRIVERS) +endif +ifdef CONFIG_PCI +DRIVERS := $(DRIVERS) $(PCI_DRIVERS) endif - ifeq ($(CONFIG_SOUND),y) -DRIVERS := $(DRIVERS) arch/arm/drivers/sound/sound.a +DRIVERS := $(DRIVERS) $(SOUND_DRIVERS) +endif +ifeq ($(CONFIG_PARIDE),y) +DRIVERS := $(DRIVERS) $(PARIDE_DRIVERS) endif symlinks:: $(RM) include/asm-arm/arch include/asm-arm/proc - (cd include/asm-arm; ln -sf arch-$(MACHINE) arch; ln -sf proc-$(PROCESSOR) proc) + (cd include/asm-arm; ln -sf arch-$(ARCHDIR) arch; ln -sf proc-$(PROCESSOR) proc) +# Once we've finished integrating the sources, the @$(MAKE) will disappear mrproper:: rm -f include/asm-arm/arch include/asm-arm/proc @$(MAKE) -C arch/$(ARCH)/drivers mrproper @@ -183,7 +216,6 @@ install: vmlinux @$(MAKEBOOT) install # My testing targets (that short circuit a few dependencies) -# zImg:; @$(MAKEBOOT) zImage Img:; @$(MAKEBOOT) Image i:; @$(MAKEBOOT) install @@ -191,8 +223,7 @@ zi:; @$(MAKEBOOT) zinstall archclean: @$(MAKEBOOT) clean - @$(MAKE) -C arch/arm/lib clean + $(RM) arch/arm/lib/constants.h archdep: @$(MAKEBOOT) dep -sed -e /^MACHINE..*=/s,= .*,= rpc,;/^PROCESSOR..*=/s,= .*,= armv, linux/arch/arm/Makefile.normal diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index e6050bf13..d210b92c2 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -25,11 +25,8 @@ install: $(CONFIGURE) Image zinstall: $(CONFIGURE) zImage sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)" -tools/build: tools/build.c - $(HOSTCC) $(HOSTCFLAGS) -o $@ $< -I$(TOPDIR)/include - clean: - rm -f Image zImage tools/build + rm -f Image zImage @$(MAKE) -C compressed clean dep: diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 8e49f5dd0..0c6a04c5b 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -26,7 +26,7 @@ piggy.o: $(SYSTEM) $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ - $(LD) -m elf_arm -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-arm -T $$tmppiggy.lnk; \ + $(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-arm -T $$tmppiggy.lnk; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; clean:; rm -f vmlinux core diff --git a/arch/arm/config.in b/arch/arm/config.in index b95c2f16e..3396b1510 100644 --- a/arch/arm/config.in +++ b/arch/arm/config.in @@ -7,38 +7,25 @@ mainmenu_name "Linux Kernel Configuration" define_bool CONFIG_ARM y mainmenu_option next_comment -comment 'Code maturity level options' -bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL -endmenu - -mainmenu_option next_comment -comment 'Loadable module support' -bool 'Enable loadable module support' CONFIG_MODULES -if [ "$CONFIG_MODULES" = "y" ]; then - bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool 'Kernel module loader' CONFIG_KMOD -fi -endmenu - -mainmenu_option next_comment -comment 'General setup' +comment 'System type and processor type' choice 'ARM system type' \ "Archimedes CONFIG_ARCH_ARC \ A5000 CONFIG_ARCH_A5K \ RiscPC CONFIG_ARCH_RPC \ EBSA-110 CONFIG_ARCH_EBSA110 \ + EBSA-285 CONFIG_ARCH_EBSA285 \ NexusPCI CONFIG_ARCH_NEXUSPCI" RiscPC if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" -o "$CONFIG_ARCH_RPC" = "y" ]; then define_bool CONFIG_ARCH_ACORN y else define_bool CONFIG_ARCH_ACORN n fi -if [ "$CONFIG_ARCH_NEXUSPCI" = "y" ]; then +if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o "$CONFIG_ARCH_EBSA285" = "y" ]; then define_bool CONFIG_PCI y else define_bool CONFIG_PCI n fi -if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o "$CONFIG_ARCH_EBSA110" = "y" ]; then +if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o "$CONFIG_ARCH_EBSA110" = "y" -o "$CONFIG_ARCH_EBSA285" = "y" ]; then define_bool CONFIG_CPU_SA110 y else if [ "$CONFIG_ARCH_A5K" = "y" ]; then @@ -51,11 +38,31 @@ else StrongARM CONFIG_CPU_SA110" StrongARM fi fi +endmenu + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'Loadable module support' +bool 'Enable loadable module support' CONFIG_MODULES +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS + bool 'Kernel module loader' CONFIG_KMOD +fi +endmenu + +mainmenu_option next_comment +comment 'General setup' bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER bool 'Use new compilation options (for GCC 2.8)' CONFIG_BINUTILS_NEW bool 'Debug kernel errors' CONFIG_DEBUG_ERRORS bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC +# This needs kernel/acct.c to be updated +#bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF @@ -70,6 +77,7 @@ fi endmenu source arch/arm/drivers/block/Config.in +source drivers/acorn/block/Config.in if [ "$CONFIG_NET" = "y" ]; then source net/Config.in @@ -81,7 +89,7 @@ comment 'SCSI support' tristate 'SCSI support?' CONFIG_SCSI if [ "$CONFIG_SCSI" != "n" ]; then - source arch/arm/drivers/scsi/Config.in + source drivers/scsi/Config.in fi endmenu @@ -91,7 +99,7 @@ if [ "$CONFIG_NET" = "y" ]; then bool 'Network device support?' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then - source arch/arm/drivers/net/Config.in + source drivers/net/Config.in fi endmenu fi @@ -128,7 +136,7 @@ if [ "$CONFIG_ARCH_ACORN" = "y" ]; then tristate 'Sound support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source arch/arm/drivers/sound/Config.in + source drivers/sound/Config.in fi endmenu fi diff --git a/arch/arm/defconfig b/arch/arm/defconfig index 48358557b..5e587ffbf 100644 --- a/arch/arm/defconfig +++ b/arch/arm/defconfig @@ -31,6 +31,7 @@ CONFIG_ARCH_ACORN=y CONFIG_CPU_SA110=y CONFIG_FRAME_POINTER=y # CONFIG_BINUTILS_NEW is not set +CONFIG_DEBUG_ERRORS=y CONFIG_NET=y CONFIG_SYSVIPC=y CONFIG_SYSCTL=y @@ -76,7 +77,7 @@ CONFIG_BLK_DEV_PART=y # # Networking options # -CONFIG_PACKET=m +# CONFIG_PACKET is not set # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set @@ -112,8 +113,11 @@ CONFIG_IP_NOSR=y # CONFIG_BRIDGE is not set # CONFIG_LLC is not set # CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set # CONFIG_NET_SCHED is not set +# CONFIG_NET_PROFILE is not set # # SCSI support @@ -149,7 +153,6 @@ CONFIG_SCSI_POWERTECSCSI=m # The following drives are not fully supported # CONFIG_SCSI_CUMANA_1=m -CONFIG_SCSI_ECOSCSI=m CONFIG_SCSI_OAK1=m CONFIG_SCSI_PPA=m CONFIG_SCSI_PPA_HAVE_PEDANTIC=2 @@ -199,6 +202,7 @@ CONFIG_LOCKD=y # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set CONFIG_ADFS_FS=y +CONFIG_ADFS_FS=y # CONFIG_MAC_PARTITION is not set CONFIG_NLS=y @@ -252,10 +256,10 @@ CONFIG_RPCMOUSE=y # # Sound # -# CONFIG_SOUND is not set -# CONFIG_VIDC is not set -# CONFIG_AUDIO is not set -# DSP_BUFFSIZE is not set +CONFIG_SOUND=m +CONFIG_VIDC=y +CONFIG_AUDIO=y +DSP_BUFFSIZE=65536 # # Kernel hacking diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 90e71345a..f263b2a7f 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -19,15 +19,24 @@ else O_OBJS += armksyms.o endif +ifdef CONFIG_PCI + O_OBJS += bios32.o +endif + ifdef CONFIG_ARCH_ACORN O_OBJS += setup.o ecard.o iic.o dma.o ifdef CONFIG_ARCH_ARC O_OBJS += oldlatches.o endif + O_OBJS += dma-$(MACHINE).o endif ifeq ($(MACHINE),ebsa110) - O_OBJS += setup-ebsa110.o dma.o + O_OBJS += setup-ebsa110.o dma.o dma-dummy.o +endif + +ifeq ($(MACHINE),ebsa285) + O_OBJS += dma.o dma-dummy.o leds-ebsa285.o setup-ebsa110.o endif ifeq ($(MACHINE),nexuspci) @@ -37,9 +46,12 @@ endif $(HEAD_OBJ): $(HEAD_OBJ:.o=.S) $(CC) -D__ASSEMBLY__ -traditional -c $(HEAD_OBJ:.o=.S) -o $@ +$(ENTRY_OBJ): $(ENTRY_OBJ:.o=.S) + $(CC) $(CFLAGS) -D__ASSEMBLY__ -c $(ENTRY_OBJ:.o=.S) -o $@ + include $(TOPDIR)/Rules.make -$(ENTRY_OBJ:.o=.S): ../lib/constants.h +$(ENTRY_OBJ): ../lib/constants.h .PHONY: ../lib/constants.h diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index 20c62e2e6..a5b49bf2a 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -63,7 +63,6 @@ extern void fp_send_sig(int); EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(udelay); -EXPORT_SYMBOL(dma_str); EXPORT_SYMBOL(xchg_str); /* expansion card support */ @@ -83,16 +82,33 @@ EXPORT_SYMBOL(outsw); EXPORT_SYMBOL(inswb); EXPORT_SYMBOL(insw); -#ifdef CONFIG_ARCH_RPC -EXPORT_SYMBOL(drambank); +/* address translation */ +#ifndef __virt_to_phys__is_a_macro +EXPORT_SYMBOL(__virt_to_phys); +#endif +#ifndef __phys_to_virt__is_a_macro +EXPORT_SYMBOL(__phys_to_virt); +#endif +#ifndef __virt_to_bus__is_a_macro +EXPORT_SYMBOL(__virt_to_bus); #endif +#ifndef __bus_to_virt__is_a_macro +EXPORT_SYMBOL(__bus_to_virt); +#endif + +EXPORT_SYMBOL(quicklists); +EXPORT_SYMBOL(__bad_pmd); +EXPORT_SYMBOL(__bad_pmd_kernel); /* dma */ +EXPORT_SYMBOL(dma_str); EXPORT_SYMBOL(enable_dma); -EXPORT_SYMBOL(set_dma_mode); +EXPORT_SYMBOL(disable_dma); EXPORT_SYMBOL(set_dma_addr); EXPORT_SYMBOL(set_dma_count); +EXPORT_SYMBOL(set_dma_mode); EXPORT_SYMBOL(get_dma_residue); +EXPORT_SYMBOL(set_dma_sg); /* * floating point math emulator support. @@ -177,7 +193,3 @@ EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); EXPORT_SYMBOL(find_first_zero_bit); EXPORT_SYMBOL(find_next_zero_bit); - -#if CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c new file mode 100644 index 000000000..4400dda42 --- /dev/null +++ b/arch/arm/kernel/bios32.c @@ -0,0 +1,144 @@ +/* + * arch/arm/kernel/bios32.c: PCI functions for ARM + * + * Copyright (C) 1998 Russell King + */ +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> + +int pcibios_present(void) +{ + return 1; +} + +static unsigned long pcibios_base_address(unsigned char dev_fn) +{ + int slot = PCI_SLOT(dev_fn); + + if (slot < 4) + return 0xf8000000 + (1 << (19 - slot)); + else + return 0; +} + +int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned char *val) +{ + unsigned long addr = pcibios_base_address(dev_fn); + unsigned char v; + + if (addr) { + __asm__("ldr%?b %0, [%1, %2]" + : "=r" (v) + : "r" (addr), "r" (where)); + *val = v; + } else + *val = 0xff; + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned short *val) +{ + unsigned long addr = pcibios_base_address(dev_fn); + unsigned short v; + + if (addr) { + __asm__("ldrh%? %0, [%1, %2]" + : "=r" (v) + : "r" (addr), "r" (where)); + *val = v; + } else + *val = 0xffff; + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned int *val) +{ + unsigned long addr = pcibios_base_address(dev_fn); + unsigned int v; + + if (addr) { + __asm__("ldr%? %0, [%1, %2]" + : "=r" (v) + : "r" (addr), "r" (where)); + *val = v; + } else + *val = 0xffffffff; + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned char val) +{ + unsigned long addr = pcibios_base_address(dev_fn); + + if (addr) + __asm__("str%?b %0, [%1, %2]" + : : "r" (val), "r" (addr), "r" (where)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned short val) +{ + unsigned long addr = pcibios_base_address(dev_fn); + + if (addr) + __asm__("strh%? %0, [%1, %2]" + : : "r" (val), "r" (addr), "r" (where)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned int val) +{ + unsigned long addr = pcibios_base_address(dev_fn); + + if (addr) + __asm__("str%? %0, [%1, %2]" + : : "r" (val), "r" (addr), "r" (where)); + return PCIBIOS_SUCCESSFUL; +} + +static int irq[] = { 18, 8, 9, 11 }; + +__initfunc(void pcibios_fixup(void)) +{ + struct pci_dev *dev; + unsigned char pin; + + for (dev = pci_devices; dev; dev = dev->next) { + pcibios_read_config_byte(dev->bus->number, + dev->devfn, + PCI_INTERRUPT_PIN, + &pin); + + dev->irq = irq[(PCI_SLOT(dev->devfn) + pin) & 3]; + + pcibios_write_config_byte(dev->bus->number, + dev->devfn, + PCI_INTERRUPT_LINE, + dev->irq); + + printk("PCI: %02x:%02x [%04x/%04x] pin %d irq %d\n", + dev->bus->number, dev->devfn, + dev->vendor, dev->device, + pin, dev->irq); + } +} + +__initfunc(void pcibios_init(void)) +{ + int rev; + + rev = *(unsigned char *)0xfe000008; + printk("DEC21285 PCI revision %02X\n", rev); +} + +__initfunc(char *pcibios_setup(char *str)) +{ + return str; +} diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S index 0d02eb85a..8a248c728 100644 --- a/arch/arm/kernel/calls.S +++ b/arch/arm/kernel/calls.S @@ -5,7 +5,7 @@ */ #ifndef NR_SYSCALLS #define NR_syscalls 256 -#define NR_SYSCALLS 182 +#define NR_SYSCALLS 184 #else /* 0 */ .long SYMBOL_NAME(sys_setup) @@ -190,5 +190,7 @@ .long SYMBOL_NAME(sys_rt_sigsuspend_wrapper) /* 180 */ .long SYMBOL_NAME(sys_pread) .long SYMBOL_NAME(sys_pwrite) - .space (NR_syscalls - 182) * 4 + .long SYMBOL_NAME(sys_xstat) + .long SYMBOL_NAME(sys_xmknod) + .space (NR_syscalls - 184) * 4 #endif diff --git a/arch/arm/kernel/dma-a5k.c b/arch/arm/kernel/dma-a5k.c new file mode 100644 index 000000000..f722809fa --- /dev/null +++ b/arch/arm/kernel/dma-a5k.c @@ -0,0 +1,79 @@ +/* + * arch/arm/kernel/dma-a5k.c + * + * Copyright (C) 1998 Russell King + * + * DMA functions specific to A5000 architecture + */ +#include <linux/sched.h> +#include <linux/init.h> + +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/hardware.h> + +#include "dma.h" + +int arch_request_dma(dmach_t channel, dma_t *dma) +{ + if (channel == DMA_VIRTUAL_FLOPPY0) + return 0; + else + return -EINVAL; +} + +void arch_free_dma(dmach_t channel, dma_t *dma) +{ + if (channel != DMA_VIRTUAL_FLOPPY0) + printk ("arch_free_dma: invalid channel %d\n", channel); +} + +int arch_get_dma_residue(dmach_t channel, dma_t *dma) +{ + if (channel != DMA_VIRTUAL_FLOPPY0) + printk ("arch_dma_count: invalid channel %d\n", dmanr); + else { + extern int floppy_fiqresidual(void); + return floppy_fiqresidual(); + } + return 0; +} + +void arch_enable_dma(dmach_t channel, dma_t *dma) +{ + if (channel != DMA_VIRTUAL_FLOPPY0) + printk ("arch_enable_dma: invalid channel %d\n", channel); + else { + void *fiqhandler_start; + unsigned int fiqhandler_length; + extern void floppy_fiqsetup (unsigned long len, unsigned long addr, + unsigned long port); + + if (dma->dma_mode == DMA_MODE_READ) { + extern unsigned char floppy_fiqin_start, floppy_fiqin_end; + fiqhandler_start = &floppy_fiqin_start; + fiqhandler_length = &floppy_fiqin_end - &floppy_fiqin_start; + } else { + extern unsigned char floppy_fiqout_start, floppy_fiqout_end; + fiqhandler_start = &floppy_fiqout_start; + fiqhandler_length = &floppy_fiqout_end - &floppy_fiqout_start; + } + memcpy ((void *)0x1c, fiqhandler_start, fiqhandler_length); + flush_page_to_ram(0); + floppy_fiqsetup (dma->buf.length, __bus_to_virt(dma->buf.address), (int)PCIO_FLOPPYDMABASE); + enable_irq (dma->dma_irq); + } +} + +void arch_disable_dma(dmach_t channel, dma_t *dma) +{ + if (channel != DMA_VIRTUAL_FLOPPY0) + printk ("arch_disable_dma: invalid channel %d\n", channel); + else + disable_irq (dma->dma_irq); +} + +__initfunc(void arch_dma_init(dma_t *dma)) +{ + dma[DMA_VIRTUAL_FLOPPY0].dma_irq = 64; +} diff --git a/arch/arm/kernel/dma-arc.c b/arch/arm/kernel/dma-arc.c new file mode 100644 index 000000000..27a139ad4 --- /dev/null +++ b/arch/arm/kernel/dma-arc.c @@ -0,0 +1,115 @@ +/* + * arch/arm/kernel/dma-arc.c + * + * Copyright (C) 1998 Dave Gilbert / Russell King + * + * DMA functions specific to Archimedes architecture + */ +#include <linux/sched.h> +#include <linux/init.h> + +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/hardware.h> + +#include "dma.h" + +int arch_request_dma(dmach_t channel, dma_t *dma) +{ + if (channel == DMA_VIRTUAL_FLOPPY0 || + channel == DMA_VIRTUAL_FLOPPY1) + return 0; + else + return -EINVAL; +} + +void arch_free_dma(dmach_t channel, dma_t *dma) +{ + if (channel != DMA_VIRTUAL_FLOPPY0 && + channel != DMA_VIRTUAL_FLOPPY1) + return 0; + else + return -EINVAL; +} + +void arch_enable_dma(dmach_t channel, dma_t *dma) +{ + switch (channel) { + case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */ + switch (dma->dma_mode) { + case DMA_MODE_READ: /* read */ + { + extern unsigned char fdc1772_dma_read, fdc1772_dma_read_end; + extern void fdc1772_setupdma(unsigned int count,unsigned int addr); + unsigned long flags; +#ifdef DEBUG + printk("enable_dma fdc1772 data read\n"); +#endif + save_flags(flags); + cliIF(); + + memcpy ((void *)0x1c, (void *)&fdc1772_dma_read, + &fdc1772_dma_read_end - &fdc1772_dma_read); + fdc1772_setupdma(dma->buf.length, __bus_to_virt(dma->buf.address)); /* Sets data pointer up */ + enable_irq (64); + restore_flags(flags); + } + break; + + case DMA_MODE_WRITE: /* write */ + { + extern unsigned char fdc1772_dma_write, fdc1772_dma_write_end; + extern void fdc1772_setupdma(unsigned int count,unsigned int addr); + unsigned long flags; + +#ifdef DEBUG + printk("enable_dma fdc1772 data write\n"); +#endif + save_flags(flags); + cliIF(); + memcpy ((void *)0x1c, (void *)&fdc1772_dma_write, + &fdc1772_dma_write_end - &fdc1772_dma_write); + fdc1772_setupdma(dma->buf.length, __bus_to_virt(dma->buf.address)); /* Sets data pointer up */ + enable_irq (64); + + restore_flags(flags); + } + break; + default: + printk ("enable_dma: dma%d not initialised\n", channel); + return; + } + } + break; + + case DMA_VIRTUAL_FLOPPY1: { /* Command end FIQ - actually just sets a flag */ + /* Need to build a branch at the FIQ address */ + extern void fdc1772_comendhandler(void); + unsigned long flags; + + /*printk("enable_dma fdc1772 command end FIQ\n");*/ + save_flags(flags); + cliIF(); + + *((unsigned int *)0x1c)=0xea000000 | (((unsigned int)fdc1772_comendhandler-(0x1c+8))/4); /* B fdc1772_comendhandler */ + + restore_flags(flags); + } + break; + } +} + +void arch_disable_dma(dmach_t channel, dma_t *dma) +{ + if (channel != DMA_VIRTUAL_FLOPPY0 && + channel != DMA_VIRTUAL_FLOPPY1) + printk("arch_disable_dma: invalid channel %d\n", channel); + else + disable_irq(dma->dma_irq); +} + +__initfunc(void arch_dma_init(dma_t *dma)) +{ + dma[DMA_VIRTUAL_FLOPPY0].dma_irq = 64; + dma[DMA_VIRTUAL_FLOPPY1].dma_irq = 65; +} diff --git a/arch/arm/kernel/dma-dummy.c b/arch/arm/kernel/dma-dummy.c new file mode 100644 index 000000000..af47512dd --- /dev/null +++ b/arch/arm/kernel/dma-dummy.c @@ -0,0 +1,45 @@ +/* + * arch/arm/kernel/dma-dummy.c + * + * Copyright (C) 1998 Russell King + * + * Dummy DMA functions + */ +#include <linux/sched.h> +#include <linux/init.h> + +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/hardware.h> + +#include "dma.h" + +int arch_request_dma(dmach_t channel, dma_t *dma, const char *devname) +{ + return -EINVAL; +} + +void arch_free_dma(dmach_t channel, dma_t *dma) +{ + printk ("arch_free_dma: invalid channel %d\n", channel); +} + +void arch_enable_dma(dmach_t channel, dma_t *dma) +{ + printk ("arch_enable_dma: invalid channel %d\n", channel); +} + +void arch_disable_dma(dmach_t channel, dma_t *dma) +{ + printk ("arch_disable_dma: invalid channel %d\n", channel); +} + +int arch_get_dma_residue(dmach_t channel, dma_t *dma) +{ + printk ("arch_get_dma_residue: invalid channel %d\n", channel); + return 0; +} + +__initfunc(void arch_dma_init(dma_t *dma)) +{ +} diff --git a/arch/arm/kernel/dma-rpc.c b/arch/arm/kernel/dma-rpc.c new file mode 100644 index 000000000..eb5bc87d7 --- /dev/null +++ b/arch/arm/kernel/dma-rpc.c @@ -0,0 +1,342 @@ +/* + * arch/arm/kernel/dma-rpc.c + * + * Copyright (C) 1998 Russell King + * + * DMA functions specific to RiscPC architecture + */ +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/mman.h> +#include <linux/init.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/hardware.h> +#include <asm/uaccess.h> + +#include "dma.h" + +#if 0 +typedef enum { + dma_size_8 = 1, + dma_size_16 = 2, + dma_size_32 = 4, + dma_size_128 = 16 +} dma_size_t; + +typedef struct { + dma_size_t transfersize; +} dma_t; +#endif + +#define TRANSFER_SIZE 2 + +#define CURA (0) +#define ENDA ((IOMD_IO0ENDA - IOMD_IO0CURA) << 2) +#define CURB ((IOMD_IO0CURB - IOMD_IO0CURA) << 2) +#define ENDB ((IOMD_IO0ENDB - IOMD_IO0CURA) << 2) +#define CR ((IOMD_IO0CR - IOMD_IO0CURA) << 2) +#define ST ((IOMD_IO0ST - IOMD_IO0CURA) << 2) + +#define state_prog_a 0 +#define state_wait_a 1 +#define state_wait_b 2 + +static void arch_get_next_sg(dmasg_t *sg, dma_t *dma) +{ + unsigned long end, offset, flags = 0; + + if (dma->sg) { + sg->address = dma->sg->address; + offset = sg->address & ~PAGE_MASK; + + end = offset + dma->sg->length; + + if (end > PAGE_SIZE) + end = PAGE_SIZE; + + if (offset + (int) TRANSFER_SIZE > end) + flags |= DMA_END_L; + + sg->length = end - TRANSFER_SIZE; + + dma->sg->length -= end - offset; + dma->sg->address += end - offset; + + if (dma->sg->length == 0) { + if (dma->sgcount > 1) { + dma->sg++; + dma->sgcount--; + } else { + dma->sg = NULL; + flags |= DMA_END_S; + } + } + } else { + flags = DMA_END_S | DMA_END_L; + sg->address = 0; + sg->length = 0; + } + + sg->length |= flags; +} + +static inline void arch_setup_dma_a(dmasg_t *sg, dma_t *dma) +{ + outl_t(sg->address, dma->dma_base + CURA); + outl_t(sg->length, dma->dma_base + ENDA); +} + +static inline void arch_setup_dma_b(dmasg_t *sg, dma_t *dma) +{ + outl_t(sg->address, dma->dma_base + CURB); + outl_t(sg->length, dma->dma_base + ENDB); +} + +static void arch_dma_handle(int irq, void *dev_id, struct pt_regs *regs) +{ + dma_t *dma = (dma_t *)dev_id; + unsigned int status = 0, no_buffer = dma->sg == NULL; + + do { + switch (dma->state) { + case state_prog_a: + arch_get_next_sg(&dma->cur_sg, dma); + arch_setup_dma_a(&dma->cur_sg, dma); + dma->state = state_wait_a; + + case state_wait_a: + status = inb_t(dma->dma_base + ST); + switch (status & (DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB)) { + case DMA_ST_OFL|DMA_ST_INT: + arch_get_next_sg(&dma->cur_sg, dma); + arch_setup_dma_a(&dma->cur_sg, dma); + break; + + case DMA_ST_INT: + arch_get_next_sg(&dma->cur_sg, dma); + arch_setup_dma_b(&dma->cur_sg, dma); + dma->state = state_wait_b; + break; + + case DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB: + arch_setup_dma_b(&dma->cur_sg, dma); + dma->state = state_wait_b; + break; + } + break; + + case state_wait_b: + status = inb_t(dma->dma_base + ST); + switch (status & (DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB)) { + case DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB: + arch_get_next_sg(&dma->cur_sg, dma); + arch_setup_dma_b(&dma->cur_sg, dma); + break; + + case DMA_ST_INT|DMA_ST_AB: + arch_get_next_sg(&dma->cur_sg, dma); + arch_setup_dma_a(&dma->cur_sg, dma); + dma->state = state_wait_a; + break; + + case DMA_ST_OFL|DMA_ST_INT: + arch_setup_dma_a(&dma->cur_sg, dma); + dma->state = state_wait_a; + break; + } + break; + } + } while (dma->sg && (status & DMA_ST_INT)); + + if (!no_buffer) + enable_irq(irq); +} + +int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name) +{ + unsigned long flags; + int ret; + + switch (channel) { + case DMA_0: + case DMA_1: + case DMA_2: + case DMA_3: + case DMA_S0: + case DMA_S1: + save_flags_cli(flags); + ret = request_irq(dma->dma_irq, arch_dma_handle, SA_INTERRUPT, dev_name, dma); + if (!ret) + disable_irq(dma->dma_irq); + restore_flags(flags); + break; + + case DMA_VIRTUAL_FLOPPY: + case DMA_VIRTUAL_SOUND: + ret = 0; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +void arch_free_dma(dmach_t channel, dma_t *dma) +{ + switch (channel) { + case DMA_0: + case DMA_1: + case DMA_2: + case DMA_3: + case DMA_S0: + case DMA_S1: + free_irq(dma->dma_irq, dma); + break; + + default: + break; + } +} + +int arch_get_dma_residue(dmach_t channel, dma_t *dma) +{ + int residue = 0; + + switch (channel) { + case DMA_0: /* Physical DMA channels */ + case DMA_1: + case DMA_2: + case DMA_3: + case DMA_S0: + case DMA_S1: + break; + + case DMA_VIRTUAL_FLOPPY: { + extern int floppy_fiqresidual(void); + residue = floppy_fiqresidual(); + } + break; + } + return residue; +} + +void arch_enable_dma(dmach_t channel, dma_t *dma) +{ + unsigned long dma_base = dma->dma_base; + unsigned int ctrl; + + switch (channel) { + case DMA_0: /* Physical DMA channels */ + case DMA_1: + case DMA_2: + case DMA_3: + case DMA_S0: + case DMA_S1: + ctrl = TRANSFER_SIZE | DMA_CR_E; + + if (dma->invalid) { + dma->invalid = 0; + + outb_t(DMA_CR_C, dma_base + CR); + dma->state = state_prog_a; + } + + if (dma->dma_mode == DMA_MODE_READ) + ctrl |= DMA_CR_D; + + outb_t(ctrl, dma_base + CR); + enable_irq(dma->dma_irq); + break; + + case DMA_VIRTUAL_FLOPPY: { + void *fiqhandler_start; + unsigned int fiqhandler_length; + extern void floppy_fiqsetup(unsigned long len, unsigned long addr, + unsigned long port); + + if (dma->dma_mode == DMA_MODE_READ) { + extern unsigned char floppy_fiqin_start, floppy_fiqin_end; + fiqhandler_start = &floppy_fiqin_start; + fiqhandler_length = &floppy_fiqin_end - &floppy_fiqin_start; + } else { + extern unsigned char floppy_fiqout_start, floppy_fiqout_end; + fiqhandler_start = &floppy_fiqout_start; + fiqhandler_length = &floppy_fiqout_end - &floppy_fiqout_start; + } + /* Allow access to page 0 via domains */ + __asm__ __volatile__("mcr p15, 0, %0, c3, c0" : + : "r" (DOMAIN_USER_MANAGER | + DOMAIN_KERNEL_CLIENT | + DOMAIN_IO_CLIENT)); + memcpy((void *)0x1c, fiqhandler_start, fiqhandler_length); + /* set domain register to normal */ + set_fs(get_fs()); + flush_page_to_ram(0); + floppy_fiqsetup(dma->buf.length, __bus_to_virt(dma->buf.address), (int)PCIO_FLOPPYDMABASE); + enable_irq(dma->dma_irq); + } + break; + + default: + break; + } +} + +void arch_disable_dma(dmach_t channel, dma_t *dma) +{ + unsigned long dma_base = dma->dma_base; + unsigned int ctrl; + + switch (channel) { + case DMA_0: /* Physical DMA channels */ + case DMA_1: + case DMA_2: + case DMA_3: + case DMA_S0: + case DMA_S1: + disable_irq(dma->dma_irq); + ctrl = inb_t(dma_base + CR); + outb_t(ctrl & ~DMA_CR_E, dma_base + CR); + break; + + case DMA_VIRTUAL_FLOPPY: + disable_irq(dma->dma_irq); + break; + } +} + +__initfunc(void arch_dma_init(dma_t *dma)) +{ + outb(0, IOMD_IO0CR); + outb(0, IOMD_IO1CR); + outb(0, IOMD_IO2CR); + outb(0, IOMD_IO3CR); + +// outb(0xf0, IOMD_DMATCR); + + dma[0].dma_base = ioaddr(IOMD_IO0CURA); + dma[0].dma_irq = IRQ_DMA0; + dma[1].dma_base = ioaddr(IOMD_IO1CURA); + dma[1].dma_irq = IRQ_DMA1; + dma[2].dma_base = ioaddr(IOMD_IO2CURA); + dma[2].dma_irq = IRQ_DMA2; + dma[3].dma_base = ioaddr(IOMD_IO3CURA); + dma[3].dma_irq = IRQ_DMA3; + dma[4].dma_base = ioaddr(IOMD_SD0CURA); + dma[4].dma_irq = IRQ_DMAS0; + dma[5].dma_base = ioaddr(IOMD_SD1CURA); + dma[5].dma_irq = IRQ_DMAS1; + dma[6].dma_irq = 64; + + /* Setup DMA channels 2,3 to be for podules + * and channels 0,1 for internal devices + */ + outb(DMA_EXT_IO3|DMA_EXT_IO2, IOMD_DMAEXT); +} diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c index 3c165c41d..6058a2863 100644 --- a/arch/arm/kernel/dma.c +++ b/arch/arm/kernel/dma.c @@ -1,199 +1,189 @@ /* * linux/arch/arm/kernel/dma.c * - * Copyright (C) 1995, 1996 Russell King + * Copyright (C) 1995-1998 Russell King + * + * Front-end to the DMA handling. You must provide the following + * architecture-specific routines: + * + * int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_id); + * void arch_free_dma(dmach_t channel, dma_t *dma); + * void arch_enable_dma(dmach_t channel, dma_t *dma); + * void arch_disable_dma(dmach_t channel, dma_t *dma); + * int arch_get_dma_residue(dmach_t channel, dma_t *dma); + * + * Moved DMA resource allocation here... */ - -#include <linux/config.h> #include <linux/sched.h> #include <linux/malloc.h> #include <linux/mman.h> +#include <linux/init.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/irq.h> #include <asm/hardware.h> #include <asm/io.h> -#define KERNEL_ARCH_DMA #include <asm/dma.h> -static unsigned long dma_address[8]; -static unsigned long dma_count[8]; -static char dma_direction[8] = { -1, -1, -1, -1, -1, -1, -1}; +#include "dma.h" -#if defined(CONFIG_ARCH_A5K) || defined(CONFIG_ARCH_RPC) -#define DMA_PCIO -#endif -#if defined(CONFIG_ARCH_ARC) && defined(CONFIG_BLK_DEV_FD) -#define DMA_OLD -#endif +static dma_t dma_chan[MAX_DMA_CHANNELS]; -void enable_dma (unsigned int dmanr) +/* Get dma list + * for /proc/dma + */ +int get_dma_list(char *buf) { - switch (dmanr) { -#ifdef DMA_PCIO - case 2: { - void *fiqhandler_start; - unsigned int fiqhandler_length; - extern void floppy_fiqsetup (unsigned long len, unsigned long addr, - unsigned long port); - switch (dma_direction[dmanr]) { - case 1: { - extern unsigned char floppy_fiqin_start, floppy_fiqin_end; - fiqhandler_start = &floppy_fiqin_start; - fiqhandler_length = &floppy_fiqin_end - &floppy_fiqin_start; - break; - } - case 0: { - extern unsigned char floppy_fiqout_start, floppy_fiqout_end; - fiqhandler_start = &floppy_fiqout_start; - fiqhandler_length = &floppy_fiqout_end - &floppy_fiqout_start; - break; - } - default: - printk ("enable_dma: dma%d not initialised\n", dmanr); - return; - } - memcpy ((void *)0x1c, fiqhandler_start, fiqhandler_length); - flush_page_to_ram(0); - floppy_fiqsetup (dma_count[dmanr], dma_address[dmanr], (int)PCIO_FLOPPYDMABASE); - enable_irq (64); - return; - } -#endif -#ifdef DMA_OLD - case 0: { /* Data DMA */ - switch (dma_direction[dmanr]) { - case 1: /* read */ - { - extern unsigned char fdc1772_dma_read, fdc1772_dma_read_end; - extern void fdc1772_setupdma(unsigned int count,unsigned int addr); - unsigned long flags; -#ifdef DEBUG - printk("enable_dma fdc1772 data read\n"); -#endif - save_flags(flags); - cliIF(); - - memcpy ((void *)0x1c, (void *)&fdc1772_dma_read, - &fdc1772_dma_read_end - &fdc1772_dma_read); - fdc1772_setupdma(dma_count[dmanr],dma_address[dmanr]); /* Sets data pointer up */ - enable_irq (64); - restore_flags(flags); - } - break; - - case 0: /* write */ - { - extern unsigned char fdc1772_dma_write, fdc1772_dma_write_end; - extern void fdc1772_setupdma(unsigned int count,unsigned int addr); - unsigned long flags; - -#ifdef DEBUG - printk("enable_dma fdc1772 data write\n"); -#endif - save_flags(flags); - cliIF(); - memcpy ((void *)0x1c, (void *)&fdc1772_dma_write, - &fdc1772_dma_write_end - &fdc1772_dma_write); - fdc1772_setupdma(dma_count[dmanr],dma_address[dmanr]); /* Sets data pointer up */ - enable_irq (64); - - restore_flags(flags); - } - break; - default: - printk ("enable_dma: dma%d not initialised\n", dmanr); - return; - } - } - break; + int i, len = 0; - case 1: { /* Command end FIQ - actually just sets a flag */ - /* Need to build a branch at the FIQ address */ - extern void fdc1772_comendhandler(void); - unsigned long flags; + for (i = 0; i < MAX_DMA_CHANNELS; i++) { + if (dma_chan[i].lock) + len += sprintf(buf + len, "%2d: %s\n", + i, dma_chan[i].device_id); + } + return len; +} - /*printk("enable_dma fdc1772 command end FIQ\n");*/ - save_flags(flags); - cliIF(); - - *((unsigned int *)0x1c)=0xea000000 | (((unsigned int)fdc1772_comendhandler-(0x1c+8))/4); /* B fdc1772_comendhandler */ +/* Request DMA channel + * + * On certain platforms, we have to allocate an interrupt as well... + */ +int request_dma(dmach_t channel, const char *device_id) +{ + if (channel < MAX_DMA_CHANNELS) { + int ret; + + if (xchg(&dma_chan[channel].lock, 1) != 0) + return -EBUSY; + + ret = arch_request_dma(channel, &dma_chan[channel], device_id); + if (!ret) { + dma_chan[channel].device_id = device_id; + dma_chan[channel].active = 0; + dma_chan[channel].invalid = 1; + } else + xchg(&dma_chan[channel].lock, 0); + + return ret; + } else { + printk (KERN_ERR "Trying to allocate DMA%d\n", channel); + return -EINVAL; + } +} + +/* Free DMA channel + * + * On certain platforms, we have to free interrupt as well... + */ +void free_dma(dmach_t channel) +{ + if (channel >= MAX_DMA_CHANNELS) { + printk (KERN_ERR "Trying to free DMA%d\n", channel); + return; + } - restore_flags(flags); + if (xchg(&dma_chan[channel].lock, 0) == 0) { + if (dma_chan[channel].active) { + printk (KERN_ERR "Freeing active DMA%d\n", channel); + arch_disable_dma(channel, &dma_chan[channel]); + dma_chan[channel].active = 0; } - break; -#endif - case DMA_0: - case DMA_1: - case DMA_2: - case DMA_3: - case DMA_S0: - case DMA_S1: - arch_enable_dma (dmanr - DMA_0); - break; - - default: - printk ("enable_dma: dma %d not supported\n", dmanr); + + printk (KERN_ERR "Trying to free free DMA%d\n", channel); + return; } + arch_free_dma(channel, &dma_chan[channel]); +} + +/* Set DMA Scatter-Gather list + */ +void set_dma_sg (dmach_t channel, dmasg_t *sg, int nr_sg) +{ + dma_chan[channel].sg = sg; + dma_chan[channel].sgcount = nr_sg; + dma_chan[channel].invalid = 1; } -void set_dma_mode (unsigned int dmanr, char mode) +/* Set DMA address + * + * Copy address to the structure, and set the invalid bit + */ +void set_dma_addr (dmach_t channel, unsigned long physaddr) { - if (dmanr < 8) { - if (mode == DMA_MODE_READ) - dma_direction[dmanr] = 1; - else if (mode == DMA_MODE_WRITE) - dma_direction[dmanr] = 0; - else - printk ("set_dma_mode: dma%d: invalid mode %02X not supported\n", - dmanr, mode); - } else if (dmanr < MAX_DMA_CHANNELS) - arch_set_dma_mode (dmanr - DMA_0, mode); - else - printk ("set_dma_mode: dma %d not supported\n", dmanr); + if (dma_chan[channel].active) + printk(KERN_ERR "set_dma_addr: altering DMA%d" + " address while DMA active\n", + channel); + + dma_chan[channel].sg = &dma_chan[channel].buf; + dma_chan[channel].sgcount = 1; + dma_chan[channel].buf.address = physaddr; + dma_chan[channel].invalid = 1; } -void set_dma_addr (unsigned int dmanr, unsigned int addr) +/* Set DMA byte count + * + * Copy address to the structure, and set the invalid bit + */ +void set_dma_count (dmach_t channel, unsigned long count) { - if (dmanr < 8) - dma_address[dmanr] = (unsigned long)addr; - else if (dmanr < MAX_DMA_CHANNELS) - arch_set_dma_addr (dmanr - DMA_0, addr); - else - printk ("set_dma_addr: dma %d not supported\n", dmanr); + if (dma_chan[channel].active) + printk(KERN_ERR "set_dma_count: altering DMA%d" + " count while DMA active\n", + channel); + + dma_chan[channel].sg = &dma_chan[channel].buf; + dma_chan[channel].sgcount = 1; + dma_chan[channel].buf.length = count; + dma_chan[channel].invalid = 1; } -void set_dma_count (unsigned int dmanr, unsigned int count) +/* Set DMA direction mode + */ +void set_dma_mode (dmach_t channel, dmamode_t mode) { - if (dmanr < 8) - dma_count[dmanr] = (unsigned long)count; - else if (dmanr < MAX_DMA_CHANNELS) - arch_set_dma_count (dmanr - DMA_0, count); - else - printk ("set_dma_count: dma %d not supported\n", dmanr); + if (dma_chan[channel].active) + printk(KERN_ERR "set_dma_mode: altering DMA%d" + " mode while DMA active\n", + channel); + + dma_chan[channel].dma_mode = mode; + dma_chan[channel].invalid = 1; } -int get_dma_residue (unsigned int dmanr) +/* Enable DMA channel + */ +void enable_dma (dmach_t channel) { - if (dmanr < 8) { - switch (dmanr) { -#if defined(CONFIG_ARCH_A5K) || defined(CONFIG_ARCH_RPC) - case 2: { - extern int floppy_fiqresidual (void); - return floppy_fiqresidual (); - } -#endif -#if defined(CONFIG_ARCH_ARC) && defined(CONFIG_BLK_DEV_FD) - case 0: { - extern unsigned int fdc1772_bytestogo; - return fdc1772_bytestogo; + if (dma_chan[channel].lock) { + if (dma_chan[channel].active == 0) { + dma_chan[channel].active = 1; + arch_enable_dma(channel, &dma_chan[channel]); } -#endif - default: - return -1; + } else + printk (KERN_ERR "Trying to enable free DMA%d\n", channel); +} + +/* Disable DMA channel + */ +void disable_dma (dmach_t channel) +{ + if (dma_chan[channel].lock) { + if (dma_chan[channel].active == 1) { + dma_chan[channel].active = 0; + arch_disable_dma(channel, &dma_chan[channel]); } - } else if (dmanr < MAX_DMA_CHANNELS) - return arch_dma_count (dmanr - DMA_0); - return -1; + } else + printk (KERN_ERR "Trying to disable free DMA%d\n", channel); +} + +int get_dma_residue(dmach_t channel) +{ + return arch_get_dma_residue(channel, &dma_chan[channel]); +} + +__initfunc(void init_dma(void)) +{ + arch_dma_init(dma_chan); } diff --git a/arch/arm/kernel/dma.h b/arch/arm/kernel/dma.h new file mode 100644 index 000000000..e4c72c6af --- /dev/null +++ b/arch/arm/kernel/dma.h @@ -0,0 +1,70 @@ +/* + * arch/arm/kernel/dma.h + * + * Copyright (C) 1998 Russell King + * + * This header file describes the interface between the generic DMA handler + * (dma.c) and the architecture-specific DMA backends (dma-*.c) + */ + +typedef struct { + dmasg_t buf; /* single DMA */ + int sgcount; /* number of DMA SG */ + dmasg_t *sg; /* DMA Scatter-Gather List */ + + unsigned int active:1; /* Transfer active */ + unsigned int invalid:1; /* Address/Count changed */ + dmamode_t dma_mode; /* DMA mode */ + + unsigned int lock; /* Device is allocated */ + const char *device_id; /* Device name */ + + unsigned int dma_base; /* Controller base address */ + int dma_irq; /* Controller IRQ */ + int state; /* Controller state */ + dmasg_t cur_sg; /* Current controller buffer */ +} dma_t; + +/* Prototype: int arch_request_dma(channel, dma, dev_id) + * Purpose : Perform architecture specific claiming of a DMA channel + * Params : channel - DMA channel number + * : dma - DMA structure (above) for channel + * : dev_id - device ID string passed with request + * Returns : 0 on success, E????? number on error + */ +int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_id); + +/* Prototype: int arch_free_dma(channel, dma) + * Purpose : Perform architecture specific freeing of a DMA channel + * Params : channel - DMA channel number + * : dma - DMA structure for channel + */ +void arch_free_dma(dmach_t channel, dma_t *dma); + +/* Prototype: void arch_enable_dma(channel, dma) + * Purpose : Enable a claimed DMA channel + * Params : channel - DMA channel number + * : dma - DMA structure for channel + */ +void arch_enable_dma(dmach_t channel, dma_t *dma); + +/* Prototype: void arch_disable_dma(channel, dma) + * Purpose : Disable a claimed DMA channel + * Params : channel - DMA channel number + * : dma - DMA structure for channel + */ +void arch_disable_dma(dmach_t channel, dma_t *dma); + +/* Prototype: int arch_get_dma_residue(channel, dma) + * Purpose : Return number of bytes left to DMA + * Params : channel - DMA channel number + * : dma - DMA structure for channel + * Returns : Number of bytes left to DMA + */ +int arch_get_dma_residue(dmach_t channel, dma_t *dma); + +/* Prototype: void arch_dma_init(dma) + * Purpose : Initialise architecture specific DMA + * Params : dma - pointer to array of DMA structures + */ +void arch_dma_init(dma_t *dma); diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c index cc18252b3..f03147e66 100644 --- a/arch/arm/kernel/ecard.c +++ b/arch/arm/kernel/ecard.c @@ -13,6 +13,7 @@ * now register their own routine to control interrupts (recommended). * 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled on reset from * Linux. (Caused cards not to respond under RiscOS without hard reset). + * 15-Feb-1998 RMK Added DMA support */ #define ECARD_C @@ -24,13 +25,14 @@ #include <linux/interrupt.h> #include <linux/mm.h> #include <linux/malloc.h> +#include <linux/init.h> -#include <asm/irq-no.h> -#include <asm/ecard.h> -#include <asm/irq.h> #include <asm/io.h> #include <asm/hardware.h> #include <asm/arch/irq.h> +#include <asm/ecard.h> +#include <asm/irq.h> +#include <asm/dma.h> #ifdef CONFIG_ARCH_ARC #include <asm/arch/oldlatches.h> @@ -59,9 +61,11 @@ static const struct expcard_blacklist { BLACKLIST_LOADER(MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, atomwide_serial_loader), BLACKLIST_LOADER(MANU_OAK, PROD_OAK_SCSI, oak_scsi_loader), +/* Supported cards with broken loader */ + { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI, noloader, "AlSystems PowerTec SCSI (loader blacklisted)" }, + /* Unsupported cards with no loader */ -BLACKLIST_NOLOADER(MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI), -BLACKLIST_NOLOADER(MANU_MCS, PROD_MCS_CONNECT32) + BLACKLIST_NOLOADER(MANU_MCS, PROD_MCS_CONNECT32) }; extern int setup_arm_irq(int, struct irqaction *); @@ -75,7 +79,6 @@ static ecard_t expcard[MAX_ECARDS]; static signed char irqno_to_expcard[16]; static unsigned int ecard_numcards, ecard_numirqcards; static unsigned int have_expmask; -static unsigned long kmem; static void ecard_def_irq_enable (ecard_t *ec, int irqnr) { @@ -178,20 +181,6 @@ void ecard_disablefiq (unsigned int fiqnr) } } -static void *ecard_malloc(int len) -{ - int r; - - len = (len + 3) & ~3; - - if (kmem) { - r = kmem; - kmem += len; - return (void *)r; - } else - return kmalloc(len, GFP_KERNEL); -} - static void ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs) { const int num_cards = ecard_numirqcards; @@ -301,13 +290,13 @@ static void ecard_readbytes (void *addr, ecard_t *ec, int off, int len, int usel lowaddress = 0; } while (lowaddress <= laddr) { - byte = inb (ec->podaddr + haddr); + byte = inb(ec->podaddr + haddr); lowaddress += 1; } while (len--) { *a++ = byte; if (len) { - byte = inb (ec->podaddr + haddr); + byte = inb(ec->podaddr + haddr); lowaddress += 1; } } @@ -417,7 +406,7 @@ int ecard_readchunk (struct in_chunk_dir *cd, ecard_t *ec, int id, int num) } if (c_id(&excd) == 0x80) { /* loader */ if (!ec->loader) { - ec->loader = (loader_t)ecard_malloc(c_len(&excd)); + ec->loader = (loader_t)kmalloc(c_len(&excd), GFP_KERNEL); ecard_readbytes(ec->loader, ec, (int)c_start(&excd), c_len(&excd), useld); } continue; @@ -441,20 +430,39 @@ int ecard_readchunk (struct in_chunk_dir *cd, ecard_t *ec, int id, int num) return 1; } -unsigned int ecard_address (ecard_t *ec, card_type_t memc, card_speed_t speed) +unsigned int ecard_address (ecard_t *ec, card_type_t type, card_speed_t speed) { switch (ec->slot_no) { case 0: case 1: case 2: case 3: - return (memc ? MEMCECIO_BASE : IOCECIO_BASE + (speed << 17)) + (ec->slot_no << 12); + switch (type) { + case ECARD_MEMC: + return MEMCECIO_BASE + (ec->slot_no << 12); + + case ECARD_IOC: + return IOCECIO_BASE + (speed << 17) + (ec->slot_no << 12); + + default: + return 0; + } + #ifdef IOCEC4IO_BASE case 4: case 5: case 6: case 7: - return (memc ? 0 : IOCEC4IO_BASE + (speed << 17)) + ((ec->slot_no - 4) << 12); + switch (type) { + case ECARD_MEMC: + return 0; + + case ECARD_IOC: + return IOCEC4IO_BASE + (speed << 17) + ((ec->slot_no - 4) << 12); + + default: + return 0; + } #endif #ifdef MEMCEC8IO_BASE case 8: @@ -470,7 +478,7 @@ unsigned int ecard_address (ecard_t *ec, card_type_t memc, card_speed_t speed) * If bit 1 of the first byte of the card is set, * then the card does not exist. */ -static int ecard_probe (int card, int freeslot) +__initfunc(static int ecard_probe (int card, int freeslot)) { ecard_t *ec = expcard + freeslot; struct ex_ecld excld; @@ -480,7 +488,7 @@ static int ecard_probe (int card, int freeslot) irqno_to_expcard[card] = -1; ec->slot_no = card; - if ((ec->podaddr = ecard_address (ec, 0, ECARD_SYNC)) == 0) + if ((ec->podaddr = ecard_address (ec, ECARD_IOC, ECARD_SYNC)) == 0) return 0; excld.r_ecld = 2; @@ -490,8 +498,9 @@ static int ecard_probe (int card, int freeslot) irqno_to_expcard[card] = freeslot; - ec->irq = -1; - ec->fiq = -1; + ec->irq = NO_IRQ; + ec->fiq = NO_IRQ; + ec->dma = NO_DMA; ec->cld.ecld = e_ecld(&excld); ec->cld.manufacturer = e_manu(&excld); ec->cld.product = e_prod(&excld); @@ -514,15 +523,20 @@ static int ecard_probe (int card, int freeslot) break; } - if (card != 8) { - ec->irq = 32 + card; + ec->irq = 32 + card; #if 0 - ec->fiq = 96 + card; + /* We don't support FIQs on expansion cards at the moment */ + ec->fiq = 96 + card; #endif - } else { +#ifdef CONFIG_ARCH_RPC + if (card != 8) { + /* On RiscPC, only first two slots have DMA capability + */ + if (card < 2) + ec->dma = 2 + card; + } else ec->irq = 11; - ec->fiq = -1; - } +#endif if ((ec->cld.ecld & 0x78) == 0) { struct in_chunk_dir incd; @@ -551,11 +565,10 @@ static struct irqaction irqexpansioncard = { ecard_irq_noexpmask, SA_INTERRUPT, * Locate all hardware - interrupt management and * actual cards. */ -unsigned long ecard_init(unsigned long start_mem) +__initfunc(void ecard_init(void)) { int i, nc = 0; - kmem = (start_mem | 3) & ~3; memset (expcard, 0, sizeof (expcard)); #ifdef HAS_EXPMASK @@ -565,6 +578,7 @@ unsigned long ecard_init(unsigned long start_mem) have_expmask = -1; } #endif + printk("Installed expansion cards:"); /* @@ -578,27 +592,25 @@ unsigned long ecard_init(unsigned long start_mem) ecard_numirqcards = nc; - /* - * Now probe other cards with different interrupt lines + /* Now probe other cards with different interrupt lines */ #ifdef MEMCEC8IO_BASE if (ecard_probe (8, nc)) nc += 1; #endif + printk("\n"); ecard_numcards = nc; if (nc && setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) { printk ("Could not allocate interrupt for expansion cards\n"); - return kmem; + return; } #ifdef HAS_EXPMASK if (nc && have_expmask) EXPMASK_ENABLE = have_expmask; #endif + oldlatch_init (); - start_mem = kmem; - kmem = 0; - return start_mem; } diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index a2de41f33..f2d752cd4 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -9,7 +9,7 @@ * Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction that causes * it to save wrong values... Be aware! */ -#include <linux/config.h> /* for CONFIG_ARCH_EBSA110 /* +#include <linux/config.h> /* for CONFIG_ARCH_EBSA110 */ #include <linux/autoconf.h> #include <linux/linkage.h> @@ -21,15 +21,6 @@ .text -@ Offsets into task structure -@ --------------------------- -@ -#define STATE 0 -#define COUNTER 4 -#define PRIORITY 8 -#define FLAGS 12 -#define SIGPENDING 16 - #define PF_TRACESYS 0x20 @ Bad Abort numbers @@ -82,21 +73,24 @@ strb r12, [r12, #0x38] @ Disable FIQ register .endm - .macro get_irqnr_and_base, irqnr, base + .macro get_irqnr_and_base, irqnr, irqstat, base mov r4, #ioc_base_high @ point at IOC .if ioc_base_low orr r4, r4, #ioc_base_low .endif - ldrb \irqnr, [r4, #0x24] @ get high priority first + ldrb \irqstat, [r4, #0x24] @ get high priority first adr \base, irq_prio_h - teq \irqnr, #0 + teq \irqstat, #0 #ifdef IOMD_BASE - ldreqb \irqnr, [r4, #0x1f4] @ get dma + ldreqb \irqstat, [r4, #0x1f4] @ get dma adreq \base, irq_prio_d - teqeq \irqnr, #0 + teqeq \irqstat, #0 #endif - ldreqb \irqnr, [r4, #0x14] @ get low priority + ldreqb \irqstat, [r4, #0x14] @ get low priority adreq \base, irq_prio_l + + teq \irqstat, #0 + ldrneb \irqnr, [r5, \irqstat] @ get IRQ number .endm /* @@ -160,10 +154,13 @@ irq_prio_h: .byte 0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10 .macro disable_fiq .endm - .macro get_irqnr_and_base, irqnr, base + .macro get_irqnr_and_base, irqnr, irqstat, base mov r4, #0xf3000000 - ldrb \irqnr, [r4] @ get interrupts + ldrb \irqstat, [r4] @ get interrupts adr \base, irq_prio_ebsa110 + + teq \irqstat, #0 + ldrneb \irqnr, [r5, \irqstat] @ get IRQ number .endm .macro irq_prio_table @@ -189,6 +186,26 @@ irq_prio_ebsa110: .byte 6, 6, 6, 6, 2, 2, 2, 2, 3, 3, 6, 6, 2, 2, 2, 2 .endm +#elif defined(CONFIG_ARCH_EBSA285) + + .macro disable_fiq + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base + mov r4, #0xfe000000 + ldr \irqstat, [r4, #0x180] @ get interrupts + mov \irqnr, #0 +1001: tst \irqstat, #1 + addeq \irqnr, \irqnr, #1 + moveq \irqstat, \irqstat, lsr #1 + tsteq \irqnr, #32 + beq 1001b + teq \irqnr, #32 + .endm + + .macro irq_prio_table + .endm + #else #error Unknown architecture #endif @@ -267,6 +284,16 @@ irq_prio_ebsa110: .endm /*============================================================================= + * Address exception handler + *----------------------------------------------------------------------------- + * These aren't too critical. + * (they're not supposed to happen, and won't happen in 32-bit mode). + */ + +vector_addrexcptn: + b vector_addrexcptn + +/*============================================================================= * Undefined FIQs *----------------------------------------------------------------------------- * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC @@ -400,86 +427,6 @@ vector_data: @ b __dabt_svc @ 3 (SVC_26 / SVC_32) /*============================================================================= - * Undefined instruction handler - *----------------------------------------------------------------------------- - * Handles floating point instructions - */ -__und_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go - stmia sp, {r0 - r12} @ Save r0 - r12 - add r8, sp, #S_PC - stmdb r8, {sp, lr}^ @ Save user r0 - r12 - ldr r4, .LCund - ldmia r4, {r5 - r7} - stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0 - mov fp, #0 - - adr r1, .LC2 - ldmia r1, {r1, r4} - ldr r1, [r1] - get_current_task r2 - teq r1, r2 - blne SYMBOL_NAME(math_state_restore) - adrsvc, al, r9, SYMBOL_NAME(fpreturn) - adrsvc al, lr, SYMBOL_NAME(fpundefinstr) - ldr pc, [r4] @ Call FP module USR entry point - - .globl SYMBOL_NAME(fpundefinstr) -SYMBOL_NAME(fpundefinstr): @ Called by FP module on undefined instr - mov r0, lr - mov r1, sp - mrs r4, cpsr @ Enable interrupts - bic r4, r4, #I_BIT - msr cpsr, r4 - bl SYMBOL_NAME(do_undefinstr) - b ret_from_exception @ Normal FP exit - -__und_svc: sub sp, sp, #S_FRAME_SIZE - stmia sp, {r0 - r12} @ save r0 - r12 - mov r6, lr - ldr r7, .LCund - ldmia r7, {r7 - r9} - add r5, sp, #S_FRAME_SIZE - add r4, sp, #S_SP - stmia r4, {r5 - r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro - - adr r1, .LC2 - ldmia r1, {r1, r4} - ldr r1, [r1] - mov r2, sp, lsr #13 - mov r2, r2, lsl #13 - teq r1, r2 - blne SYMBOL_NAME(math_state_restore) - adrsvc al, r9, SYMBOL_NAME(fpreturnsvc) - adrsvc al, lr, SYMBOL_NAME(fpundefinstrsvc) - ldr pc, [r4] @ Call FP module SVC entry point - - .globl SYMBOL_NAME(fpundefinstrsvc) -SYMBOL_NAME(fpundefinstrsvc): - mov r0, r5 @ unsigned long pc - mov r1, sp @ struct pt_regs *regs - bl SYMBOL_NAME(do_undefinstr) - - .globl SYMBOL_NAME(fpreturnsvc) -SYMBOL_NAME(fpreturnsvc): - ldr lr, [sp, #S_PSR] @ Get SVC cpsr - msr spsr, lr - ldmia sp, {r0 - pc}^ @ Restore SVC registers - -.LC2: .word SYMBOL_NAME(last_task_used_math) - .word SYMBOL_NAME(fp_enter) - -__und_invalid: sub sp, sp, #S_FRAME_SIZE - stmia sp, {r0 - lr} - mov r7, r0 - ldr r4, .LCund - ldmia r4, {r5, r6} @ Get UND/IRQ/FIQ/ABT pc, cpsr - add r4, sp, #S_PC - stmia r4, {r5, r6, r7} @ Save UND/IRQ/FIQ/ABT pc, cpsr, old_r0 - mov r0, sp @ struct pt_regs *regs - mov r1, #BAD_UNDEFINSTR @ int reason - and r2, r6, #31 @ int mode - b SYMBOL_NAME(bad_mode) @ Does not ever return... -/*============================================================================= * Prefetch abort handler *----------------------------------------------------------------------------- */ @@ -531,14 +478,62 @@ t: .ascii "*** undef ***\r\n\0" #endif /*============================================================================= - * Address exception handler + * Data abort handler code *----------------------------------------------------------------------------- - * These aren't too critical. - * (they're not supposed to happen, and won't happen in 32-bit mode). */ +.LCprocfns: .word SYMBOL_NAME(processor) -vector_addrexcptn: - b vector_addrexcptn +__dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go + stmia sp, {r0 - r12} @ save r0 - r12 + add r3, sp, #S_PC + stmdb r3, {sp, lr}^ + ldr r0, .LCabt + ldmia r0, {r0 - r2} @ Get USR pc, cpsr + stmia r3, {r0 - r2} @ Save USR pc, cpsr, old_r0 + mov fp, #0 + mrs r2, cpsr @ Enable interrupts if they were + bic r2, r2, #I_BIT @ previously + msr cpsr, r2 + ldr r2, .LCprocfns + mov lr, pc + ldr pc, [r2, #8] @ call processor specific code + mov r3, sp + bl SYMBOL_NAME(do_DataAbort) + b ret_from_sys_call + +__dabt_svc: sub sp, sp, #S_FRAME_SIZE + stmia sp, {r0 - r12} @ save r0 - r12 + ldr r2, .LCabt + add r0, sp, #S_FRAME_SIZE + add r5, sp, #S_SP + mov r1, lr + ldmia r2, {r2 - r4} @ get pc, cpsr + stmia r5, {r0 - r4} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro + tst r3, #I_BIT + mrseq r0, cpsr @ Enable interrupts if they were + biceq r0, r0, #I_BIT @ previously + msreq cpsr, r0 + mov r0, r2 + ldr r2, .LCprocfns + mov lr, pc + ldr pc, [r2, #8] @ call processor specific code + mov r3, sp + bl SYMBOL_NAME(do_DataAbort) + ldr r0, [sp, #S_PSR] + msr spsr, r0 + ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr + +__dabt_invalid: sub sp, sp, #S_FRAME_SIZE + stmia sp, {r0 - lr} @ Save SVC r0 - lr [lr *should* be intact] + mov r7, r0 + ldr r4, .LCabt + ldmia r4, {r5, r6} @ Get SVC pc, cpsr + add r4, sp, #S_PC + stmia r4, {r5, r6, r7} @ Save SVC pc, cpsr, old_r0 + mov r0, sp + mov r1, #BAD_DATA + and r2, r6, #31 + b SYMBOL_NAME(bad_mode) /*============================================================================= * Interrupt (IRQ) handler @@ -551,9 +546,7 @@ __irq_usr: sub sp, sp, #S_FRAME_SIZE ldr r4, .LCirq ldmia r4, {r5 - r7} @ get saved PC, SPSR stmia r8, {r5 - r7} @ save pc, psr, old_r0 -1: get_irqnr_and_base r6, r5 - teq r6, #0 - ldrneb r0, [r5, r6] @ get IRQ number +1: get_irqnr_and_base r0, r6, r5 movne r1, sp @ @ routine called with r0 = irq number, r1 = struct pt_regs * @@ -572,9 +565,7 @@ __irq_svc: sub sp, sp, #S_FRAME_SIZE add r5, sp, #S_FRAME_SIZE add r4, sp, #S_SP stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro -1: get_irqnr_and_base r6, r5 - teq r6, #0 - ldrneb r0, [r5, r6] @ get IRQ number +1: get_irqnr_and_base r0, r6, r5 movne r1, sp @ @ routine called with r0 = irq number, r1 = struct pt_regs * @@ -598,63 +589,85 @@ __irq_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate space on stack for frame b SYMBOL_NAME(bad_mode) /*============================================================================= - * Data abort handler code + * Undefined instruction handler *----------------------------------------------------------------------------- + * Handles floating point instructions */ -.LCprocfns: .word SYMBOL_NAME(processor) - -__dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go - stmia sp, {r0 - r12} @ save r0 - r12 - add r3, sp, #S_PC - stmdb r3, {sp, lr}^ - ldr r0, .LCabt - ldmia r0, {r0 - r2} @ Get USR pc, cpsr - stmia r3, {r0 - r2} @ Save USR pc, cpsr, old_r0 +__und_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go + stmia sp, {r0 - r12} @ Save r0 - r12 + add r8, sp, #S_PC + stmdb r8, {sp, lr}^ @ Save user r0 - r12 + ldr r4, .LCund + ldmia r4, {r5 - r7} + stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0 mov fp, #0 - mrs r2, cpsr @ Enable interrupts if they were - bic r2, r2, #I_BIT @ previously - msr cpsr, r2 - ldr r2, .LCprocfns - mov lr, pc - ldr pc, [r2, #8] @ call processor specific code - mov r3, sp - bl SYMBOL_NAME(do_DataAbort) - b ret_from_sys_call -__dabt_svc: sub sp, sp, #S_FRAME_SIZE + adr r1, .LC2 + ldmia r1, {r1, r4} + ldr r1, [r1] + get_current_task r2 + teq r1, r2 + blne SYMBOL_NAME(math_state_restore) + adrsvc al, r9, SYMBOL_NAME(fpreturn) + adrsvc al, lr, SYMBOL_NAME(fpundefinstr) + ldr pc, [r4] @ Call FP module USR entry point + + .globl SYMBOL_NAME(fpundefinstr) +SYMBOL_NAME(fpundefinstr): @ Called by FP module on undefined instr + mov r0, lr + mov r1, sp + mrs r4, cpsr @ Enable interrupts + bic r4, r4, #I_BIT + msr cpsr, r4 + bl SYMBOL_NAME(do_undefinstr) + b ret_from_exception @ Normal FP exit + +__und_svc: sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ save r0 - r12 - ldr r2, .LCabt - add r0, sp, #S_FRAME_SIZE - add r5, sp, #S_SP - mov r1, lr - ldmia r2, {r2 - r4} @ get pc, cpsr - stmia r5, {r0 - r4} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro - tst r3, #I_BIT - mrseq r0, cpsr @ Enable interrupts if they were - biceq r0, r0, #I_BIT @ previously - msreq cpsr, r0 - mov r0, r2 - ldr r2, .LCprocfns - mov lr, pc - ldr pc, [r2, #8] @ call processor specific code - mov r3, sp - bl SYMBOL_NAME(do_DataAbort) - ldr r0, [sp, #S_PSR] - msr spsr, r0 - ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr + mov r6, lr + ldr r7, .LCund + ldmia r7, {r7 - r9} + add r5, sp, #S_FRAME_SIZE + add r4, sp, #S_SP + stmia r4, {r5 - r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro -__dabt_invalid: sub sp, sp, #S_FRAME_SIZE - stmia sp, {r0 - lr} @ Save SVC r0 - lr [lr *should* be intact] + adr r1, .LC2 + ldmia r1, {r1, r4} + ldr r1, [r1] + mov r2, sp, lsr #13 + mov r2, r2, lsl #13 + teq r1, r2 + blne SYMBOL_NAME(math_state_restore) + adrsvc al, r9, SYMBOL_NAME(fpreturnsvc) + adrsvc al, lr, SYMBOL_NAME(fpundefinstrsvc) + ldr pc, [r4] @ Call FP module SVC entry point + + .globl SYMBOL_NAME(fpundefinstrsvc) +SYMBOL_NAME(fpundefinstrsvc): + mov r0, r5 @ unsigned long pc + mov r1, sp @ struct pt_regs *regs + bl SYMBOL_NAME(do_undefinstr) + + .globl SYMBOL_NAME(fpreturnsvc) +SYMBOL_NAME(fpreturnsvc): + ldr lr, [sp, #S_PSR] @ Get SVC cpsr + msr spsr, lr + ldmia sp, {r0 - pc}^ @ Restore SVC registers + +.LC2: .word SYMBOL_NAME(last_task_used_math) + .word SYMBOL_NAME(fp_enter) + +__und_invalid: sub sp, sp, #S_FRAME_SIZE + stmia sp, {r0 - lr} mov r7, r0 - ldr r4, .LCabt - ldmia r4, {r5, r6} @ Get SVC pc, cpsr + ldr r4, .LCund + ldmia r4, {r5, r6} @ Get UND/IRQ/FIQ/ABT pc, cpsr add r4, sp, #S_PC - stmia r4, {r5, r6, r7} @ Save SVC pc, cpsr, old_r0 - mov r0, sp - mov r1, #BAD_DATA - and r2, r6, #31 - b SYMBOL_NAME(bad_mode) - + stmia r4, {r5, r6, r7} @ Save UND/IRQ/FIQ/ABT pc, cpsr, old_r0 + mov r0, sp @ struct pt_regs *regs + mov r1, #BAD_UNDEFINSTR @ int reason + and r2, r6, #31 @ int mode + b SYMBOL_NAME(bad_mode) @ Does not ever return... #include "entry-common.S" diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 5725e1781..98c41266f 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -1,56 +1,54 @@ -/* - *============================================================================= - * Low-level interface code - *----------------------------------------------------------------------------- - * Trap initialisation - *----------------------------------------------------------------------------- - * - * Note - FIQ code has changed. The default is a couple of words in 0x1c, 0x20 - * that call _unexp_fiq. Nowever, we now copy the FIQ routine to 0x1c (removes - * some excess cycles). - * - * What we need to put into 0-0x1c are ldrs to branch to 0xC0000000 - * (the kernel). - * 0x1c onwards is reserved for FIQ, so I think that I will allocate 0xe0 onwards for - * the actuall address to jump to. - */ -/* - * these go into 0x00 - */ -.Lbranches: swi SYS_ERROR0 - ldr pc, .Lbranches + 0xe4 - ldr pc, .Lbranches + 0xe8 - ldr pc, .Lbranches + 0xec - ldr pc, .Lbranches + 0xf0 - ldr pc, .Lbranches + 0xf4 - ldr pc, .Lbranches + 0xf8 - ldr pc, .Lbranches + 0xfc -/* - * this is put into 0xe4 and above - */ -.Ljump_addresses: - .word vector_undefinstr @ 0xe4 - .word vector_swi @ 0xe8 - .word vector_prefetch @ 0xec - .word vector_data @ 0xf0 - .word vector_addrexcptn @ 0xf4 - .word vector_IRQ @ 0xf8 - .word _unexp_fiq @ 0xfc -/* - * initialise the trap system +/*============================================================================ + * All exits to user mode from the kernel go through this code. */ -ENTRY(trap_init) - stmfd sp!, {r4 - r7, lr} - initialise_traps_extra - mov r0, #0xe4 - adr r1, .Ljump_addresses - ldmia r1, {r1 - r6} - stmia r0, {r1 - r6} - mov r0, #0 - adr r1, .Lbranches - ldmia r1, {r1 - r7} - stmia r0, {r1 - r7} - LOADREGS(fd, sp!, {r4 - r7, pc}) + + .globl ret_from_sys_call + + .globl SYMBOL_NAME(fpreturn) +SYMBOL_NAME(fpreturn): +ret_from_exception: + adr r0, 1f + ldmia r0, {r0, r1} + ldr r0, [r0] + ldr r1, [r1] + tst r0, r1 + blne SYMBOL_NAME(do_bottom_half) +ret_from_intr: ldr r0, [sp, #S_PSR] + tst r0, #3 + beq ret_with_reschedule + b ret_from_all + +ret_signal: mov r1, sp + adrsvc al, lr, ret_from_all + b SYMBOL_NAME(do_signal) + +2: bl SYMBOL_NAME(schedule) + +ret_from_sys_call: + adr r0, 1f + ldmia r0, {r0, r1} + ldr r0, [r0] + ldr r1, [r1] + tst r0, r1 + adrsvc ne, lr, ret_from_intr + bne SYMBOL_NAME(do_bottom_half) + +ret_with_reschedule: + ldr r0, 1f + 8 + ldr r0, [r0] + teq r0, #0 + bne 2b + + get_current_task r1 + ldr r1, [r1, #TSK_SIGPENDING] + teq r1, #0 + bne ret_signal + +ret_from_all: restore_user_regs + +1: .word SYMBOL_NAME(bh_mask) + .word SYMBOL_NAME(bh_active) + .word SYMBOL_NAME(need_resched) /*============================================================================= * SWI handler @@ -77,7 +75,7 @@ vector_swi: save_user_regs bcs 2f get_current_task r5 - ldr ip, [r5, #FLAGS] @ check for syscall tracing + ldr ip, [r5, #TSK_FLAGS] @ check for syscall tracing tst ip, #PF_TRACESYS bne 1f @@ -91,7 +89,7 @@ vector_swi: save_user_regs 1: ldr r7, [sp, #S_IP] @ save old IP mov r0, #0 - str r7, [sp, #S_IP] @ trace entry [IP = 0] + str r0, [sp, #S_IP] @ trace entry [IP = 0] bl SYMBOL_NAME(syscall_trace) str r7, [sp, #S_IP] ldmia sp, {r0 - r3} @ have to reload r0 - r3 @@ -193,57 +191,59 @@ sys_rt_sigreturn_wrapper: add r0, sp, #4 b SYMBOL_NAME(sys_rt_sigreturn) -/*============================================================================ - * All exits to user mode from the kernel go through this code. +/* + *============================================================================= + * Low-level interface code + *----------------------------------------------------------------------------- + * Trap initialisation + *----------------------------------------------------------------------------- + * + * Note - FIQ code has changed. The default is a couple of words in 0x1c, 0x20 + * that call _unexp_fiq. Nowever, we now copy the FIQ routine to 0x1c (removes + * some excess cycles). + * + * What we need to put into 0-0x1c are ldrs to branch to 0xC0000000 + * (the kernel). + * 0x1c onwards is reserved for FIQ, so I think that I will allocate 0xe0 onwards for + * the actuall address to jump to. */ - - .globl ret_from_sys_call - - .globl SYMBOL_NAME(fpreturn) -SYMBOL_NAME(fpreturn): -ret_from_exception: - adr r0, 1f - ldmia r0, {r0, r1} - ldr r0, [r0] - ldr r1, [r1] - tst r0, r1 - blne SYMBOL_NAME(do_bottom_half) -ret_from_intr: ldr r0, [sp, #S_PSR] - tst r0, #3 - beq ret_with_reschedule - b ret_from_all - -ret_signal: mov r1, sp - adrsvc al, lr, ret_from_all - b SYMBOL_NAME(do_signal) - -2: bl SYMBOL_NAME(schedule) - -ret_from_sys_call: - adr r0, 1f - ldmia r0, {r0, r1} - ldr r0, [r0] - ldr r1, [r1] - tst r0, r1 - adrsvc ne, lr, ret_from_intr - bne SYMBOL_NAME(do_bottom_half) - -ret_with_reschedule: - ldr r0, 1f + 8 - ldr r0, [r0] - teq r0, #0 - bne 2b - - get_current_task r1 - ldr r1, [r1, #SIGPENDING] - teq r1, #0 - bne ret_signal - -ret_from_all: restore_user_regs - -1: .word SYMBOL_NAME(bh_mask) - .word SYMBOL_NAME(bh_active) - .word SYMBOL_NAME(need_resched) +/* + * these go into 0x00 + */ +.Lbranches: swi SYS_ERROR0 + ldr pc, .Lbranches + 0xe4 + ldr pc, .Lbranches + 0xe8 + ldr pc, .Lbranches + 0xec + ldr pc, .Lbranches + 0xf0 + ldr pc, .Lbranches + 0xf4 + ldr pc, .Lbranches + 0xf8 + ldr pc, .Lbranches + 0xfc +/* + * this is put into 0xe4 and above + */ +.Ljump_addresses: + .word vector_undefinstr @ 0xe4 + .word vector_swi @ 0xe8 + .word vector_prefetch @ 0xec + .word vector_data @ 0xf0 + .word vector_addrexcptn @ 0xf4 + .word vector_IRQ @ 0xf8 + .word _unexp_fiq @ 0xfc +/* + * initialise the trap system + */ +ENTRY(trap_init) + stmfd sp!, {r4 - r7, lr} + initialise_traps_extra + mov r0, #0xe4 + adr r1, .Ljump_addresses + ldmia r1, {r1 - r6} + stmia r0, {r1 - r6} + mov r0, #0 + adr r1, .Lbranches + ldmia r1, {r1 - r7} + stmia r0, {r1 - r7} + LOADREGS(fd, sp!, {r4 - r7, pc}) /*============================================================================ * FP support diff --git a/arch/arm/kernel/head-armo.S b/arch/arm/kernel/head-armo.S index 7bd69ed5f..80d88e890 100644 --- a/arch/arm/kernel/head-armo.S +++ b/arch/arm/kernel/head-armo.S @@ -42,7 +42,7 @@ Lcontinue: str r5, [r6] b SYMBOL_NAME(start_kernel) LC1: .word SYMBOL_NAME(_stext) -LC0: .word SYMBOL_NAME(_edata) +LC0: .word SYMBOL_NAME(__bss_start) .word SYMBOL_NAME(arm_id) .word SYMBOL_NAME(_end) .word SYMBOL_NAME(init_task_union)+8192 diff --git a/arch/arm/kernel/head-armv.S b/arch/arm/kernel/head-armv.S index 0af401e43..e53b7144f 100644 --- a/arch/arm/kernel/head-armv.S +++ b/arch/arm/kernel/head-armv.S @@ -16,19 +16,21 @@ .globl __stext /* * Entry point and restart point. Entry *must* be called with r0 == 0, - * MMU off. + * MMU off. Note! These should be unique!!! Please read Documentation/ARM-README + * for more information. * - * r1 = 0 -> ebsa (Ram @ 0x00000000) - * r1 = 1 -> RPC (Ram @ 0x10000000) - * r1 = 2 -> ebsit (???) + * r1 = 0 -> ebsa110 (Ram @ 0x00000000) + * r1 = 1 -> RPC (Ram @ 0x10000000) + * r1 = 2 -> ebsit (???) * r1 = 3 -> nexuspci + * r1 = 4 -> ebsa285 (Ram @ 0x00000000) */ ENTRY(stext) ENTRY(_stext) __entry: teq r0, #0 @ check for illegal entry... bne .Lerror @ loop indefinitely - cmp r1, #4 @ Unknown machine architecture + cmp r1, #5 @ Unknown machine architecture bge .Lerror @ @ First thing to do is to get the page tables set up so that we can call the kernel @@ -57,18 +59,23 @@ __entry: adr r4, .LCMachTypes add r4, r4, r1, lsl #4 - ldmia r4, {r4, r5, r6} @ r4 = page dir in physical ram - + ldmia r4, {r4, r5, r6} +/* + * r4 = page dir in physical ram + * r5 = physical address of start of RAM + * r6 = I/O address + */ mov r0, r4 mov r1, #0 add r2, r0, #0x4000 1: str r1, [r0], #4 @ Clear page table teq r0, r2 bne 1b -@ -@ Add enough entries to allow the kernel to be called. -@ It will sort out the real mapping in paging_init -@ +/* + * Add enough entries to allow the kernel to be called. + * It will sort out the real mapping in paging_init. + * We map in 2MB of memory into 0xC0000000 - 0xC0200000 + */ add r0, r4, #0x3000 mov r1, #0x0000000c @ SECT_CACHEABLE | SECT_BUFFERABLE orr r1, r1, r8 @@ -121,7 +128,7 @@ __entry: .Lbranch: .long .Lalready_done_mmap @ Real address of routine - @ EBSA (pg dir phys, phys ram start, phys i/o) + @ EBSA110 (pg dir phys, phys ram start, phys i/o) .LCMachTypes: .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) .long 0 @ Address of RAM .long 0xe0000000 @ I/O address @@ -145,6 +152,14 @@ __entry: .long 0x10000000 .long 0 + @ EBSA285 + .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) + .long 0 @ Address of RAM + .long 0x24000000 @ I/O base address (0x42000000 -> 0xFE000000) + .long 0 + + + .LCProcTypes: @ ARM6 / 610 .long 0x41560600 .long 0xffffff00 @@ -168,7 +183,7 @@ __entry: .long 0 -.LC0: .long SYMBOL_NAME(_edata) +.LC0: .long SYMBOL_NAME(__bss_start) .long SYMBOL_NAME(arm_id) .long SYMBOL_NAME(_end) .long SYMBOL_NAME(init_task_union)+8192 @@ -231,19 +246,84 @@ __entry: b SYMBOL_NAME(start_kernel) #if 1 + +#if defined(CONFIG_ARCH_RPC) + .macro addruart,rx + mov \rx, #0xe0000000 + orr \rx, \rx, #0x00010000 + orr \rx, \rx, #0x00000fe0 + .endm + + .macro senduart,rd,rx + strb \rd, [\rx] + .endm + + .macro busyuart,rd,rx +1002: ldrb \rd, [\rx, #0x14] + and \rd, \rd, #0x60 + bne 1002b + .endm + + .macro waituart,rd,rx +1001: ldrb \rd, [\rx, #0x18] + tst \rd, #0x10 + beq 1001b + .endm + +#elif defined(CONFIG_ARCH_EBSA110) + .macro addruart,rx + mov \rx, #0xf0000000 + orr \rx, \rx, #0x00000be0 + .endm + + .macro senduart,rd,rx + strb \rd, [\rx] + .endm + + .macro busyuart,rd,rx +1002: ldrb \rd, [\rx, #0x14] + and \rd, \rd, #0x60 + bne 1002b + .endm + + .macro waituart,rd,rx +1001: ldrb \rd, [\rx, #0x18] + tst \rd, #0x10 + beq 1001b + .endm + +#elif defined(CONFIG_ARCH_EBSA285) + .macro addruart,rx + mov \rx, #0xfe000000 + .endm + + .macro senduart,rd,rx + str \rd, [\rx, #0x160] @ UARTDR + .endm + + .macro busyuart,rd,rx +1001: ldr \rd, [\rx, #0x178] @ UARTFLG + tst \rd, #1 << 3 + bne 1001b + .endm + + .macro waituart,rd,rx + .endm +#endif + /* * Useful debugging routines */ - .globl _printhex8 -_printhex8: mov r1, #8 +ENTRY(printhex8) + mov r1, #8 b printhex - .globl _printhex4 -_printhex4: mov r1, #4 +ENTRY(printhex4) + mov r1, #4 b printhex - .globl _printhex2 -_printhex2: mov r1, #2 +ENTRY(printhex2) + mov r1, #2 printhex: ldr r2, =hexbuf add r3, r2, r1 mov r1, #0 @@ -258,46 +338,23 @@ printhex: ldr r2, =hexbuf bne 1b mov r0, r2 - .globl _printascii -_printascii: -#ifdef CONFIG_ARCH_RPC - mov r3, #0xe0000000 - orr r3, r3, #0x00010000 - orr r3, r3, #0x00000fe0 -#else - mov r3, #0xf0000000 - orr r3, r3, #0x0be0 -#endif - b 3f -1: ldrb r2, [r3, #0x18] - tst r2, #0x10 - beq 1b - strb r1, [r3] -2: ldrb r2, [r3, #0x14] - and r2, r2, #0x60 - teq r2, #0x60 - bne 2b +ENTRY(printascii) + addruart r3 + b 2f +1: waituart r2, r3 + senduart r1, r3 + busyuart r2, r3 teq r1, #'\n' moveq r1, #'\r' beq 1b -3: teq r0, #0 +2: teq r0, #0 ldrneb r1, [r0], #1 teqne r1, #0 bne 1b mov pc, lr - .ltorg - - .globl _printch -_printch: -#ifdef CONFIG_ARCH_RPC - mov r3, #0xe0000000 - orr r3, r3, #0x00010000 - orr r3, r3, #0x00000fe0 -#else - mov r3, #0xf0000000 - orr r3, r3, #0x0be0 -#endif +ENTRY(printch) + addruart r3 mov r1, r0 mov r0, #0 b 1b diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index e0fb7540a..1dde0e0ba 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -33,16 +33,10 @@ #include <asm/io.h> #include <asm/system.h> #include <asm/hardware.h> -#include <asm/irq-no.h> #include <asm/arch/irq.h> +unsigned int local_bh_count[NR_CPUS]; unsigned int local_irq_count[NR_CPUS]; -#ifdef __SMP__ -atomic_t __arm_bh_counter; -#else -int __arm_bh_counter; -#endif - spinlock_t irq_controller_lock; #ifndef SMP @@ -80,13 +74,21 @@ void enable_irq(unsigned int irq_nr) struct irqaction *irq_action[NR_IRQS]; -/* - * Bitmask indicating valid interrupt numbers +#ifdef CONFIG_ARCH_ACORN +/* Bitmask indicating valid interrupt numbers + * (to be moved to include/asm-arm/arch-*) */ unsigned long validirqs[NR_IRQS / 32] = { - 0x003fffff, 0x000001ff, 0x000000ff, 0x00000000 + 0x003ffe7f, 0x000001ff, 0x000000ff, 0x00000000 }; +#define valid_irq(x) ((x) < NR_IRQS && validirqs[(x) >> 5] & (1 << ((x) & 31))) +#else + +#define valid_irq(x) ((x) < NR_IRQS) + +#endif + int get_irq_list(char *buf) { int i; @@ -98,7 +100,7 @@ int get_irq_list(char *buf) if (!action) continue; p += sprintf(p, "%3d: %10u %s", - i, kstat.interrupts[i], action->name); + i, kstat_irqs(i), action->name); for (action = action->next; action; action = action->next) { p += sprintf(p, ", %s", action->name); } @@ -126,7 +128,7 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) cpu = smp_processor_id(); irq_enter(cpu, irq); - kstat.interrupts[irq]++; + kstat.irqs[cpu][irq]++; /* Return with this interrupt masked if no action */ status = 0; @@ -143,13 +145,26 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); __cli(); + + switch (irq) { #if defined(HAS_IOMD) || defined(HAS_IOC) - if (irq != IRQ_KEYBOARDTX && irq != IRQ_EXPANSIONCARD) + case IRQ_KEYBOARDTX: + case IRQ_EXPANSIONCARD: + break; +#endif +#ifdef HAS_IOMD + case IRQ_DMA0: + case IRQ_DMA1: + case IRQ_DMA2: + case IRQ_DMA3: + break; #endif - { + + default: spin_lock(&irq_controller_lock); unmask_irq(irq); spin_unlock(&irq_controller_lock); + break; } } @@ -235,7 +250,7 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) unsigned long retval; struct irqaction *action; - if (irq >= NR_IRQS || !(validirqs[irq >> 5] & (1 << (irq & 31)))) + if (!valid_irq(irq)) return -EINVAL; if (!handler) return -EINVAL; @@ -263,7 +278,7 @@ void free_irq(unsigned int irq, void *dev_id) struct irqaction * action, **p; unsigned long flags; - if (irq >= NR_IRQS || !(validirqs[irq >> 5] & (1 << (irq & 31)))) { + if (!valid_irq(irq)) { printk(KERN_ERR "Trying to free IRQ%d\n",irq); #ifdef CONFIG_DEBUG_ERRORS __backtrace(); @@ -294,7 +309,7 @@ unsigned long probe_irq_on (void) /* first snaffle up any unassigned irqs */ for (i = 15; i > 0; i--) { - if (!irq_action[i]) { + if (!irq_action[i] && valid_irq(i)) { enable_irq(i); irqs |= 1 << i; } @@ -323,5 +338,7 @@ int probe_irq_off (unsigned long irqs) __initfunc(void init_IRQ(void)) { + extern void init_dma(void); irq_init_irq(); + init_dma(); } diff --git a/arch/arm/kernel/leds-ebsa285.c b/arch/arm/kernel/leds-ebsa285.c new file mode 100644 index 000000000..f8add1ab7 --- /dev/null +++ b/arch/arm/kernel/leds-ebsa285.c @@ -0,0 +1,44 @@ +/* + * arch/arm/kernel/leds-285.c + * + * Copyright (C) 1998 Russell King + * + * EBSA-285 LED control routines. We use the leds as follows: + * + * - Green - toggles state every 50 timer interrupts + * - Amber - On if system is not idle + * - Red - currently unused + */ +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.h> + +static char led_state = XBUS_LED_RED | XBUS_LED_GREEN; + +void leds_event(led_event_t ledevt) +{ + unsigned long flags; + + save_flags_cli(flags); + + switch(ledevt) { + case led_idle_start: + led_state |= XBUS_LED_AMBER; + break; + + case led_idle_end: + led_state &= ~XBUS_LED_AMBER; + break; + + case led_timer: + led_state ^= XBUS_LED_GREEN; + break; + + default: + break; + } + + restore_flags(flags); + + *XBUS_LEDS = led_state; +} diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 7f45e7c3c..9372539ef 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -69,8 +69,10 @@ asmlinkage int sys_idle(void) current->priority = -100; for (;;) { +#if 0 //def ARCH_IDLE_OK if (!hlt_counter && !need_resched) proc_idle (); +#endif run_task_queue(&tq_scheduler); schedule(); } @@ -142,7 +144,7 @@ void show_regs(struct pt_regs * regs) " mrc p15, 0, %1, c2, c0\n" " mrc p15, 0, %2, c3, c0\n" : "=r" (ctrl), "=r" (transbase), "=r" (dac)); - printk("Control: %04X Table: %08X DAC: %08X", + printk("Control: %04X Table: %08X DAC: %08X ", ctrl, transbase, dac); } #endif diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 5fa67df6c..f95e8de7e 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -27,18 +27,6 @@ */ #define BREAKINST 0xef9f0001 -/* change a pid into a task struct. */ -static inline struct task_struct * get_task(int pid) -{ - int i; - - for (i = 1; i < NR_TASKS; i++) { - if (task[i] != NULL && (task[i]->pid == pid)) - return task[i]; - } - return NULL; -} - /* * this routine will get a word off of the processes privileged stack. * the offset is how far from the base addr as stored in the TSS. @@ -581,7 +569,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (pid == 1) /* you may not mess with init */ goto out; ret = -ESRCH; - if (!(child = get_task(pid))) + if (!(child = find_task_by_pid(pid))) goto out; ret = -EPERM; if (request == PTRACE_ATTACH) { diff --git a/arch/arm/kernel/setup-ebsa110.c b/arch/arm/kernel/setup-ebsa110.c index 285284b7d..8cbfe7dc3 100644 --- a/arch/arm/kernel/setup-ebsa110.c +++ b/arch/arm/kernel/setup-ebsa110.c @@ -26,62 +26,95 @@ #include <linux/delay.h> #include <linux/major.h> #include <linux/utsname.h> +#include <linux/init.h> -#include <asm/segment.h> -#include <asm/system.h> #include <asm/hardware.h> #include <asm/pgtable.h> +#include <asm/segment.h> +#include <asm/setup.h> +#include <asm/system.h> #ifndef CONFIG_CMDLINE #define CONFIG_CMDLINE "root=nfs rw console=ttyS1,38400n8" #endif +#define COMMAND_LINE_SIZE 256 + #define MEM_SIZE (16*1024*1024) -#define COMMAND_LINE_SIZE 256 +static char command_line[COMMAND_LINE_SIZE] = { 0, }; + char saved_command_line[COMMAND_LINE_SIZE]; +struct processor processor; +struct screen_info screen_info; unsigned char aux_device_present; unsigned long arm_id; + extern int root_mountflags; extern int _etext, _edata, _end; +extern const struct processor sa110_processor_functions; #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 */ -static inline void setup_ramdisk (void) +static inline void setup_ramdisk(int start, int prompt, int load) { - rd_image_start = 0; - rd_prompt = 1; - rd_doload = 1; + rd_image_start = start; + rd_prompt = prompt; + rd_doload = load; } #else -#define setup_ramdisk() +#define setup_ramdisk(start,prompt,load) #endif +#ifdef PARAMS_BASE +static struct param_struct *params = (struct param_struct *)PARAMS_BASE; + +static inline char *setup_params(unsigned long *mem_end_p) +{ + ROOT_DEV = to_kdev_t(params->u1.s.rootdev); + ORIG_X = params->u1.s.video_x; + ORIG_Y = params->u1.s.video_y; + ORIG_VIDEO_COLS = params->u1.s.video_num_cols; + ORIG_VIDEO_LINES = params->u1.s.video_num_rows; + + setup_ramdisk(params->u1.s.rd_start, + (params->u1.s.flags & FLAG_RDPROMPT) == 0, + (params->u1.s.flags & FLAG_RDLOAD) == 0); + + *mem_end_p = 0xc0000000 + MEM_SIZE; + + return params->commandline; +} +#else static char default_command_line[] = CONFIG_CMDLINE; -static char command_line[COMMAND_LINE_SIZE] = { 0, }; - char saved_command_line[COMMAND_LINE_SIZE]; -struct processor processor; -extern const struct processor sa110_processor_functions; +static inline char *setup_params(unsigned long *mem_end_p) +{ + ROOT_DEV = 0x00ff; + + setup_ramdisk(0, 1, 1); + + *mem_end_p = 0xc0000000 + MEM_SIZE; + + return default_command_line; +} +#endif -void setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p) +__initfunc(void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p)) { unsigned long memory_start, memory_end; char c = ' ', *to = command_line, *from; int len = 0; memory_start = (unsigned long)&_end; - memory_end = 0xc0000000 + MEM_SIZE; - from = default_command_line; processor = sa110_processor_functions; - processor._proc_init (); + processor._proc_init(); - ROOT_DEV = 0x00ff; - setup_ramdisk(); + from = setup_params(&memory_end); init_task.mm->start_code = TASK_SIZE; init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext; diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index ac304fb3e..a3b86fef7 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -26,15 +26,16 @@ #include <linux/major.h> #include <linux/utsname.h> #include <linux/blk.h> +#include <linux/init.h> -#include <asm/segment.h> -#include <asm/system.h> #include <asm/hardware.h> +#include <asm/io.h> #include <asm/pgtable.h> -#include <asm/arch/mmu.h> #include <asm/procinfo.h> -#include <asm/io.h> +#include <asm/segment.h> #include <asm/setup.h> +#include <asm/system.h> +#include <asm/arch/mmu.h> struct drive_info_struct { char dummy[32]; } drive_info; struct screen_info screen_info; @@ -153,13 +154,17 @@ static void setup_initrd (struct param_struct *params, unsigned long memory_end) #define setup_initrd(p,m) #endif +#ifdef IOEB_BASE static inline void check_ioeb_present(void) { if (((*IOEB_BASE) & 15) == 5) armidlist[armidindex].features |= F_IOEB; } +#else +#define check_ioeb_present() +#endif -static void get_processor_type (void) +static inline void get_processor_type (void) { for (armidindex = 0; ; armidindex ++) if (!((armidlist[armidindex].id ^ arm_id) & @@ -179,11 +184,14 @@ static void get_processor_type (void) #define COMMAND_LINE_SIZE 256 +/* Can this be initdata? --pb + * command_line can be, saved_command_line can't though + */ 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) +__initfunc(void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p)) { static unsigned char smptrap; unsigned long memory_start, memory_end; diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index b1c679ec5..1f598e14d 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -23,6 +23,7 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/smp.h> +#include <linux/init.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -30,7 +31,6 @@ #include <asm/delay.h> #include <linux/timex.h> -#include <asm/irq-no.h> #include <asm/hardware.h> extern int setup_arm_irq(int, struct irqaction *); @@ -143,12 +143,12 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) update_rtc (); } -static struct irqaction irqtimer0 = { timer_interrupt, 0, 0, "timer", NULL, NULL}; +static struct irqaction irqtimer = { timer_interrupt, 0, 0, "timer", NULL, NULL}; -void time_init(void) +__initfunc(void time_init(void)) { xtime.tv_sec = setup_timer(); xtime.tv_usec = 0; - setup_arm_irq(IRQ_TIMER0, &irqtimer0); + setup_arm_irq(IRQ_TIMER, &irqtimer); } diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 7ff7436c5..222fd6b17 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -59,17 +59,27 @@ static int verify_stack_pointer (unsigned long stackptr, int size) return 0; } -static void dump_stack (unsigned long *start, unsigned long *end, int offset, int max) +/* + * Dump out the contents of some memory nicely... + */ +void dump_mem(unsigned long bottom, unsigned long top) { - unsigned long *p; + unsigned long p = bottom & ~31; int i; - for (p = start + offset, i = 0; i < max && p < end; i++, p++) { - if (i && (i & 7) == 0) - printk ("\n "); - printk ("%08lx ", *p); + for (p = bottom & ~31; p < top;) { + printk("%08lx: ", p); + + for (i = 0; i < 8; i++, p += 4) { + if (p < bottom || p >= top) + printk(" "); + else + printk("%08lx ", *(unsigned long *)p); + if (i == 3) + printk(" "); + } + printk ("\n"); } - printk ("\n"); } /* @@ -139,28 +149,26 @@ void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret) break; } - console_verbose (); - printk ("Internal error: %s: %x\n", str, err); - printk ("CPU: %d", smp_processor_id()); - show_regs (regs); - printk ("Process %s (pid: %d, stackpage=%08lx)\nStack: ", + console_verbose(); + printk("Internal error: %s: %x\n", str, err); + printk("CPU: %d", smp_processor_id()); + show_regs(regs); + printk("Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, 4096+(unsigned long)current); cstack = (unsigned long)(regs + 1); sstack = 4096+(unsigned long)current; - if (*(unsigned long *)sstack != STACK_MAGIC) - printk ("*** corrupted stack page\n "); - - if (verify_stack_pointer (cstack, 4)) - printk ("%08lx invalid kernel stack pointer\n", cstack); + printk("Stack: "); + if (verify_stack_pointer(cstack, 4)) + printk("invalid kernel stack pointer %08lx", cstack); else if(cstack > sstack + 4096) - printk("(sp overflow)\n"); + printk("(sp overflow)"); else if(cstack < sstack) - printk("(sp underflow)\n"); - else - dump_stack ((unsigned long *)sstack, (unsigned long *)sstack + 1024, - cstack - sstack, kstack_depth_to_print); + printk("(sp underflow)"); + printk("\n"); + + dump_mem(cstack, sstack + 4096); frameptr = regs->ARM_fp; if (frameptr) { @@ -172,7 +180,7 @@ void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret) } } - dump_instr (instruction_pointer(regs)); + dump_instr(instruction_pointer(regs)); died = 0; if (ret != -1) do_exit (ret); diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 10fad6b43..8ec13266d 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -5,17 +5,13 @@ # L_TARGET := lib.a -L_OBJS := backtrace.o bitops.o delay.o fp_support.o \ +L_OBJS := backtrace.o bitops.o checksum.o delay.o fp_support.o \ loaders.o memcpy.o memfastset.o system.o string.o uaccess.o ifeq ($(PROCESSOR),armo) L_OBJS += uaccess-armo.o endif -ifdef CONFIG_INET - L_OBJS += checksum.o -endif - ifdef CONFIG_ARCH_ACORN L_OBJS += ll_char_wr.o io-acorn.o ifdef CONFIG_ARCH_A5K @@ -26,30 +22,27 @@ ifdef CONFIG_ARCH_ACORN endif endif -ifdef CONFIG_ARCH_EBSA110 +ifeq ($(MACHINE),ebsa110) L_OBJS += io-ebsa110.o endif -include $(TOPDIR)/Rules.make +ifeq ($(MACHINE),ebsa285) + L_OBJS += io-ebsa285.o +endif -constants.h: getconstants - ./getconstants > constants.h +include $(TOPDIR)/Rules.make -getconstants: getconstants.c getconstants.h - $(HOSTCC) -D__KERNEL__ -o getconstants getconstants.c +constants.h: getconsdata.o extractconstants.pl + $(PERL) extractconstants.pl $(OBJDUMP) > $@ -getconstants.h: getconsdata.c +getconsdata.o: getconsdata.c $(CC) $(CFLAGS) -c getconsdata.c - $(PERL) extractinfo.perl $(OBJDUMP) > $@ %.o: %.S -ifndef $(CONFIG_BINUTILS_NEW) +ifneq ($(CONFIG_BINUTILS_NEW),y) $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.$<.s $(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.$<.s $(RM) ..tmp.$<.s else $(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $< endif - -clean: - $(RM) getconstants constants.h getconstants.h diff --git a/arch/arm/lib/extractconstants.pl b/arch/arm/lib/extractconstants.pl new file mode 100644 index 000000000..ff095a232 --- /dev/null +++ b/arch/arm/lib/extractconstants.pl @@ -0,0 +1,46 @@ +#!/usr/bin/perl + +$OBJDUMP=$ARGV[0]; + +sub swapdata { + local ($num) = @_; + + return substr($num, 6, 2).substr($num, 4, 2).substr ($num, 2, 2).substr ($num, 0, 2); +} + +open (DATA, $OBJDUMP.' --full-contents --section=.data getconsdata.o | grep \'^ 00\' |') || + die ('Cant objdump!'); +while (<DATA>) { + ($addr, $data0, $data1, $data2, $data3) = split (' '); + $dat[hex($addr)] = hex(&swapdata($data0)); + $dat[hex($addr)+4] = hex(&swapdata($data1)); + $dat[hex($addr)+8] = hex(&swapdata($data2)); + $dat[hex($addr)+12] = hex(&swapdata($data3)); +} +close (DATA); + +open (DATA, $OBJDUMP.' --syms getconsdata.o |') || die ('Cant objdump!'); +while (<DATA>) { + /elf32/ && ( $elf = 1 ); + /a.out/ && ( $aout = 1 ); + next if ($aout && ! / 07 /); + next if ($elf && ! (/^00...... g/ && /.data/)); + next if (!$aout && !$elf); + + if ($aout) { + ($addr, $flags, $sect, $a1, $a2, $a3, $name) = split (' '); + $nam[hex($addr)] = substr($name, 1); + } + if ($elf) { + chomp; + $addr = substr ($_, 0, 8); + $name = substr ($_, 32); + $nam[hex($addr)] = $name; + } +} +close (DATA); + +print "/*\n * *** This file is automatically generated from getconsdata.c. Do not edit! ***\n */\n"; +for ($i = 0; $i < hex($addr)+4; $i += 4) { + print "#define $nam[$i] $dat[$i]\n"; +} diff --git a/arch/arm/lib/getconsdata.c b/arch/arm/lib/getconsdata.c index 901c1ad16..bdf09d5a3 100644 --- a/arch/arm/lib/getconsdata.c +++ b/arch/arm/lib/getconsdata.c @@ -1,9 +1,8 @@ /* * linux/arch/arm/lib/getconsdata.c * - * Copyright (C) 1995, 1996 Russell King + * Copyright (C) 1995-1998 Russell King */ - #include <linux/config.h> #include <linux/sched.h> #include <linux/mm.h> @@ -15,17 +14,55 @@ #define OFF_MM(n) (unsigned long)&(((struct mm_struct *)0)->n) #ifdef KERNEL_DOMAIN -unsigned long kernel_domain = KERNEL_DOMAIN; +unsigned long DOM_KERNELDOMAIN = KERNEL_DOMAIN; #endif #ifdef USER_DOMAIN -unsigned long user_domain = USER_DOMAIN; -#endif -unsigned long addr_limit = OFF_TSK(addr_limit); -unsigned long tss_memmap = OFF_TSK(tss.memmap); -unsigned long mm = OFF_TSK(mm); -unsigned long pgd = OFF_MM(pgd); -unsigned long tss_save = OFF_TSK(tss.save); -unsigned long tss_fpesave = OFF_TSK(tss.fpstate.soft.save); +unsigned long DOM_USERDOMAIN = USER_DOMAIN; +#endif + +unsigned long TSK_STATE = OFF_TSK(state); +unsigned long TSK_FLAGS = OFF_TSK(flags); +unsigned long TSK_SIGPENDING = OFF_TSK(sigpending); +unsigned long TSK_ADDR_LIMIT = OFF_TSK(addr_limit); + +unsigned long MM = OFF_TSK(mm); +unsigned long PGD = OFF_MM(pgd); + +unsigned long TSS_MEMMAP = OFF_TSK(tss.memmap); +unsigned long TSS_SAVE = OFF_TSK(tss.save); +unsigned long TSS_FPESAVE = OFF_TSK(tss.fpstate.soft.save); #if defined(CONFIG_CPU_ARM2) || defined(CONFIG_CPU_ARM3) -unsigned long tss_memcmap = OFF_TSK(tss.memcmap); +unsigned long TSS_MEMCMAP = OFF_TSK(tss.memcmap); +#endif + +#ifdef _PAGE_PRESENT +unsigned long PAGE_PRESENT = _PAGE_PRESENT; +#endif +#ifdef _PAGE_RW +unsigned long PAGE_RW = _PAGE_RW; #endif +#ifdef _PAGE_USER +unsigned long PAGE_USER = _PAGE_USER; +#endif +#ifdef _PAGE_ACCESSED +unsigned long PAGE_ACCESSED = _PAGE_ACCESSED; +#endif +#ifdef _PAGE_DIRTY +unsigned long PAGE_DIRTY = _PAGE_DIRTY; +#endif +#ifdef _PAGE_READONLY +unsigned long PAGE_READONLY = _PAGE_READONLY; +#endif +#ifdef _PAGE_NOT_USER +unsigned long PAGE_NOT_USER = _PAGE_NOT_USER; +#endif +#ifdef _PAGE_OLD +unsigned long PAGE_OLD = _PAGE_OLD; +#endif +#ifdef _PAGE_CLEAN +unsigned long PAGE_CLEAN = _PAGE_CLEAN; +#endif + +unsigned long KSWI_BASE = 0x900000; +unsigned long KSWI_SYS_BASE = 0x9f0000; +unsigned long SYS_ERROR0 = 0x9f0000; diff --git a/arch/arm/lib/io-acorn.S b/arch/arm/lib/io-acorn.S index 172783b02..51550e014 100644 --- a/arch/arm/lib/io-acorn.S +++ b/arch/arm/lib/io-acorn.S @@ -57,7 +57,7 @@ ENTRY(insw) mov r2, r2, lsl#1 ENTRY(inswb) mov ip, sp - stmfd sp!, {r4 - r10 ,fp ,ip ,lr ,pc} + stmfd sp!, {r4 - r10, fp, ip, lr, pc} sub fp, ip, #4 addr r3, r0 add r0, r3, r0, lsl #2 @@ -70,7 +70,7 @@ ENTRY(inswb) strgeb r4, [r1], #1 movgt r4, r4, LSR#8 strgtb r4, [r1], #1 - ldmleea fp, {r4 - r10, fp, sp, pc}^ + LOADREGS(leea, fp, {r4 - r10, fp, sp, pc}) sub r2, r2, #2 Linswok: mov ip, #0xFF orr ip, ip, ip, lsl #8 diff --git a/arch/arm/lib/io-ebsa285.S b/arch/arm/lib/io-ebsa285.S new file mode 100644 index 000000000..a5c6386f0 --- /dev/null +++ b/arch/arm/lib/io-ebsa285.S @@ -0,0 +1,109 @@ +#define __ASSEMBLER__ +#include <linux/linkage.h> + +ENTRY(insl) + add r0, r0, #0xff000000 + add r0, r0, #0x00e00000 + ands ip, r1, #3 + bne 2f + +1: ldr r3, [r0] + str r3, [r1], #4 + subs r2, r2, #1 + bne 1b + mov pc, lr + +2: cmp ip, #2 + ldr ip, [r0] + blt 3f + bgt 4f + + strh ip, [r1], #2 + mov ip, ip, lsr #16 +1: subs r2, r2, #1 + ldrne r3, [r0] + orrne ip, ip, r3, lsl #16 + strne ip, [r1], #4 + movne ip, r3, lsr #16 + bne 1b + strh ip, [r1], #2 + mov pc, lr + +3: strb ip, [r1], #1 + mov ip, ip, lsr #8 + strh ip, [r1], #2 + mov ip, ip, lsr #16 +1: subs r2, r2, #1 + ldrne r3, [r0] + orrne ip, ip, r3, lsl #8 + strne ip, [r1], #4 + movne ip, r3, lsr #24 + bne 1b + strb ip, [r1], #1 + mov pc, lr + +4: strb ip, [r1], #1 + mov ip, ip, lsr #8 +1: subs r2, r2, #1 + ldrne r3, [r0] + orrne ip, ip, r3, lsl #24 + strne ip, [r1], #4 + movne ip, r3, lsr #8 + bne 1b + strb ip, [r1], #1 + mov ip, ip, lsr #8 + strh ip, [r1], #2 + mov pc, lr + +ENTRY(outsl) + add r0, r0, #0xff000000 + add r0, r0, #0x00e00000 + ands ip, r1, #3 + bne 2f + +1: ldr r3, [r1], #4 + str r3, [r0] + subs r2, r2, #1 + bne 1b + mov pc, lr + +2: bic r1, r1, #3 + cmp ip, #2 + ldr ip, [r1], #4 + mov ip, ip, lsr #16 + blt 3f + bgt 4f + +1: ldr r3, [r1], #4 + orr ip, ip, r3, lsl #16 + str ip, [r0] + mov ip, r3, lsr #16 + subs r2, r2, #1 + bne 1b + mov pc, lr + +3: ldr r3, [r1], #4 + orr ip, ip, r3, lsl #8 + str ip, [r0] + mov ip, r3, lsr #24 + subs r2, r2, #1 + bne 3b + mov pc, lr + +4: ldr r3, [r1], #4 + orr ip, ip, r3, lsl #24 + str ip, [r0] + mov ip, r3, lsr #8 + subs r2, r2, #1 + bne 4b + mov pc, lr + + +ENTRY(outsw) +ENTRY(outswb) + mov pc, lr + +ENTRY(insw) +ENTRY(inswb) + mov pc, lr + diff --git a/arch/arm/lib/ll_char_wr.S b/arch/arm/lib/ll_char_wr.S index 7df08d93b..fb1a5c5a4 100644 --- a/arch/arm/lib/ll_char_wr.S +++ b/arch/arm/lib/ll_char_wr.S @@ -3,9 +3,10 @@ * * Copyright (C) 1995, 1996 Russell King. * - * Speedups & 1bpp code (C) 1996 Philip Blundel & Russell King. + * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King. * * 10-04-96 RMK Various cleanups & reduced register usage. + * 08-04-98 RMK Shifts re-ordered */ @ Regs: [] = corruptable @@ -32,22 +33,22 @@ ENTRY(ll_write_char) @ @ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc) @ - eor ip, r1, #UNDERLINE << 24 + eor ip, r1, #UNDERLINE << 9 /* * calculate colours */ - tst r1, #INVERSE << 24 - moveq r2, r1, lsr #8 - moveq r3, r1, lsr #16 - movne r2, r1, lsr #16 - movne r3, r1, lsr #8 + tst r1, #INVERSE << 9 + moveq r2, r1, lsr #16 + moveq r3, r1, lsr #24 + movne r2, r1, lsr #24 + movne r3, r1, lsr #16 and r3, r3, #255 and r2, r2, #255 /* * calculate offset into character table */ - and r1, r1, #255 - mov r1, r1, lsl #3 + mov r1, r1, lsl #23 + mov r1, r1, lsr #20 /* * calculate offset required for each row [maybe I should make this an argument to this fn. * Have to see what the register usage is like in the calling routines. @@ -67,7 +68,7 @@ ENTRY(ll_write_char) add r0, r0, r5, lsl #3 @ Move to bottom of character add r1, r1, #7 ldrb r7, [r6, r1] - tst ip, #UNDERLINE << 24 + tst ip, #UNDERLINE << 9 eoreq r7, r7, #255 teq r4, #8 beq Lrow8bpplp @@ -131,7 +132,7 @@ Lrow8bpplp: mov ip, r7, lsr #4 @ Lrow1bpp: add r6, r6, r1 ldmia r6, {r4, r7} - tst ip, #INVERSE << 24 + tst ip, #INVERSE << 9 mvnne r4, r4 mvnne r7, r7 strb r4, [r0], r5 @@ -147,7 +148,7 @@ Lrow1bpp: add r6, r6, r1 mov r7, r7, lsr #8 strb r7, [r0], r5 mov r7, r7, lsr #8 - tst ip, #UNDERLINE << 24 + tst ip, #UNDERLINE << 9 mvneq r7, r7 strb r7, [r0], r5 LOADREGS(fd, sp!, {r4 - r7, pc}) diff --git a/arch/arm/lib/memcpy.S b/arch/arm/lib/memcpy.S index 209768f9f..f26e6cb1c 100644 --- a/arch/arm/lib/memcpy.S +++ b/arch/arm/lib/memcpy.S @@ -8,6 +8,12 @@ #include <asm/assembler.h> #include <linux/linkage.h> +#ifndef ENTRY +#define ENTRY(x...) \ + .globl _##x; \ +_##x: +#endif + .text #define ENTER \ mov ip,sp ;\ @@ -84,6 +90,7 @@ ENTRY(memmove) blt 6b ands ip, r1, #3 beq 1b + 8: bic r1, r1, #3 ldr r7, [r1], #4 cmp ip, #2 @@ -105,14 +112,14 @@ ENTRY(memmove) subs r2, r2, #16 bge 9b adds r2, r2, #12 - blt 1b + blt 100f 10: mov r3, r7, lsr #8 ldr r7, [r1], #4 orr r3, r3, r7, lsl #24 str r3, [r0], #4 subs r2, r2, #4 bge 10b - sub r1, r1, #3 +100: sub r1, r1, #3 b 6b 11: cmp r2, #12 diff --git a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S index a1524bee9..78256b319 100644 --- a/arch/arm/lib/uaccess.S +++ b/arch/arm/lib/uaccess.S @@ -13,13 +13,21 @@ #include <asm/errno.h> .text - +#ifdef ENTRY #define USER(x...) \ 9999: x; \ .section __ex_table,"a"; \ .align 3; \ .long 9999b,9001f; \ .previous +#else +#define USER(x...) \ + x +#define ENTRY(x...) \ + .globl _##x; \ +_##x: +#define TESTING +#endif #define PAGE_SHIFT 12 @@ -278,12 +286,12 @@ USER( strgebt r3, [r0], #1) // May fault USER( strgtbt r3, [r0], #1) // May fault b .c2u_finished +#ifndef TESTING .section .fixup,"ax" .align 0 9001: LOADREGS(fd,sp!, {r0, r4 - r7, pc}) .previous - - +#endif /* Prototype: unsigned long __arch_copy_from_user(void *to,const void *from,unsigned long n); * Purpose : copy a block from user memory to kernel memory @@ -538,10 +546,12 @@ USER( ldrget r3, [r1], #0) // May fault strgtb r3, [r0], #1 b .cfu_finished +#ifndef TESTING .section .fixup,"ax" .align 0 9001: LOADREGS(fd,sp!, {r0, r4 - r7, pc}) .previous +#endif /* Prototype: int __arch_clear_user(void *addr, size_t sz) * Purpose : clear some user memory @@ -556,7 +566,7 @@ ENTRY(__arch_clear_user) blt 2f ands ip, r0, #3 beq 1f - cmp ip, #1 + cmp ip, #2 USER( strbt r2, [r0], #1) USER( strlebt r2, [r0], #1) USER( strltbt r2, [r0], #1) @@ -566,9 +576,9 @@ USER( strltbt r2, [r0], #1) USER( strplt r2, [r0], #4) USER( strplt r2, [r0], #4) bpl 1b -2: adds r1, r1, #4 @ 3 2 1 0 -1 -2 -3 + adds r1, r1, #4 @ 3 2 1 0 -1 -2 -3 USER( strplt r2, [r0], #4) - tst r1, #2 @ 1x 1x 0x 0x 1x 1x 0x +2: tst r1, #2 @ 1x 1x 0x 0x 1x 1x 0x USER( strnebt r2, [r0], #1) USER( strnebt r2, [r0], #1) tst r1, #1 @ x1 x0 x1 x0 x1 x0 x1 @@ -576,6 +586,7 @@ USER( strnebt r2, [r0], #1) mov r0, #0 LOADREGS(fd,sp!, {r1, pc}) +#ifndef TESTING .section .fixup,"ax" .align 0 9001: LOADREGS(fd,sp!, {r0, pc}) @@ -611,21 +622,25 @@ USER( ldrbt r1, [r0], #1) */ ENTRY(__arch_strncpy_from_user) stmfd sp!, {lr} - mov ip, r2 + add ip, r1, #1 1: subs r2, r2, #1 bmi 2f USER( ldrbt r3, [r1], #1) strb r3, [r0], #1 teq r3, #0 bne 1b -2: subs r0, ip, r2 - LOADREGS(fd,sp!, {pc}) + sub r0, r1, ip + LOADREGS(fd, sp!, {pc}) +2: sub ip, ip, #1 + sub r0, r1, ip + LOADREGS(fd, sp!, {pc}) .section .fixup,"ax" .align 0 9001: mov r0, #-EFAULT - LOADREGS(fd,sp!, {pc}) + LOADREGS(fd, sp!, {pc}) .previous .align +#endif diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 0488da561..be9be35f0 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -7,8 +7,14 @@ # # Note 2! The CFLAGS definition is now in the main makefile... +ifeq ($(MACHINE),a5k) +MMARCH=arc +else +MMARCH=$(MACHINE) +endif + O_TARGET := mm.o -O_OBJS := init.o extable.o fault-$(PROCESSOR).o mm-$(MACHINE).o +O_OBJS := init.o extable.o fault-$(PROCESSOR).o mm-$(MMARCH).o ifeq ($(PROCESSOR),armo) O_OBJS += proc-arm2,3.o @@ -28,9 +34,9 @@ proc-sa110.o: ../lib/constants.h ../lib/constants.h: @$(MAKE) -C ../lib constants.h +ifneq ($(CONFIG_BINUTILS_NEW),y) %.o: %.S -ifndef $(CONFIG_BINUTILS_NEW) - $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.s - $(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.s - $(RM) ..tmp.s + $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..$@.tmp.s + $(CC) $(CFLAGS:-pipe=) -c -o $@ ..$@.tmp.s + $(RM) ..$@.tmp.s endif diff --git a/arch/arm/mm/fault-armo.c b/arch/arm/mm/fault-armo.c index a0fd65df2..6e1024c30 100644 --- a/arch/arm/mm/fault-armo.c +++ b/arch/arm/mm/fault-armo.c @@ -27,6 +27,50 @@ #define FAULT_CODE_WRITE 0x02 #define FAULT_CODE_USER 0x01 +struct pgtable_cache_struct quicklists; + +void __bad_pte(pmd_t *pmd) +{ + printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); + set_pmd(pmd, mk_pmd(BAD_PAGETABLE)); +} + +pgd_t *get_pgd_slow(void) +{ + pgd_t *pgd = (pgd_t *) kmalloc(PTRS_PER_PGD * BYTES_PER_PTR, GFP_KERNEL); + pgd_t *init; + + if (pgd) { + init = pgd_offset(&init_mm, 0); + memzero (pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); + memcpy (pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR); + } + return pgd; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) kmalloc (PTRS_PER_PTE * BYTES_PER_PTR, GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + memzero (pte, PTRS_PER_PTE * BYTES_PER_PTR); + set_pmd(pmd, mk_pmd(pte)); + return pte + offset; + } + set_pmd(pmd, mk_pmd(BAD_PAGETABLE)); + return NULL; + } + kfree (pte); + if (pmd_bad(*pmd)) { + __bad_pte(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + extern void die_if_kernel(char *msg, struct pt_regs *regs, unsigned int err, unsigned int ret); static void kernel_page_fault (unsigned long addr, int mode, struct pt_regs *regs, diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index 2925761fb..051b336bf 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -25,8 +25,96 @@ #define FAULT_CODE_READ 0x02 #define FAULT_CODE_USER 0x01 +struct pgtable_cache_struct quicklists; + +void __bad_pmd(pmd_t *pmd) +{ + printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); + set_pmd(pmd, mk_user_pmd(BAD_PAGETABLE)); +} + +void __bad_pmd_kernel(pmd_t *pmd) +{ + printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); + set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE)); +} + +pgd_t *get_pgd_slow(void) +{ + /* + * need to get a 16k page for level 1 + */ + pgd_t *pgd = (pgd_t *) __get_free_pages(GFP_KERNEL,2); + pgd_t *init; + + if (pgd) { + init = pgd_offset(&init_mm, 0); + memzero ((void *)pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); + memcpy (pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR); + } + return pgd; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) get_small_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + memzero (pte, PTRS_PER_PTE * BYTES_PER_PTR); + set_pmd(pmd, mk_user_pmd(pte)); + return pte + offset; + } + set_pmd(pmd, mk_user_pmd(BAD_PAGETABLE)); + return NULL; + } + free_small_page ((unsigned long) pte); + if (pmd_bad(*pmd)) { + __bad_pmd(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + +pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) get_small_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + memzero (pte, PTRS_PER_PTE * BYTES_PER_PTR); + set_pmd(pmd, mk_kernel_pmd(pte)); + return pte + offset; + } + set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE)); + return NULL; + } + free_small_page ((unsigned long) pte); + if (pmd_bad(*pmd)) { + __bad_pmd_kernel(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + extern void die_if_kernel(char *msg, struct pt_regs *regs, unsigned int err, unsigned int ret); +#ifdef DEBUG +static int sp_valid (unsigned long *sp) +{ + unsigned long addr = (unsigned long) sp; + + if (addr >= 0xb0000000 && addr < 0xd0000000) + return 1; + if (addr >= 0x03ff0000 && addr < 0x04000000) + return 1; + return 0; +} +#endif + static void kernel_page_fault (unsigned long addr, int mode, struct pt_regs *regs, struct task_struct *tsk, struct mm_struct *mm) { @@ -103,6 +191,16 @@ bad_area: printk ("%s: memory violation at pc=0x%08lx, lr=0x%08lx (bad address=0x%08lx, code %d)\n", tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, mode); #ifdef DEBUG + { + unsigned int i, j; + unsigned long *sp = (unsigned long *) (regs->ARM_sp - 128); + for (j = 0; j < 20 && sp_valid (sp); j++) { + printk ("%p: ", sp); + for (i = 0; i < 8 && sp_valid (sp); i += 1, sp++) + printk ("%08lx ", *sp); + printk ("\n"); + } + } show_regs (regs); c_backtrace (regs->ARM_fp, regs->ARM_cpsr); #endif @@ -133,7 +231,6 @@ do_DataAbort (unsigned long addr, int fsr, int error_code, struct pt_regs *regs) { if (user_mode(regs)) error_code |= FAULT_CODE_USER; - #define DIE(signr,nam)\ force_sig(signr, current);\ die_if_kernel(nam, regs, fsr, signr);\ @@ -154,18 +251,22 @@ do_DataAbort (unsigned long addr, int fsr, int error_code, struct pt_regs *regs) case 11: DIE(SIGSEGV, "Domain fault") case 13:/* permission fault on section */ -#ifndef DEBUG +#ifdef DEBUG { - unsigned int i, j, a; -static int count=2; -if (count-- == 0) while (1); - a = regs->ARM_sp; - for (j = 0; j < 10; j++) { - printk ("%08x: ", a); - for (i = 0; i < 8; i += 1, a += 4) - printk ("%08lx ", *(unsigned long *)a); + unsigned int i, j; + unsigned long *sp; + + printk ("%s: section permission fault (bad address=0x%08lx, code %d)\n", + current->comm, addr, error_code); + sp = (unsigned long *) (regs->ARM_sp - 128); + for (j = 0; j < 20 && sp_valid (sp); j++) { + printk ("%p: ", sp); + for (i = 0; i < 8 && sp_valid (sp); i += 1, sp++) + printk ("%08lx ", *sp); printk ("\n"); } + show_regs (regs); + c_backtrace(regs->ARM_fp, regs->ARM_cpsr); } #endif DIE(SIGSEGV, "Permission fault") diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index b9e777a32..36b118eb2 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -17,6 +17,7 @@ #include <linux/mm.h> #include <linux/swap.h> #include <linux/smp.h> +#include <linux/init.h> #ifdef CONFIG_BLK_DEV_INITRD #include <linux/blk.h> #endif @@ -30,7 +31,6 @@ pgd_t swapper_pg_dir[PTRS_PER_PGD]; -const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; extern char _etext, _stext, _edata, __bss_start, _end; extern char __init_begin, __init_end; @@ -103,7 +103,7 @@ void show_mem(void) /* * paging_init() sets up the page tables... */ -unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) +__initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)) { extern unsigned long free_area_init(unsigned long, unsigned long); @@ -130,7 +130,7 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) * memory is free. This is done after various parts of the system have * claimed their memory after the kernel image. */ -void mem_init(unsigned long start_mem, unsigned long end_mem) +__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) { extern void sound_init(void); int codepages = 0; diff --git a/arch/arm/mm/mm-arc.c b/arch/arm/mm/mm-arc.c index 4a4b4718c..ff447a6be 100644 --- a/arch/arm/mm/mm-arc.c +++ b/arch/arm/mm/mm-arc.c @@ -5,3 +5,4 @@ * * Copyright (C) 1998 Russell King */ +#include <asm/arch/mm-init.h> diff --git a/arch/arm/mm/mm-ebsa110.c b/arch/arm/mm/mm-ebsa110.c index 907a3f399..a937e098d 100644 --- a/arch/arm/mm/mm-ebsa110.c +++ b/arch/arm/mm/mm-ebsa110.c @@ -5,3 +5,22 @@ * * Copyright (C) 1998 Russell King */ + +#include <asm/io.h> + +/* map in IO */ +void setup_io_pagetables(void) +{ + unsigned long address = IO_START; + int spi = IO_BASE >> PGDIR_SHIFT; + + pgd_val(swapper_pg_dir[spi-1]) = 0xc0000000 | PMD_TYPE_SECT | + PMD_DOMAIN(DOMAIN_KERNEL) | PMD_SECT_AP_WRITE; + + while (address < IO_START + IO_SIZE && address) { + pgd_val(swapper_pg_dir[spi++]) = address | PMD_TYPE_SECT | + PMD_DOMAIN(DOMAIN_IO) | + PMD_SECT_AP_WRITE; + address += PGDIR_SIZE; + } +} diff --git a/arch/arm/mm/mm-ebsa285.c b/arch/arm/mm/mm-ebsa285.c new file mode 100644 index 000000000..eb5d7152a --- /dev/null +++ b/arch/arm/mm/mm-ebsa285.c @@ -0,0 +1,97 @@ +/* + * arch/arm/mm/mm-ebsa285.c + * + * Extra MM routines for the EBSA285 architecture + * + * Copyright (C) 1998 Russell King + */ +#include <linux/sched.h> +#include <linux/mm.h> + +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/io.h> +#include <asm/proc/mm-init.h> + +/* Logical Physical + * 0xfff00000 0x40000000 X-Bus + * 0xffe00000 0x7c000000 PCI I/O space + * 0xfe000000 0x42000000 CSR + * 0xfd000000 0x78000000 Outbound write flush + * 0xfc000000 0x79000000 PCI IACK/special space + * 0xf9000000 0x7a000000 PCI Config type 1 + * 0xf8000000 0x7b000000 PCI Config type 0 + */ + +static struct mapping { + unsigned long virtual; + unsigned long physical; + unsigned long length; +} io_mapping[] = { + /* + * This is to allow us to fiddle with the EEPROM + * This entry will go away in time + */ + { 0xd8000000, 0x41000000, 0x00400000 }, + + /* + * These ones are so that we can fiddle + * with the various cards (eg VGA) + * until we're happy with them... + */ + { 0xdc000000, 0x7c000000, 0x00100000 }, + { 0xe0000000, 0x80000000, 0x10000000 }, + + { 0xf8000000, 0x7b000000, 0x01000000 }, /* Type 0 Config */ + + { 0xf9000000, 0x7a000000, 0x01000000 }, /* Type 1 Config */ + + { 0xfc000000, 0x79000000, 0x01000000 }, /* PCI IACK */ + { 0xfd000000, 0x78000000, 0x01000000 }, /* Outbound wflsh*/ + { 0xfe000000, 0x42000000, 0x01000000 }, /* CSR */ + { 0xffe00000, 0x7c000000, 0x00100000 }, /* PCI I/O */ + { 0xfff00000, 0x40000000, 0x00100000 }, /* X-Bus */ +}; + +#define SIZEOFIO (sizeof(io_mapping) / sizeof(io_mapping[0])) + +/* map in IO */ +unsigned long setup_io_pagetables(unsigned long start_mem) +{ + struct mapping *mp; + int i; + + for (i = 0, mp = io_mapping; i < SIZEOFIO; i++, mp++) { + while ((mp->virtual & 1048575 || mp->physical & 1048575) && mp->length >= PAGE_SIZE) { + alloc_init_page(&start_mem, mp->virtual, mp->physical, DOMAIN_IO, + PTE_AP_WRITE); + + mp->length -= PAGE_SIZE; + mp->virtual += PAGE_SIZE; + mp->physical += PAGE_SIZE; + } + + while (mp->length >= 1048576) { +if (mp->virtual > 0xf0000000) + alloc_init_section(&start_mem, mp->virtual, mp->physical, DOMAIN_IO, + PMD_SECT_AP_WRITE); +else +alloc_init_section(&start_mem, mp->virtual, mp->physical, DOMAIN_USER, PMD_SECT_AP_WRITE | PMD_SECT_AP_READ); + + mp->length -= 1048576; + mp->virtual += 1048576; + mp->physical += 1048576; + } + + while (mp->length >= PAGE_SIZE) { + alloc_init_page(&start_mem, mp->virtual, mp->physical, DOMAIN_IO, + PTE_AP_WRITE); + + mp->length -= PAGE_SIZE; + mp->virtual += PAGE_SIZE; + mp->physical += PAGE_SIZE; + } + } + return start_mem; +} + diff --git a/arch/arm/mm/mm-nexuspci.c b/arch/arm/mm/mm-nexuspci.c index bbae80b19..2bb2d0fab 100644 --- a/arch/arm/mm/mm-nexuspci.c +++ b/arch/arm/mm/mm-nexuspci.c @@ -1,7 +1,28 @@ /* * arch/arm/mm/mm-nexuspci.c + * from arch/arm/mm/mm-ebsa110.c * - * Extra MM routines for the Archimedes architecture + * Extra MM routines for the NexusPCI architecture * * Copyright (C) 1998 Russell King */ + +#include <asm/io.h> + +/* map in IO */ +void setup_io_pagetables(void) +{ + unsigned long address = IO_START; + int spi = IO_BASE >> PGDIR_SHIFT; + + pgd_val(swapper_pg_dir[spi-1]) = 0xc0000000 | PMD_TYPE_SECT | + PMD_DOMAIN(DOMAIN_KERNEL) | PMD_SECT_AP_WRITE; + + while (address < IO_START + IO_SIZE && address) { + pgd_val(swapper_pg_dir[spi++]) = address | PMD_TYPE_SECT | + PMD_DOMAIN(DOMAIN_IO) | + PMD_SECT_AP_WRITE; + address += PGDIR_SIZE; + } +} + diff --git a/arch/arm/mm/mm-rpc.c b/arch/arm/mm/mm-rpc.c index 5eccb1f81..f789b8f79 100644 --- a/arch/arm/mm/mm-rpc.c +++ b/arch/arm/mm/mm-rpc.c @@ -6,7 +6,14 @@ * Copyright (C) 1998 Russell King */ +#include <linux/sched.h> +#include <linux/slab.h> + +#include <asm/pgtable.h> #include <asm/setup.h> +#include <asm/io.h> +#include <asm/proc/mm-init.h> +#include <asm/arch/mm-init.h> #define NR_DRAM_BANKS 4 #define NR_VRAM_BANKS 1 @@ -21,8 +28,8 @@ #define FIRST_DRAM_ADDR 0x10000000 #define PHYS_TO_BANK(x) (((x) >> BANK_SHIFT) & (NR_DRAM_BANKS - 1)) -#define BANK_TO_PHYS(x) ((FIRST_DRAM_ADDR) + - (((x) - FIRST_DRAM_BANK) << BANK_SHIFT) +#define BANK_TO_PHYS(x) ((FIRST_DRAM_ADDR) + \ + (((x) - FIRST_DRAM_BANK) << BANK_SHIFT)) struct ram_bank { unsigned int virt_addr; /* virtual address of the *end* of this bank + 1 */ @@ -75,6 +82,56 @@ void init_dram_banks(struct param_struct *params) rambank[bank].virt_addr = PAGE_OFFSET + bytes; } - drambank[4].phys_offset = 0xd6000000; - drambank[4].virt_addr = 0xd8000000; + rambank[FIRST_VRAM_BANK].phys_offset = 0xd6000000; + rambank[FIRST_VRAM_BANK].virt_addr = 0xd8000000; + + current->tss.memmap = __virt_to_phys((unsigned long)swapper_pg_dir); +} + +static struct mapping { + unsigned long virtual; + unsigned long physical; + unsigned long length; +} io_mapping[] = { + { SCREEN2_BASE, SCREEN_START, 2*1048576 }, /* VRAM */ + { IO_BASE, IO_START, IO_SIZE } /* IO space */ +}; + +#define SIZEOFIO (sizeof(io_mapping) / sizeof(io_mapping[0])) + +/* map in IO */ +unsigned long setup_io_pagetables(unsigned long start_mem) +{ + struct mapping *mp; + int i; + + for (i = 0, mp = io_mapping; i < SIZEOFIO; i++, mp++) { + while ((mp->virtual & 1048575 || mp->physical & 1048575) && mp->length >= PAGE_SIZE) { + alloc_init_page(&start_mem, mp->virtual, mp->physical, DOMAIN_IO, + PTE_AP_WRITE); + + mp->length -= PAGE_SIZE; + mp->virtual += PAGE_SIZE; + mp->physical += PAGE_SIZE; + } + + while (mp->length >= 1048576) { + alloc_init_section(&start_mem, mp->virtual, mp->physical, DOMAIN_IO, + PMD_SECT_AP_WRITE); + mp->length -= 1048576; + mp->virtual += 1048576; + mp->physical += 1048576; + } + + while (mp->length >= PAGE_SIZE) { + alloc_init_page(&start_mem, mp->virtual, mp->physical, DOMAIN_IO, + PTE_AP_WRITE); + + mp->length -= PAGE_SIZE; + mp->virtual += PAGE_SIZE; + mp->physical += PAGE_SIZE; + } + } + + return start_mem; } diff --git a/arch/arm/mm/proc-arm6,7.S b/arch/arm/mm/proc-arm6,7.S index 776d0d57c..a853671fc 100644 --- a/arch/arm/mm/proc-arm6,7.S +++ b/arch/arm/mm/proc-arm6,7.S @@ -72,10 +72,10 @@ _arm6_7_switch_to: stmfd sp!, {ip} @ Save cpsr_SVC str sp, [r0, #TSS_SAVE] @ Save sp_SVC ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC - ldr r0, [r1, #ADDR_LIMIT] + ldr r0, [r1, #TSK_ADDR_LIMIT] teq r0, #0 - moveq r0, #KERNEL_DOMAIN - movne r0, #USER_DOMAIN + moveq r0, #DOM_KERNELDOMAIN + movne r0, #DOM_USERDOMAIN mcr p15, 0, r0, c3, c0 @ Set domain reg ldr r0, [r1, #TSS_MEMMAP] @ Page table pointer mov r1, #0 diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S index 7d53bf230..633e8c164 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -161,8 +161,6 @@ _sa110_flush_icache_area: blt 1b mcr p15, 0, r0, c7, c5, 0 @ flush I cache mov pc, lr - -@LC0: .word _current /* * Function: sa110_switch_to (struct task_struct *prev, struct task_struct *next) * @@ -183,10 +181,10 @@ _sa110_switch_to: stmfd sp!, {ip} @ Save cpsr_SVC str sp, [r0, #TSS_SAVE] @ Save sp_SVC ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC - ldr r0, [r1, #ADDR_LIMIT] + ldr r0, [r1, #TSK_ADDR_LIMIT] teq r0, #0 - moveq r0, #KERNEL_DOMAIN - movne r0, #USER_DOMAIN + moveq r0, #DOM_KERNELDOMAIN + movne r0, #DOM_USERDOMAIN mcr p15, 0, r0, c3, c0 @ Set segment ldr r0, [r1, #TSS_MEMMAP] @ Page table pointer ldr r3, =Lclean_switch @@ -227,8 +225,6 @@ _sa110_data_abort: mov r2, r2, lsr #19 @ b1 = L and r3, r2, #0x69 << 2 and r2, r2, #2 -// teq r3, #0x21 << 2 -// orreq r2, r2, #1 @ b0 = {LD,ST}RT mrc p15, 0, r1, c5, c0, 0 @ get FSR and r1, r1, #255 mov pc, lr diff --git a/arch/arm/vmlinux.lds b/arch/arm/vmlinux.lds index 787e5c99d..db1f720e7 100644 --- a/arch/arm/vmlinux.lds +++ b/arch/arm/vmlinux.lds @@ -1,4 +1,5 @@ -/* ld script to make i386 Linux kernel +/* ld script to make ARM Linux kernel + * taken from the i386 version * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz> */ OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm") @@ -34,11 +35,15 @@ SECTIONS _edata = .; /* End of data section */ - . = ALIGN(4096); /* Init code and data */ + /* This has to be aligned to a page boundary to do us any good. This + alignment is overkill for ARM6 up but needed for ARM3. Since all this + data will be thrown away I don't think the extra padding will hurt. + -- pb */ + . = ALIGN(32768); /* Init code and data */ __init_begin = .; .text.init : { *(.text.init) } .data.init : { *(.data.init) } - . = ALIGN(4096); + . = ALIGN(32768); __init_end = .; __bss_start = .; /* BSS */ diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 359c20b9b..98bde850f 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -58,9 +58,6 @@ SUBDIRS := $(SUBDIRS) arch/i386/math-emu DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a endif -memsize: dummy - @echo "__kernel_offset__ = (0x1000-$(CONFIG_MAX_MEMSIZE))*1024*1024;" > arch/i386/.kernel_offset.lds - arch/i386/kernel: dummy $(MAKE) linuxsubdirs SUBDIRS=arch/i386/kernel @@ -69,27 +66,27 @@ arch/i386/mm: dummy MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot -zImage: memsize vmlinux +zImage: vmlinux @$(MAKEBOOT) zImage -bzImage: memsize vmlinux +bzImage: vmlinux @$(MAKEBOOT) bzImage compressed: zImage -zlilo: memsize vmlinux +zlilo: vmlinux @$(MAKEBOOT) BOOTIMAGE=zImage zlilo -bzlilo: memsize vmlinux +bzlilo: vmlinux @$(MAKEBOOT) BOOTIMAGE=bzImage zlilo -zdisk: memsize vmlinux +zdisk: vmlinux @$(MAKEBOOT) BOOTIMAGE=zImage zdisk -bzdisk: memsize vmlinux +bzdisk: vmlinux @$(MAKEBOOT) BOOTIMAGE=bzImage zdisk -install: memsize vmlinux +install: vmlinux @$(MAKEBOOT) BOOTIMAGE=bzImage install archclean: diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S index fb1bd5559..0aa8ddc44 100644 --- a/arch/i386/boot/compressed/head.S +++ b/arch/i386/boot/compressed/head.S @@ -37,10 +37,10 @@ startup_32: cld cli movl $(__KERNEL_DS),%eax - mov %ax,%ds - mov %ax,%es - mov %ax,%fs - mov %ax,%gs + movl %ax,%ds + movl %ax,%es + movl %ax,%fs + movl %ax,%gs #ifdef __SMP__ orw %bx,%bx # What state are we in BX=1 for SMP # 0 for boot diff --git a/arch/i386/config.in b/arch/i386/config.in index 058f908e3..86c214a1e 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -17,7 +17,9 @@ choice 'Processor family' \ Pentium/K5/5x86/6x86 CONFIG_M586 \ PPro/K6/6x86MX CONFIG_M686" Pentium bool 'Math emulation' CONFIG_MATH_EMULATION -int ' Max physical memory in MB' CONFIG_MAX_MEMSIZE 1024 +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR +fi endmenu mainmenu_option next_comment @@ -37,7 +39,8 @@ bool 'PCI support' CONFIG_PCI if [ "$CONFIG_PCI" = "y" ]; then bool ' PCI BIOS support' CONFIG_PCI_BIOS bool ' PCI direct access support' CONFIG_PCI_DIRECT - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' PCI quirks' CONFIG_PCI_QUIRKS + if [ "$CONFIG_PCI_QUIRKS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE fi bool ' Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC @@ -114,17 +117,6 @@ if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then fi endmenu -# Conditionally compile in the Uniform CD-ROM driver -if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_MCDX" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then - define_bool CONFIG_CDROM y -else - if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_MCDX" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then - define_bool CONFIG_CDROM m - else - define_bool CONFIG_CDROM n - fi -fi - source fs/Config.in source fs/nls/Config.in diff --git a/arch/i386/defconfig b/arch/i386/defconfig index f3792ce05..ab1c622c7 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -15,7 +15,6 @@ CONFIG_M586=y # CONFIG_M686 is not set # CONFIG_MATH_EMULATION is not set -CONFIG_MAX_MEMSIZE=1024 # # Loadable module support @@ -31,6 +30,7 @@ CONFIG_NET=y CONFIG_PCI=y CONFIG_PCI_BIOS=y CONFIG_PCI_DIRECT=y +CONFIG_PCI_QUIRKS=y CONFIG_PCI_OLD_PROC=y # CONFIG_MCA is not set CONFIG_SYSVIPC=y @@ -214,7 +214,6 @@ CONFIG_EEXPRESS_PRO100=y # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set -CONFIG_CDROM=y # # Filesystems diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index ce1e6652d..6f63d2c97 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -21,6 +21,7 @@ O_TARGET := kernel.o O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o OX_OBJS := i386_ksyms.o +MX_OBJS := ifdef CONFIG_PCI O_OBJS += bios32.o @@ -30,6 +31,14 @@ ifdef CONFIG_MCA O_OBJS += mca.o endif +ifeq ($(CONFIG_MTRR),y) +OX_OBJS += mtrr.o +else + ifeq ($(CONFIG_MTRR),m) + MX_OBJS += mtrr.o + endif +endif + ifdef SMP diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index 7e865c417..f2955918a 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -1,7 +1,7 @@ /* - * bios32.c - BIOS32, PCI BIOS functions. + * bios32.c - Low-Level PCI Access * - * $Id: bios32.c,v 1.5 1997/12/02 01:48:00 ralf Exp $ + * $Id: bios32.c,v 1.29 1998/04/17 16:31:15 mj Exp $ * * Sponsored by * iX Multiuser Multitasking Magazine @@ -64,14 +64,16 @@ * * Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access parts * and cleaned it up... Martin Mares <mj@atrey.karlin.mff.cuni.cz> + * + * Feb 6, 1998 : No longer using BIOS to find devices and device classes. [mj] */ #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/init.h> +#include <linux/ioport.h> #include <asm/page.h> #include <asm/segment.h> @@ -85,14 +87,20 @@ #include "irq.h" +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + /* * Generic PCI access -- indirect calls according to detected HW. */ struct pci_access { int pci_present; - int (*find_device)(unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *); - int (*find_class)(unsigned int, unsigned short, unsigned char *, unsigned char *); int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *); int (*read_config_word)(unsigned char, unsigned char, unsigned char, unsigned short *); int (*read_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int *); @@ -108,8 +116,6 @@ static int pci_stub(void) static struct pci_access pci_access_none = { 0, /* No PCI present */ - (void *) pci_stub, /* No functions implemented */ - (void *) pci_stub, (void *) pci_stub, (void *) pci_stub, (void *) pci_stub, @@ -125,54 +131,10 @@ int pcibios_present(void) return access_pci->pci_present; } -int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *device_fn) -{ - return access_pci->find_class(class_code, index, bus, device_fn); -} - -int pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, unsigned char *device_fn) -{ - return access_pci->find_device(vendor, device_id, index, bus, device_fn); -} - int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned char *value) { - int res; - - res = access_pci->read_config_byte(bus, device_fn, where, value); - -#ifdef __SMP__ -/* - * IOAPICs can take PCI IRQs directly, lets first check the mptable: - */ - if (where == PCI_INTERRUPT_LINE) { - int irq; - char pin; - - /* - * get the PCI IRQ INT _physical pin_ for this device - */ - access_pci->read_config_byte(bus, device_fn, - PCI_INTERRUPT_PIN, &pin); - /* - * subtle, PCI pins are numbered starting from 1 ... - */ - pin--; - - irq = IO_APIC_get_PCI_irq_vector (bus,PCI_SLOT(device_fn),pin); - if (irq != -1) - *value = (unsigned char) irq; - - printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", - bus,PCI_SLOT(device_fn), pin, irq); - - } -#endif - - return res; + return access_pci->read_config_byte(bus, device_fn, where, value); } int pcibios_read_config_word (unsigned char bus, @@ -205,60 +167,19 @@ int pcibios_write_config_dword (unsigned char bus, return access_pci->write_config_dword(bus, device_fn, where, value); } -/* - * Direct access to PCI hardware... - */ - -/* - * Given the vendor and device ids, find the n'th instance of that device - * in the system. - */ +#define PCI_PROBE_BIOS 1 +#define PCI_PROBE_CONF1 2 +#define PCI_PROBE_CONF2 4 +#define PCI_NO_SORT 0x100 +#define PCI_BIOS_SORT 0x200 -#ifdef CONFIG_PCI_DIRECT - -static int pci_direct_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} +static unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2; /* - * Given the class, find the n'th instance of that device - * in the system. + * Direct access to PCI hardware... */ -static int pci_direct_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} +#ifdef CONFIG_PCI_DIRECT /* * Functions for accessing PCI configuration space with type 1 accesses @@ -346,8 +267,6 @@ static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device static struct pci_access pci_direct_conf1 = { 1, - pci_direct_find_device, - pci_direct_find_class, pci_conf1_read_config_byte, pci_conf1_read_config_word, pci_conf1_read_config_dword, @@ -458,8 +377,6 @@ static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device static struct pci_access pci_direct_conf2 = { 1, - pci_direct_find_device, - pci_direct_find_class, pci_conf2_read_config_byte, pci_conf2_read_config_word, pci_conf2_read_config_dword, @@ -470,39 +387,43 @@ static struct pci_access pci_direct_conf2 = { __initfunc(static struct pci_access *pci_check_direct(void)) { - unsigned int tmp; - unsigned long flags; + unsigned int tmp; + unsigned long flags; - save_flags(flags); cli(); + save_flags(flags); cli(); + + /* + * Check if configuration type 1 works. + */ + if (pci_probe & PCI_PROBE_CONF1) { + outb (0x01, 0xCFB); + tmp = inl (0xCF8); + outl (0x80000000, 0xCF8); + if (inl (0xCF8) == 0x80000000) { + outl (tmp, 0xCF8); + restore_flags(flags); + printk("PCI: Using configuration type 1\n"); + return &pci_direct_conf1; + } + outl (tmp, 0xCF8); + } + + /* + * Check if configuration type 2 works. + */ + if (pci_probe & PCI_PROBE_CONF2) { + outb (0x00, 0xCFB); + outb (0x00, 0xCF8); + outb (0x00, 0xCFA); + if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) { + restore_flags(flags); + printk("PCI: Using configuration type 2\n"); + return &pci_direct_conf2; + } + } - /* - * Check if configuration type 1 works. - */ - outb (0x01, 0xCFB); - tmp = inl (0xCF8); - outl (0x80000000, 0xCF8); - if (inl (0xCF8) == 0x80000000) { - outl (tmp, 0xCF8); - restore_flags(flags); - printk("PCI: Using configuration type 1\n"); - return &pci_direct_conf1; - } - outl (tmp, 0xCF8); - - /* - * Check if configuration type 2 works. - */ - outb (0x00, 0xCFB); - outb (0x00, 0xCF8); - outb (0x00, 0xCFA); - if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) { restore_flags(flags); - printk("PCI: Using configuration type 2\n"); - return &pci_direct_conf2; - } - restore_flags(flags); - printk("PCI: PCI hardware not found (i.e., not present or not supported).\n"); - return NULL; + return NULL; } #endif @@ -599,7 +520,7 @@ static unsigned long bios32_service(unsigned long service) printk("bios32_service(0x%lx): not present\n", service); return 0; default: /* Shouldn't happen */ - printk("bios32_service(0x%lx): returned 0x%x, mail drew@colorado.edu\n", + printk("bios32_service(0x%lx): returned 0x%x, report to <mj@ucw.cz>.\n", service, return_code); return 0; } @@ -642,7 +563,7 @@ __initfunc(static int check_pcibios(void)) if (present_status || (signature != PCI_SIGNATURE)) { printk ("PCI: %s: BIOS32 Service Directory says PCI BIOS is present,\n" " but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n" - " and signature of 0x%08lx (%c%c%c%c). Mail drew@Colorado.EDU\n", + " and signature of 0x%08lx (%c%c%c%c). Report to <mj@ucw.cz>.\n", (signature == PCI_SIGNATURE) ? "WARNING" : "ERROR", present_status, signature, (char) (signature >> 0), (char) (signature >> 8), @@ -660,6 +581,8 @@ __initfunc(static int check_pcibios(void)) return 0; } +#if 0 /* Not used */ + static int pci_bios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *device_fn) { @@ -684,8 +607,10 @@ static int pci_bios_find_class (unsigned int class_code, unsigned short index, return (int) (ret & 0xff00) >> 8; } -static int pci_bios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, unsigned char *device_fn) +#endif + +__initfunc(static int pci_bios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, unsigned char *device_fn)) { unsigned short bx; unsigned short ret; @@ -847,8 +772,6 @@ static int pci_bios_write_config_dword (unsigned char bus, static struct pci_access pci_bios_access = { 1, - pci_bios_find_device, - pci_bios_find_class, pci_bios_read_config_byte, pci_bios_read_config_word, pci_bios_read_config_dword, @@ -887,21 +810,17 @@ __initfunc(static struct pci_access *pci_find_bios(void)) if (sum != 0) continue; if (check->fields.revision != 0) { - printk("PCI: unsupported BIOS32 revision %d at 0x%p, mail drew@colorado.edu\n", + printk("PCI: unsupported BIOS32 revision %d at 0x%p, report to <mj@ucw.cz>\n", check->fields.revision, check); continue; } - printk ("PCI: BIOS32 Service Directory structure at 0x%p\n", check); + DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check); if (check->fields.entry >= 0x100000) { -#ifdef CONFIG_PCI_DIRECT - printk("PCI: BIOS32 entry in high memory, trying direct PCI access.\n"); - return pci_check_direct(); -#else - printk("PCI: BIOS32 entry in high memory, cannot use.\n"); -#endif + printk("PCI: BIOS32 entry (0x%p) in high memory, cannot use.\n", check); + return NULL; } else { bios32_entry = check->fields.entry; - printk ("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); + DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); bios32_indirect.address = bios32_entry + PAGE_OFFSET; if (check_pcibios()) return &pci_bios_access; @@ -912,36 +831,237 @@ __initfunc(static struct pci_access *pci_find_bios(void)) return NULL; } +/* + * Sort the device list according to PCI BIOS. + */ + +__initfunc(void pcibios_sort(void)) +{ + struct pci_dev *dev = pci_devices; + struct pci_dev **last = &pci_devices; + struct pci_dev *d, **dd, *e; + int idx; + unsigned char bus, devfn; + + DBG("PCI: Sorting device list...\n"); + while ((e = dev)) { + idx = 0; + while (pci_bios_find_device(e->vendor, e->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) { + idx++; + for(dd=&dev; (d = *dd); dd = &d->next) { + if (d->bus->number == bus && d->devfn == devfn) { + *dd = d->next; + *last = d; + last = &d->next; + break; + } + } + if (!d) + printk("PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn); + } + if (!idx) { + printk("PCI: Device %02x:%02x not found by BIOS\n", + dev->bus->number, dev->devfn); + d = dev; + dev = dev->next; + *last = d; + last = &d->next; + } + } + *last = NULL; +} + #endif /* - * No fixup function used. + * Several BIOS'es forget to assign addresses to I/O ranges. + * We try to fix it here, expecting there are free addresses + * starting with 0x5800. Ugly, but until we come with better + * resource management, it's the only simple solution. */ -__initfunc(unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) +static int pci_last_io_addr __initdata = 0x5800; + +__initfunc(void pcibios_fixup_io_addr(struct pci_dev *dev, int idx)) { - return mem_start; + unsigned short cmd; + unsigned int reg = PCI_BASE_ADDRESS_0 + 4*idx; + unsigned int size, addr, try; + unsigned int bus = dev->bus->number; + unsigned int devfn = dev->devfn; + + if (!pci_last_io_addr) { + printk("PCI: Unassigned I/O space for %02x:%02x\n", bus, devfn); + return; + } + pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); + pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd & ~PCI_COMMAND_IO); + pcibios_write_config_dword(bus, devfn, reg, ~0); + pcibios_read_config_dword(bus, devfn, reg, &size); + size = (~(size & PCI_BASE_ADDRESS_IO_MASK) & 0xffff) + 1; + addr = 0; + if (!size || size > 0x100) + printk("PCI: Unable to handle I/O allocation for %02x:%02x (%04x), tell <mj@ucw.cz>\n", bus, devfn, size); + else { + do { + addr = (pci_last_io_addr + size - 1) & ~(size-1); + pci_last_io_addr = addr + size; + } while (check_region(addr, size)); + printk("PCI: Assigning I/O space %04x-%04x to device %02x:%02x\n", addr, addr+size-1, bus, devfn); + pcibios_write_config_dword(bus, devfn, reg, addr | PCI_BASE_ADDRESS_SPACE_IO); + pcibios_read_config_dword(bus, devfn, reg, &try); + if ((try & PCI_BASE_ADDRESS_IO_MASK) != addr) { + addr = 0; + printk("PCI: Address setup failed, got %04x\n", try); + } else + dev->base_address[idx] = try; + } + if (!addr) { + pcibios_write_config_dword(bus, devfn, reg, 0); + dev->base_address[idx] = 0; + } + pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd); +} + +/* + * Arch-dependent fixups. We need to fix here base addresses, I/O + * and memory enables and IRQ's as the PCI BIOS'es are buggy as hell. + */ + +__initfunc(void pcibios_fixup(void)) +{ + struct pci_dev *dev; + int i, has_io, has_mem; + unsigned short cmd; + + for(dev = pci_devices; dev; dev=dev->next) { + /* + * There are buggy BIOSes that forget to enable I/O and memory + * access to PCI devices. We try to fix this, but we need to + * be sure that the BIOS didn't forget to assign an address + * to the device. [mj] + */ + has_io = has_mem = 0; + for(i=0; i<6; i++) { + unsigned long a = dev->base_address[i]; + if (a & PCI_BASE_ADDRESS_SPACE_IO) { + has_io = 1; + a &= PCI_BASE_ADDRESS_IO_MASK; + if (!a || a == PCI_BASE_ADDRESS_IO_MASK) + pcibios_fixup_io_addr(dev, i); + } else if (a & PCI_BASE_ADDRESS_MEM_MASK) + has_mem = 1; + } + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (has_io && !(cmd & PCI_COMMAND_IO)) { + printk("PCI: Enabling I/O for device %02x:%02x\n", + dev->bus->number, dev->devfn); + cmd |= PCI_COMMAND_IO; + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) { + printk("PCI: Enabling memory for device %02x:%02x\n", + dev->bus->number, dev->devfn); + cmd |= PCI_COMMAND_MEMORY; + pci_write_config_word(dev, PCI_COMMAND, cmd); + } +#ifdef __SMP__ + /* + * Recalculate IRQ numbers if we use the I/O APIC + */ + { + int irq; + unsigned char pin; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + pin--; /* interrupt pins are numbered starting from 1 */ + irq = IO_APIC_get_PCI_irq_vector (dev->bus->number, PCI_SLOT(dev->devfn), pin); + if (irq >= 0) { + printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", + dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); + dev->irq = irq; + } + } + } +#endif + /* + * Fix out-of-range IRQ numbers and report bogus IRQ. + */ + if (dev->irq >= NR_IRQS) + dev->irq = 0; + } + +#ifdef CONFIG_PCI_BIOS + if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT)) + pcibios_sort(); +#endif } /* - * Initialization. Try all known PCI access methods. + * Initialization. Try all known PCI access methods. Note that we support + * using both PCI BIOS and direct access: in such cases, we use I/O ports + * to access config space, but we still keep BIOS order of cards to be + * compatible with 2.0.X. This should go away in 2.3. */ -__initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)) +__initfunc(void pcibios_init(void)) { - struct pci_access *a = NULL; + struct pci_access *bios = NULL; + struct pci_access *dir = NULL; #ifdef CONFIG_PCI_BIOS - a = pci_find_bios(); -#else + if ((pci_probe & PCI_PROBE_BIOS) && ((bios = pci_find_bios()))) + pci_probe |= PCI_BIOS_SORT; +#endif #ifdef CONFIG_PCI_DIRECT - a = pci_check_direct(); -#else -#error "You need to set CONFIG_PCI_BIOS or CONFIG_PCI_DIRECT if you want PCI support." + if (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2)) + dir = pci_check_direct(); #endif + if (dir) + access_pci = dir; + else if (bios) + access_pci = bios; +} + +#if !defined(CONFIG_PCI_BIOS) && !defined(CONFIG_PCI_DIRECT) +#error PCI configured with neither PCI BIOS or PCI direct access support. #endif - if (a) - access_pci = a; - return memory_start; +__initfunc(char *pcibios_setup(char *str)) +{ + if (!strcmp(str, "off")) { + pci_probe = 0; + return NULL; + } else if (!strncmp(str, "io=", 3)) { + char *p; + unsigned int x = simple_strtoul(str+3, &p, 16); + if (p && *p) + return str; + pci_last_io_addr = x; + return NULL; + } +#ifdef CONFIG_PCI_BIOS + else if (!strcmp(str, "bios")) { + pci_probe = PCI_PROBE_BIOS; + return NULL; + } else if (!strcmp(str, "nobios")) { + pci_probe &= ~PCI_PROBE_BIOS; + return NULL; + } else if (!strcmp(str, "nosort")) { + pci_probe |= PCI_NO_SORT; + return NULL; + } +#endif +#ifdef CONFIG_PCI_DIRECT + else if (!strcmp(str, "conf1")) { + pci_probe = PCI_PROBE_CONF1; + return NULL; + } + else if (!strcmp(str, "conf2")) { + pci_probe = PCI_PROBE_CONF2; + return NULL; + } +#endif + return str; } diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 14b82b45b..b6541005f 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -81,8 +81,8 @@ ENOSYS = 38 #define SAVE_ALL \ cld; \ - push %es; \ - push %ds; \ + pushl %es; \ + pushl %ds; \ pushl %eax; \ pushl %ebp; \ pushl %edi; \ @@ -91,8 +91,8 @@ ENOSYS = 38 pushl %ecx; \ pushl %ebx; \ movl $(__KERNEL_DS),%edx; \ - mov %dx,%ds; \ - mov %dx,%es; + movl %dx,%ds; \ + movl %dx,%es; #define RESTORE_ALL \ popl %ebx; \ @@ -102,8 +102,8 @@ ENOSYS = 38 popl %edi; \ popl %ebp; \ popl %eax; \ - pop %ds; \ - pop %es; \ + popl %ds; \ + popl %es; \ addl $4,%esp; \ iret @@ -155,7 +155,7 @@ ENTRY(system_call) jae badsys testb $0x20,flags(%ebx) # PF_TRACESYS jne tracesys - call SYMBOL_NAME(sys_call_table)(,%eax,4) + call *SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # save the return value ALIGN .globl ret_from_sys_call @@ -193,7 +193,7 @@ tracesys: movl $-ENOSYS,EAX(%esp) call SYMBOL_NAME(syscall_trace) movl ORIG_EAX(%esp),%eax - call SYMBOL_NAME(sys_call_table)(,%eax,4) + call *SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # save the return value call SYMBOL_NAME(syscall_trace) jmp ret_from_sys_call @@ -231,7 +231,7 @@ ENTRY(divide_error) pushl $ SYMBOL_NAME(do_divide_error) ALIGN error_code: - push %ds + pushl %ds pushl %eax xorl %eax,%eax pushl %ebp @@ -241,17 +241,27 @@ error_code: decl %eax # eax = -1 pushl %ecx pushl %ebx +#if 1 xorl %ecx,%ecx # zero ecx cld mov %es,%cx # get the lower order bits of es +#else + cld +# Some older processors leave the top 16 bits of the 32 bit destination +# register undefined, rather than zeroed in the following instruction. +# This won't matter when restoring or loading a segment register from the +# stack. It may be a problem if any code reads the full 32 bit value. +# dosemu? kernel? Would somebody like to verify that this way is really OK? + movl %es,%cx +#endif xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. ) movl %esp,%edx xchgl %ecx, ES(%esp) # get the address and save es. pushl %eax # push the error code pushl %edx movl $(__KERNEL_DS),%edx - mov %dx,%ds - mov %dx,%es + movl %dx,%ds + movl %dx,%es GET_CURRENT(%ebx) call *%ecx addl $8,%esp @@ -533,6 +543,7 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_pread) /* 180 */ .long SYMBOL_NAME(sys_pwrite) .long SYMBOL_NAME(sys_chown) + .long SYMBOL_NAME(sys_getcwd) .rept NR_syscalls-182 .long SYMBOL_NAME(sys_ni_syscall) diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index 048921838..86031f37f 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -45,10 +45,10 @@ startup_32: */ cld movl $(__KERNEL_DS),%eax - mov %ax,%ds - mov %ax,%es - mov %ax,%fs - mov %ax,%gs + movl %ax,%ds + movl %ax,%es + movl %ax,%fs + movl %ax,%gs #ifdef __SMP__ orw %bx,%bx jz 1f @@ -321,10 +321,10 @@ is386: pushl %ecx # restore original EFLAGS lidt idt_descr ljmp $(__KERNEL_CS),$1f 1: movl $(__KERNEL_DS),%eax # reload all the segment registers - mov %ax,%ds # after changing gdt. - mov %ax,%es - mov %ax,%fs - mov %ax,%gs + movl %ax,%ds # after changing gdt. + movl %ax,%es + movl %ax,%fs + movl %ax,%gs #ifdef __SMP__ movl $(__KERNEL_DS), %eax mov %ax,%ss # Reload the stack pointer (segment only) @@ -404,16 +404,16 @@ ignore_int: pushl %eax pushl %ecx pushl %edx - push %es - push %ds + pushl %es + pushl %ds movl $(__KERNEL_DS),%eax - mov %ax,%ds - mov %ax,%es + movl %ax,%ds + movl %ax,%es pushl $int_msg call SYMBOL_NAME(printk) popl %eax - pop %ds - pop %es + popl %ds + popl %es popl %edx popl %ecx popl %eax @@ -619,9 +619,6 @@ ENTRY(idt_table) .fill 256,8,0 # idt is uninitialized /* - * This gdt setup gives the kernel a CONFIG_MAX_MEMSIZE sized address space at - * virtual address PAGE_OFFSET. - * * This contains up to 8192 quadwords depending on NR_TASKS - 64kB of * gdt entries. Ugh. * diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index d2837d648..66dec5fed 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -8,7 +8,6 @@ #include <linux/in6.h> #include <linux/interrupt.h> #include <linux/smp_lock.h> -#include <linux/pci.h> #include <asm/semaphore.h> #include <asm/processor.h> @@ -64,13 +63,14 @@ EXPORT_SYMBOL(__generic_copy_to_user); EXPORT_SYMBOL(strlen_user); #ifdef __SMP__ -EXPORT_SYMBOL(apic_reg); /* Needed internally for the I386 inlines */ EXPORT_SYMBOL(cpu_data); EXPORT_SYMBOL_NOVERS(kernel_flag); EXPORT_SYMBOL_NOVERS(active_kernel_processor); EXPORT_SYMBOL(smp_invalidate_needed); EXPORT_SYMBOL_NOVERS(__lock_kernel); EXPORT_SYMBOL(lk_lockmsg); +EXPORT_SYMBOL(__cpu_logical_map); +EXPORT_SYMBOL(smp_num_cpus); /* Global SMP irq stuff */ EXPORT_SYMBOL(synchronize_irq); @@ -82,6 +82,8 @@ EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); EXPORT_SYMBOL(__global_save_flags); EXPORT_SYMBOL(__global_restore_flags); +EXPORT_SYMBOL(smp_message_pass); +EXPORT_SYMBOL(mtrr_hook); #endif #ifdef CONFIG_MCA @@ -97,7 +99,3 @@ EXPORT_SYMBOL(mca_set_adapter_procfn); EXPORT_SYMBOL(mca_isenabled); EXPORT_SYMBOL(mca_isadapter); #endif - -#if CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 219e7f853..6e422614e 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -28,13 +28,11 @@ #include "irq.h" -#define IO_APIC_BASE 0xfec00000 - /* * volatile is justified in this case, it might change * spontaneously, GCC should not cache it */ -volatile unsigned int * io_apic_reg = NULL; +#define IO_APIC_BASE ((volatile int *)0xfec00000) /* * The structure of the IO-APIC: @@ -96,17 +94,19 @@ int nr_ioapic_registers = 0; /* # of IRQ routing registers */ int mp_irq_entries = 0; /* # of MP IRQ source entries */ struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; /* MP IRQ source entries */ +int mpc_default_type = 0; /* non-0 if default (table-less) + MP configuration */ unsigned int io_apic_read (unsigned int reg) { - *io_apic_reg = reg; - return *(io_apic_reg+4); + *IO_APIC_BASE = reg; + return *(IO_APIC_BASE+4); } void io_apic_write (unsigned int reg, unsigned int value) { - *io_apic_reg = reg; - *(io_apic_reg+4) = value; + *IO_APIC_BASE = reg; + *(IO_APIC_BASE+4) = value; } void enable_IO_APIC_irq (unsigned int irq) @@ -256,7 +256,7 @@ void setup_IO_APIC_irqs (void) /* * PCI IRQ redirection. Yes, limits are hardcoded. */ - if ((i>=16) && (i<=19)) { + if ((i>=16) && (i<=23)) { if (pirq_entries[i-16] != -1) { if (!pirq_entries[i-16]) { printk("disabling PIRQ%d\n", i-16); @@ -516,16 +516,16 @@ void print_IO_APIC (void) static void init_sym_mode (void) { printk("enabling Symmetric IO mode ... "); - outb (0x70, 0x22); - outb (0x01, 0x23); + outb_p (0x70, 0x22); + outb_p (0x01, 0x23); printk("...done.\n"); } void init_pic_mode (void) { printk("disabling Symmetric IO mode ... "); - outb (0x70, 0x22); - outb (0x00, 0x23); + outb_p (0x70, 0x22); + outb_p (0x00, 0x23); printk("...done.\n"); } @@ -579,17 +579,85 @@ static int ioapic_blacklisted (void) return in_ioapic_list(ioapic_blacklist); } +static void setup_ioapic_id (void) +{ + struct IO_APIC_reg_00 reg_00; -void setup_IO_APIC (void) + /* + * 'default' mptable configurations mean a hardwired setup, + * 2 CPUs, 16 APIC registers. IO-APIC ID is usually set to 0, + * setting it to ID 2 should be fine. + */ + + /* + * Sanity check, is ID 2 really free? Every APIC in the + * system must have a unique ID or we get lots of nice + * 'stuck on smp_invalidate_needed IPI wait' messages. + */ + if (cpu_present_map & (1<<0x2)) + panic("APIC ID 2 already used"); + + /* + * set the ID + */ + *(int *)®_00 = io_apic_read(0); + printk("... changing IO-APIC physical APIC ID to 2 ...\n"); + reg_00.ID = 0x2; + io_apic_write(0, *(int *)®_00); + + /* + * Sanity check + */ + *(int *)®_00 = io_apic_read(0); + if (reg_00.ID != 0x2) + panic("could not set ID"); +} + +static void construct_default_ISA_mptable (void) { - int i; + int i, pos=0; + + for (i=0; i<16; i++) { + if (!IO_APIC_IRQ(i)) + continue; + + mp_irqs[pos].mpc_irqtype = 0; + mp_irqs[pos].mpc_irqflag = 0; + mp_irqs[pos].mpc_srcbus = 0; + mp_irqs[pos].mpc_srcbusirq = i; + mp_irqs[pos].mpc_dstapic = 0; + mp_irqs[pos].mpc_dstirq = i; + pos++; + } + mp_irq_entries = pos; + mp_bus_id_to_type[0] = MP_BUS_ISA; + /* - * Map the IO APIC into kernel space + * MP specification 1.4 defines some extra rules for default + * configurations, fix them up here: */ + + switch (mpc_default_type) + { + case 2: + break; + default: + /* + * pin 2 is IRQ0: + */ + mp_irqs[0].mpc_dstirq = 2; + } - printk("mapping IO APIC from standard address.\n"); - io_apic_reg = ioremap_nocache(IO_APIC_BASE,4096); - printk("new virtual address: %p.\n",io_apic_reg); + setup_ioapic_id(); +} + +void setup_IO_APIC (void) +{ + int i; + + if (!pirqs_enabled) + for (i=0; i<MAX_PIRQS; i++) + pirq_entries[i]=-1; init_sym_mode(); { @@ -605,12 +673,6 @@ void setup_IO_APIC (void) for (i=0; i<nr_ioapic_registers; i++) clear_IO_APIC_irq (i); -#if DEBUG_1 - for (i=0; i<16; i++) - if (IO_APIC_IRQ(i)) - setup_IO_APIC_irq_ISA_default (i); -#endif - /* * the following IO-APIC's can be enabled: * @@ -634,7 +696,18 @@ void setup_IO_APIC (void) io_apic_irqs = 0; } + /* + * If there are no explicit mp irq entries: it's either one of the + * default configuration types or we are broken. In both cases it's + * fine to set up most of the low 16 IOAPIC pins to ISA defaults. + */ + if (!mp_irq_entries) { + printk("no explicit IRQ entries, using default mptable\n"); + construct_default_ISA_mptable(); + } + init_IO_APIC_traps(); + setup_IO_APIC_irqs (); if (!timer_irq_works ()) { @@ -644,9 +717,9 @@ void setup_IO_APIC (void) printk("..MP-BIOS bug: i8254 timer not connected to IO-APIC\n"); printk("..falling back to 8259A-based timer interrupt\n"); } - - printk("nr of MP irq sources: %d.\n", mp_irq_entries); - printk("nr of IOAPIC registers: %d.\n", nr_ioapic_registers); + + printk("nr of MP irq sources: %d.\n", mp_irq_entries); + printk("nr of IOAPIC registers: %d.\n", nr_ioapic_registers); print_IO_APIC(); } diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c index 44fd26530..19587312a 100644 --- a/arch/i386/kernel/ioport.c +++ b/arch/i386/kernel/ioport.c @@ -76,8 +76,6 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) return 0; } -unsigned int *stack; - /* * sys_iopl has to be used when you want to access the IO ports * beyond the 0x3ff range: to get the full 65536 ports bitmapped diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 95ce9fb14..2b8b86cc7 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -68,10 +68,6 @@ static unsigned int cached_irq_mask = (1<<NR_IRQS)-1; spinlock_t irq_controller_lock; -static unsigned int irq_events [NR_IRQS] = { -1, }; -static int disabled_irq [NR_IRQS] = { 0, }; -static int ipi_pending [NR_IRQS] = { 0, }; - /* * Not all IRQs can be routed through the IO-APIC, eg. on certain (older) * boards the timer interrupt and sometimes the keyboard interrupt is @@ -124,11 +120,34 @@ static struct hw_interrupt_type ioapic_irq_type = { }; #endif -struct hw_interrupt_type *irq_handles[NR_IRQS] = -{ - [0 ... 15] = &i8259A_irq_type /* standard ISA IRQs */ +/* + * Status: reason for being disabled: somebody has + * done a "disable_irq()" or we must not re-enter the + * already executing irq.. + */ +#define IRQ_INPROGRESS 1 +#define IRQ_DISABLED 2 + +/* + * This is the "IRQ descriptor", which contains various information + * about the irq, including what kind of hardware handling it has, + * whether it is disabled etc etc. + * + * Pad this out to 32 bytes for cache and indexing reasons. + */ +typedef struct { + unsigned int status; /* IRQ status - IRQ_INPROGRESS, IRQ_DISABLED */ + unsigned int events; /* Do we have any pending events? */ + unsigned int ipi; /* Have we sent off the pending IPI? */ + struct hw_interrupt_type *handler; /* handle/enable/disable functions */ + struct irqaction *action; /* IRQ action list */ + unsigned int unused[3]; +} irq_desc_t; + +irq_desc_t irq_desc[NR_IRQS] = { + [0 ... 15] = { 0, 0, 0, &i8259A_irq_type, }, /* standard ISA IRQs */ #ifdef __SMP__ - , [16 ... NR_IRQS-1] = &ioapic_irq_type /* 'high' PCI IRQs */ + [16 ... 23] = { 0, 0, 0, &ioapic_irq_type, }, /* 'high' PCI IRQs */ #endif }; @@ -175,6 +194,7 @@ void set_8259A_irq_mask(unsigned int irq) void unmask_generic_irq(unsigned int irq) { + irq_desc[irq].status = 0; if (IO_APIC_IRQ(irq)) enable_IO_APIC_irq(irq); else { @@ -241,6 +261,7 @@ BUILD_IRQ(23) BUILD_SMP_INTERRUPT(reschedule_interrupt) BUILD_SMP_INTERRUPT(invalidate_interrupt) BUILD_SMP_INTERRUPT(stop_cpu_interrupt) +BUILD_SMP_INTERRUPT(mtrr_interrupt) /* * every pentium local APIC has two 'local interrupts', with a @@ -297,17 +318,6 @@ static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL }; */ static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL}; -static struct irqaction *irq_action[NR_IRQS] = { - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -#ifdef __SMP__ - ,NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -#endif -}; - int get_irq_list(char *buf) { int i, j; @@ -320,7 +330,7 @@ int get_irq_list(char *buf) *p++ = '\n'; for (i = 0 ; i < NR_IRQS ; i++) { - action = irq_action[i]; + action = irq_desc[i].action; if (!action) continue; p += sprintf(p, "%3d: ",i); @@ -335,7 +345,7 @@ int get_irq_list(char *buf) if (IO_APIC_IRQ(i)) p += sprintf(p, " IO-APIC "); else - p += sprintf(p, " XT PIC "); + p += sprintf(p, " XT-PIC "); p += sprintf(p, " %s", action->name); for (action=action->next; action; action = action->next) { @@ -535,20 +545,31 @@ static inline void get_irqlock(int cpu) global_irq_holder = cpu; } +#define EFLAGS_IF_SHIFT 9 + /* * A global "cli()" while in an interrupt context * turns into just a local cli(). Interrupts * should use spinlocks for the (very unlikely) * case that they ever want to protect against * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). */ void __global_cli(void) { - int cpu = smp_processor_id(); + unsigned int flags; - __cli(); - if (!local_irq_count[cpu]) - get_irqlock(cpu); + __save_flags(flags); + if (flags & (1 << EFLAGS_IF_SHIFT)) { + int cpu = smp_processor_id(); + __cli(); + if (!local_irq_count[cpu]) + get_irqlock(cpu); + } } void __global_sti(void) @@ -560,33 +581,53 @@ void __global_sti(void) __sti(); } +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ unsigned long __global_save_flags(void) { - if (!local_irq_count[smp_processor_id()]) - return global_irq_holder == (unsigned char) smp_processor_id(); - else { - unsigned long x; - __save_flags(x); - return x; + int retval; + int local_enabled; + unsigned long flags; + + __save_flags(flags); + local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!local_irq_count[smp_processor_id()]) { + if (local_enabled) + retval = 1; + if (global_irq_holder == (unsigned char) smp_processor_id()) + retval = 0; } + return retval; } void __global_restore_flags(unsigned long flags) { - if (!local_irq_count[smp_processor_id()]) { - switch (flags) { - case 0: - __global_sti(); - break; - case 1: - __global_cli(); - break; - default: - printk("global_restore_flags: %08lx (%08lx)\n", - flags, (&flags)[-1]); - } - } else - __restore_flags(flags); + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + } } #endif @@ -597,7 +638,7 @@ static int handle_IRQ_event(unsigned int irq, struct pt_regs * regs) int status; status = 0; - action = *(irq + irq_action); + action = irq_desc[irq].action; if (action) { status |= 1; @@ -618,125 +659,26 @@ static int handle_IRQ_event(unsigned int irq, struct pt_regs * regs) return status; } - -void disable_irq(unsigned int irq) -{ - unsigned long flags; - - spin_lock_irqsave(&irq_controller_lock, flags); - irq_handles[irq]->disable(irq); - spin_unlock_irqrestore(&irq_controller_lock, flags); - - synchronize_irq(); -} - /* * disable/enable_irq() wait for all irq contexts to finish * executing. Also it's recursive. */ static void disable_8259A_irq(unsigned int irq) { - disabled_irq[irq]++; cached_irq_mask |= 1 << irq; set_8259A_irq_mask(irq); } -#ifdef __SMP__ -static void disable_ioapic_irq(unsigned int irq) -{ - disabled_irq[irq]++; - /* - * We do not disable IO-APIC irqs in hardware ... - */ -} -#endif - void enable_8259A_irq (unsigned int irq) { - unsigned long flags; - spin_lock_irqsave(&irq_controller_lock, flags); - if (disabled_irq[irq]) - disabled_irq[irq]--; - else { - spin_unlock_irqrestore(&irq_controller_lock, flags); - return; - } cached_irq_mask &= ~(1 << irq); set_8259A_irq_mask(irq); - spin_unlock_irqrestore(&irq_controller_lock, flags); -} - -#ifdef __SMP__ -void enable_ioapic_irq (unsigned int irq) -{ - unsigned long flags, should_handle_irq; - int cpu = smp_processor_id(); - - spin_lock_irqsave(&irq_controller_lock, flags); - if (disabled_irq[irq]) - disabled_irq[irq]--; - else { - spin_unlock_irqrestore(&irq_controller_lock, flags); - return; - } -#if 0 - /* - * In the SMP+IOAPIC case it might happen that there are an unspecified - * number of pending IRQ events unhandled. These cases are very rare, - * so we 'resend' these IRQs via IPIs, to the same CPU. It's much - * better to do it this way as thus we dont have to be aware of - * 'pending' interrupts in the IRQ path, except at this point. - */ - if (!disabled_irq[irq] && irq_events[irq]) { - if (!ipi_pending[irq]) { - ipi_pending[irq] = 1; - --irq_events[irq]; - send_IPI(cpu,IO_APIC_VECTOR(irq)); - } - } - spin_unlock_irqrestore(&irq_controller_lock, flags); -#else - if (!disabled_irq[irq] && irq_events[irq]) { - struct pt_regs regs; /* FIXME: these are fake currently */ - - disabled_irq[irq]++; - hardirq_enter(cpu); - spin_unlock(&irq_controller_lock); - - release_irqlock(cpu); - while (test_bit(0,&global_irq_lock)) mb(); -again: - handle_IRQ_event(irq, ®s); - - spin_lock(&irq_controller_lock); - disabled_irq[irq]--; - should_handle_irq=0; - if (--irq_events[irq] && !disabled_irq[irq]) { - should_handle_irq=1; - disabled_irq[irq]++; - } - spin_unlock(&irq_controller_lock); - - if (should_handle_irq) - goto again; - - irq_exit(cpu, irq); - __restore_flags(flags); - } else - spin_unlock_irqrestore(&irq_controller_lock, flags); -#endif -} -#endif - -void enable_irq(unsigned int irq) -{ - irq_handles[irq]->enable(irq); } void make_8259A_irq (unsigned int irq) { io_apic_irqs &= ~(1<<irq); - irq_handles[irq] = &i8259A_irq_type; + irq_desc[irq].handler = &i8259A_irq_type; disable_irq(irq); enable_irq(irq); } @@ -750,6 +692,7 @@ void make_8259A_irq (unsigned int irq) static inline void mask_and_ack_8259A(unsigned int irq) { spin_lock(&irq_controller_lock); + irq_desc[irq].status |= IRQ_INPROGRESS; cached_irq_mask |= 1 << irq; if (irq & 8) { inb(0xA1); /* DUMMY */ @@ -772,7 +715,8 @@ static void do_8259A_IRQ(unsigned int irq, int cpu, struct pt_regs * regs) if (handle_IRQ_event(irq, regs)) { spin_lock(&irq_controller_lock); - unmask_8259A(irq); + if (!(irq_desc[irq].status &= IRQ_DISABLED)) + unmask_8259A(irq); spin_unlock(&irq_controller_lock); } @@ -780,41 +724,119 @@ static void do_8259A_IRQ(unsigned int irq, int cpu, struct pt_regs * regs) } #ifdef __SMP__ + +/* + * In the SMP+IOAPIC case it might happen that there are an unspecified + * number of pending IRQ events unhandled. These cases are very rare, + * so we 'resend' these IRQs via IPIs, to the same CPU. It's much + * better to do it this way as thus we dont have to be aware of + * 'pending' interrupts in the IRQ path, except at this point. + */ +static void enable_ioapic_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + if (desc->events && !desc->ipi) { + desc->ipi = 1; + send_IPI(APIC_DEST_SELF, IO_APIC_VECTOR(irq)); + } +} + +/* + * We do not actually disable IO-APIC irqs in hardware ... + */ +static void disable_ioapic_irq(unsigned int irq) +{ +} + static void do_ioapic_IRQ(unsigned int irq, int cpu, struct pt_regs * regs) { - int should_handle_irq = 0; + irq_desc_t *desc = irq_desc + irq; + + spin_lock(&irq_controller_lock); + /* Ack the irq inside the lock! */ ack_APIC_irq(); + desc->ipi = 0; - spin_lock(&irq_controller_lock); - if (ipi_pending[irq]) - ipi_pending[irq] = 0; + /* If the irq is disabled for whatever reason, just set a flag and return */ + if (desc->status & (IRQ_DISABLED | IRQ_INPROGRESS)) { + desc->events = 1; + spin_unlock(&irq_controller_lock); + return; + } - if (!irq_events[irq]++ && !disabled_irq[irq]) - should_handle_irq = 1; + desc->status = IRQ_INPROGRESS; + desc->events = 0; hardirq_enter(cpu); spin_unlock(&irq_controller_lock); - if (should_handle_irq) { - while (test_bit(0,&global_irq_lock)) mb(); -again: - handle_IRQ_event(irq, regs); + while (test_bit(0,&global_irq_lock)) barrier(); + + for (;;) { + int pending; + + /* If there is no IRQ handler, exit early, leaving the irq "in progress" */ + if (!handle_IRQ_event(irq, regs)) + goto no_handler; spin_lock(&irq_controller_lock); - should_handle_irq=0; - if (--irq_events[irq] && !disabled_irq[irq]) - should_handle_irq=1; + pending = desc->events; + desc->events = 0; + if (!pending) + break; spin_unlock(&irq_controller_lock); - - if (should_handle_irq) - goto again; } + desc->status &= IRQ_DISABLED; + spin_unlock(&irq_controller_lock); +no_handler: hardirq_exit(cpu); release_irqlock(cpu); } + #endif + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ +void disable_irq(unsigned int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_controller_lock, flags); + /* + * At this point we may actually have a pending interrupt being active + * on another CPU. So don't touch the IRQ_INPROGRESS bit.. + */ + irq_desc[irq].status |= IRQ_DISABLED; + irq_desc[irq].handler->disable(irq); + spin_unlock_irqrestore(&irq_controller_lock, flags); + + synchronize_irq(); +} + +void enable_irq(unsigned int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_controller_lock, flags); + /* + * In contrast to the above, we should _not_ have any concurrent + * interrupt activity here, so we just clear both disabled bits. + * + * This allows us to have IRQ_INPROGRESS set until we actually + * install a handler for this interrupt (make irq autodetection + * work by just looking at the status field for the irq) + */ + irq_desc[irq].status = 0; + irq_desc[irq].handler->enable(irq); + spin_unlock_irqrestore(&irq_controller_lock, flags); +} + /* * do_IRQ handles all normal device IRQ's (the special * SMP cross-CPU interrupts have their own specific @@ -845,7 +867,7 @@ asmlinkage void do_IRQ(struct pt_regs regs) int cpu = smp_processor_id(); kstat.irqs[cpu][irq]++; - irq_handles[irq]->handle(irq, cpu, ®s); + irq_desc[irq].handler->handle(irq, cpu, ®s); /* * This should be conditional: we should really get @@ -865,7 +887,7 @@ int setup_x86_irq(unsigned int irq, struct irqaction * new) struct irqaction *old, **p; unsigned long flags; - p = irq_action + irq; + p = &irq_desc[irq].action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) @@ -890,7 +912,7 @@ int setup_x86_irq(unsigned int irq, struct irqaction * new) spin_lock(&irq_controller_lock); #ifdef __SMP__ if (IO_APIC_IRQ(irq)) { - irq_handles[irq] = &ioapic_irq_type; + irq_desc[irq].handler = &ioapic_irq_type; /* * First disable it in the 8259A: */ @@ -948,7 +970,7 @@ void free_irq(unsigned int irq, void *dev_id) printk("Trying to free IRQ%d\n",irq); return; } - for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { + for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) { if (action->dev_id != dev_id) continue; @@ -964,32 +986,29 @@ void free_irq(unsigned int irq, void *dev_id) } /* - * probing is always single threaded [FIXME: is this true?] + * IRQ autodetection code.. + * + * This depends on the fact that any interrupt that + * comes in on to an unassigned handler will get stuck + * with "IRQ_INPROGRESS" asserted and the interrupt + * disabled. */ -static unsigned int probe_irqs[NR_CPUS][NR_IRQS]; - unsigned long probe_irq_on (void) { - unsigned int i, j, irqs = 0; + unsigned int i, irqs = 0; unsigned long delay; /* - * save current irq counts - */ - memcpy(probe_irqs,kstat.irqs,NR_CPUS*NR_IRQS*sizeof(int)); - - /* * first, enable any unassigned irqs */ + spin_lock_irq(&irq_controller_lock); for (i = NR_IRQS-1; i > 0; i--) { - if (!irq_action[i]) { - unsigned long flags; - spin_lock_irqsave(&irq_controller_lock, flags); + if (!irq_desc[i].action) { unmask_generic_irq(i); irqs |= (1 << i); - spin_unlock_irqrestore(&irq_controller_lock, flags); } } + spin_unlock_irq(&irq_controller_lock); /* * wait for spurious interrupts to increase counters @@ -1000,35 +1019,35 @@ unsigned long probe_irq_on (void) /* * now filter out any obviously spurious interrupts */ - for (i=0; i<NR_IRQS; i++) - for (j=0; j<NR_CPUS; j++) - if (kstat.irqs[j][i] != probe_irqs[j][i]) - irqs &= ~(1UL << i); + spin_lock_irq(&irq_controller_lock); + for (i=0; i<NR_IRQS; i++) { + if (irq_desc[i].status & IRQ_INPROGRESS) + irqs &= ~(1UL << i); + } + spin_unlock_irq(&irq_controller_lock); return irqs; } int probe_irq_off (unsigned long irqs) { - int i,j, irq_found = -1; + int i, irq_found = -1; + spin_lock_irq(&irq_controller_lock); for (i=0; i<NR_IRQS; i++) { - int sum = 0; - for (j=0; j<NR_CPUS; j++) { - sum += kstat.irqs[j][i]; - sum -= probe_irqs[j][i]; - } - if (sum && (irqs & (1UL << i))) { + if ((irqs & 1) && (irq_desc[i].status & IRQ_INPROGRESS)) { if (irq_found != -1) { irq_found = -irq_found; goto out; - } else - irq_found = i; + } + irq_found = i; } + irqs >>= 1; } if (irq_found == -1) irq_found = 0; out: + spin_unlock_irq(&irq_controller_lock); return irq_found; } @@ -1050,7 +1069,7 @@ void init_IO_APIC_traps(void) for (i = 0; i < NR_IRQS ; i++) if (IO_APIC_VECTOR(i) <= 0xfe) /* HACK */ { if (IO_APIC_IRQ(i)) { - irq_handles[i] = &ioapic_irq_type; + irq_desc[i].handler = &ioapic_irq_type; /* * First disable it in the 8259A: */ @@ -1071,10 +1090,9 @@ __initfunc(void init_IRQ(void)) outb_p(LATCH & 0xff , 0x40); /* LSB */ outb(LATCH >> 8 , 0x40); /* MSB */ - printk("INIT IRQ\n"); for (i=0; i<NR_IRQS; i++) { - irq_events[i] = 0; - disabled_irq[i] = 0; + irq_desc[i].events = 0; + irq_desc[i].status = 0; } /* * 16 old-style INTA-cycle interrupt gates: @@ -1110,6 +1128,9 @@ __initfunc(void init_IRQ(void)) /* self generated IPI for local APIC timer */ set_intr_gate(0x41, apic_timer_interrupt); + /* IPI for MTRR control */ + set_intr_gate(0x50, mtrr_interrupt); + #endif request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); diff --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h index 9824026dc..81795c85c 100644 --- a/arch/i386/kernel/irq.h +++ b/arch/i386/kernel/irq.h @@ -23,10 +23,7 @@ void init_pic_mode (void); extern unsigned int io_apic_irqs; -extern inline int IO_APIC_VECTOR (int irq) -{ - return (0x51+(irq<<3)); -} +#define IO_APIC_VECTOR(irq) (0x51+((irq)<<3)) #define MAX_IRQ_SOURCES 128 #define MAX_MP_BUSSES 32 @@ -83,8 +80,8 @@ static inline void irq_exit(int cpu, unsigned int irq) #define SAVE_ALL \ "cld\n\t" \ - "push %es\n\t" \ - "push %ds\n\t" \ + "pushl %es\n\t" \ + "pushl %ds\n\t" \ "pushl %eax\n\t" \ "pushl %ebp\n\t" \ "pushl %edi\n\t" \ @@ -93,8 +90,8 @@ static inline void irq_exit(int cpu, unsigned int irq) "pushl %ecx\n\t" \ "pushl %ebx\n\t" \ "movl $" STR(__KERNEL_DS) ",%edx\n\t" \ - "mov %dx,%ds\n\t" \ - "mov %dx,%es\n\t" + "movl %dx,%ds\n\t" \ + "movl %dx,%es\n\t" #define IRQ_NAME2(nr) nr##_interrupt(void) #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr) diff --git a/arch/i386/kernel/ldt.c b/arch/i386/kernel/ldt.c index 65c743195..64d4ab153 100644 --- a/arch/i386/kernel/ldt.c +++ b/arch/i386/kernel/ldt.c @@ -18,7 +18,7 @@ static int read_ldt(void * ptr, unsigned long bytecount) { - void * address = current->ldt; + void * address = current->mm->segments; unsigned long size; if (!ptr) @@ -37,6 +37,7 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode) { struct modify_ldt_ldt_s ldt_info; unsigned long *lp; + struct mm_struct * mm; int error, i; if (bytecount != sizeof(ldt_info)) @@ -48,19 +49,32 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode) if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES) return -EINVAL; - if (!current->ldt) { + mm = current->mm; + + /* + * Horrible dependencies! Try to get rid of this. This is wrong, + * as it only reloads the ldt for the first process with this + * mm. The implications are that you should really make sure that + * you have a ldt before you do the first clone(), otherwise + * you get strange behaviour (the kernel is safe, it's just user + * space strangeness). + * + * For no good reason except historical, the GDT index of the LDT + * is chosen to follow the index number in the task[] array. + */ + if (!mm->segments) { for (i=1 ; i<NR_TASKS ; i++) { if (task[i] == current) { - if (!(current->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE))) + if (!(mm->segments = (void *) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE))) return -ENOMEM; - memset(current->ldt, 0, LDT_ENTRIES*LDT_ENTRY_SIZE); - set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, current->ldt, LDT_ENTRIES); + memset(mm->segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE); + set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, mm->segments, LDT_ENTRIES); load_ldt(i); } } } - lp = (unsigned long *) ¤t->ldt[ldt_info.entry_number]; + lp = (unsigned long *) (LDT_ENTRY_SIZE * ldt_info.entry_number + (unsigned long) mm->segments); /* Allow LDTs to be cleared by the user. */ if (ldt_info.base_addr == 0 && ldt_info.limit == 0 && (oldmode || diff --git a/arch/i386/kernel/mca.c b/arch/i386/kernel/mca.c index de6de8f14..ae67822bc 100644 --- a/arch/i386/kernel/mca.c +++ b/arch/i386/kernel/mca.c @@ -62,7 +62,7 @@ static struct MCA_info* mca_info = 0; /*--------------------------------------------------------------------*/ #ifdef CONFIG_PROC_FS -static long mca_do_proc_init( long memory_start, long memory_end ); +static void mca_do_proc_init( void ); static int mca_default_procfn( char* buf, int slot ); static ssize_t proc_mca_read( struct file*, char*, size_t, loff_t *); @@ -79,7 +79,7 @@ static struct inode_operations proc_mca_inode_operations = { /*--------------------------------------------------------------------*/ -__initfunc(long mca_init(long memory_start, long memory_end)) +__initfunc(void mca_init(void)) { unsigned int i, j; int foundscsi = 0; @@ -96,21 +96,14 @@ __initfunc(long mca_init(long memory_start, long memory_end)) */ if (!MCA_bus) - return memory_start; + return; cli(); /* * Allocate MCA_info structure (at address divisible by 8) */ - if( ((memory_start+7)&(~7)) > memory_end ) - { - /* uh oh */ - return memory_start; - } - - mca_info = (struct MCA_info*) ((memory_start+7)&(~7)); - memory_start = ((long)mca_info) + sizeof(struct MCA_info); + mca_info = kmalloc(sizeof(struct MCA_info), GFP_ATOMIC); /* * Make sure adapter setup is off @@ -194,10 +187,8 @@ __initfunc(long mca_init(long memory_start, long memory_end)) request_region(0x100,0x08,"POS (MCA)"); #ifdef CONFIG_PROC_FS - memory_start = mca_do_proc_init( memory_start, memory_end ); + mca_do_proc_init(); #endif - - return memory_start; } /*--------------------------------------------------------------------*/ @@ -418,12 +409,12 @@ int get_mca_info(char *buf) /*--------------------------------------------------------------------*/ -__initfunc(long mca_do_proc_init( long memory_start, long memory_end )) +__initfunc(void mca_do_proc_init( void )) { int i = 0; struct proc_dir_entry* node = 0; - if( mca_info == 0 ) return memory_start; /* never happens */ + if( mca_info == 0 ) return; /* never happens */ proc_register( &proc_mca, &(struct proc_dir_entry) { PROC_MCA_REGISTERS, 3, "pos", S_IFREG|S_IRUGO, @@ -439,11 +430,7 @@ __initfunc(long mca_do_proc_init( long memory_start, long memory_end )) mca_info->slot[i].dev = 0; if( ! mca_isadapter( i ) ) continue; - if( memory_start + sizeof(struct proc_dir_entry) > memory_end ) { - continue; - } - node = (struct proc_dir_entry*) memory_start; - memory_start += sizeof(struct proc_dir_entry); + node = kmalloc(sizeof(struct proc_dir_entry), GFP_ATOMIC); if( i < MCA_MAX_SLOT_NR ) { node->low_ino = PROC_MCA_SLOT + i; @@ -464,7 +451,6 @@ __initfunc(long mca_do_proc_init( long memory_start, long memory_end )) proc_register( &proc_mca, node ); } - return memory_start; } /* mca_do_proc_init() */ /*--------------------------------------------------------------------*/ diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c new file mode 100644 index 000000000..f2981c5cf --- /dev/null +++ b/arch/i386/kernel/mtrr.c @@ -0,0 +1,1229 @@ +/* Generic MTRR (Memory Type Range Register) driver. + + Copyright (C) 1997-1998 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + Source: "Pentium Pro Family Developer's Manual, Volume 3: + Operating System Writer's Guide" (Intel document number 242692), + section 11.11.7 + + ChangeLog + + Prehistory Martin Tischhäuser <martin@ikcbarka.fzk.de> + Initial register-setting code (from proform-1.0). + 19971216 Richard Gooch <rgooch@atnf.csiro.au> + Original version for /proc/mtrr interface, SMP-safe. + v1.0 + 19971217 Richard Gooch <rgooch@atnf.csiro.au> + Bug fix for ioctls()'s. + Added sample code in Documentation/mtrr.txt + v1.1 + 19971218 Richard Gooch <rgooch@atnf.csiro.au> + Disallow overlapping regions. + 19971219 Jens Maurer <jmaurer@menuett.rhein-main.de> + Register-setting fixups. + v1.2 + 19971222 Richard Gooch <rgooch@atnf.csiro.au> + Fixups for kernel 2.1.75. + v1.3 + 19971229 David Wragg <dpw@doc.ic.ac.uk> + Register-setting fixups and conformity with Intel conventions. + 19971229 Richard Gooch <rgooch@atnf.csiro.au> + Cosmetic changes and wrote this ChangeLog ;-) + 19980106 Richard Gooch <rgooch@atnf.csiro.au> + Fixups for kernel 2.1.78. + v1.4 + 19980119 David Wragg <dpw@doc.ic.ac.uk> + Included passive-release enable code (elsewhere in PCI setup). + v1.5 + 19980131 Richard Gooch <rgooch@atnf.csiro.au> + Replaced global kernel lock with private spinlock. + v1.6 + 19980201 Richard Gooch <rgooch@atnf.csiro.au> + Added wait for other CPUs to complete changes. + v1.7 + 19980202 Richard Gooch <rgooch@atnf.csiro.au> + Bug fix in definition of <set_mtrr> for UP. + v1.8 + 19980319 Richard Gooch <rgooch@atnf.csiro.au> + Fixups for kernel 2.1.90. + 19980323 Richard Gooch <rgooch@atnf.csiro.au> + Move SMP BIOS fixup before secondary CPUs call <calibrate_delay> + v1.9 + 19980325 Richard Gooch <rgooch@atnf.csiro.au> + Fixed test for overlapping regions: confused by adjacent regions + 19980326 Richard Gooch <rgooch@atnf.csiro.au> + Added wbinvd in <set_mtrr_prepare>. + 19980401 Richard Gooch <rgooch@atnf.csiro.au> + Bug fix for non-SMP compilation. + 19980418 David Wragg <dpw@doc.ic.ac.uk> + Fixed-MTRR synchronisation for SMP and use atomic operations + instead of spinlocks. + 19980418 Richard Gooch <rgooch@atnf.csiro.au> + Differentiate different MTRR register classes for BIOS fixup. + v1.10 + 19980419 David Wragg <dpw@doc.ic.ac.uk> + Bug fix in variable MTRR synchronisation. + v1.11 + 19980419 Richard Gooch <rgooch@atnf.csiro.au> + Fixups for kernel 2.1.97. + v1.12 + 19980421 Richard Gooch <rgooch@atnf.csiro.au> + Safer synchronisation across CPUs when changing MTRRs. + v1.13 + 19980423 Richard Gooch <rgooch@atnf.csiro.au> + Bugfix for SMP systems without MTRR support. + v1.14 + 19980427 Richard Gooch <rgooch@atnf.csiro.au> + Trap calls to <mtrr_add> and <mtrr_del> on non-MTRR machines. + v1.15 + 19980427 Richard Gooch <rgooch@atnf.csiro.au> + Use atomic bitops for setting SMP change mask. + v1.16 + 19980428 Richard Gooch <rgooch@atnf.csiro.au> + Removed spurious diagnostic message. + v1.17 + 19980429 Richard Gooch <rgooch@atnf.csiro.au> + Moved register-setting macros into this file. + Moved setup code from init/main.c to i386-specific areas. + v1.18 +*/ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/timer.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/string.h> +#include <linux/malloc.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/ctype.h> +#include <linux/proc_fs.h> +#include <linux/mm.h> +#include <linux/module.h> +#define MTRR_NEED_STRINGS +#include <asm/mtrr.h> +#include <linux/init.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/segment.h> +#include <asm/bitops.h> +#include <asm/smp_lock.h> +#include <asm/atomic.h> +#include <linux/smp.h> + +#define MTRR_VERSION "1.18 (19980429)" + +#define TRUE 1 +#define FALSE 0 + +#define X86_FEATURE_MTRR 0x1000 /* memory type registers */ + +#define MTRRcap_MSR 0x0fe +#define MTRRdefType_MSR 0x2ff + +#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) +#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) + +#define NUM_FIXED_RANGES 88 +#define MTRRfix64K_00000_MSR 0x250 +#define MTRRfix16K_80000_MSR 0x258 +#define MTRRfix16K_A0000_MSR 0x259 +#define MTRRfix4K_C0000_MSR 0x268 +#define MTRRfix4K_C8000_MSR 0x269 +#define MTRRfix4K_D0000_MSR 0x26a +#define MTRRfix4K_D8000_MSR 0x26b +#define MTRRfix4K_E0000_MSR 0x26c +#define MTRRfix4K_E8000_MSR 0x26d +#define MTRRfix4K_F0000_MSR 0x26e +#define MTRRfix4K_F8000_MSR 0x26f + +#ifdef __SMP__ +# define MTRR_CHANGE_MASK_FIXED 0x01 +# define MTRR_CHANGE_MASK_VARIABLE 0x02 +# define MTRR_CHANGE_MASK_DEFTYPE 0x04 +#endif + +/* In the processor's MTRR interface, the MTRR type is always held in + an 8 bit field: */ +typedef u8 mtrr_type; + +#define LINE_SIZE 80 +#define JIFFIE_TIMEOUT 100 + +#ifdef __SMP__ +# define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type) +#else +# define set_mtrr(reg,base,size,type) set_mtrr_up (reg, base, size, type,TRUE) +#endif + +#ifndef CONFIG_PROC_FS +# define compute_ascii() while (0) +#endif + +#ifdef CONFIG_PROC_FS +static char *ascii_buffer = NULL; +static unsigned int ascii_buf_bytes = 0; +#endif +static unsigned int *usage_table = NULL; +#ifdef __SMP__ +static spinlock_t main_lock = SPIN_LOCK_UNLOCKED; +#endif + +/* Private functions */ +#ifdef CONFIG_PROC_FS +static void compute_ascii (void); +#endif + + +struct set_mtrr_context +{ + unsigned long flags; + unsigned long deftype_lo; + unsigned long deftype_hi; + unsigned long cr4val; +}; + +/* + * Access to machine-specific registers (available on 586 and better only) + * Note: the rd* operations modify the parameters directly (without using + * pointer indirection), this allows gcc to optimize better + */ +#define rdmsr(msr,val1,val2) \ + __asm__ __volatile__("rdmsr" \ + : "=a" (val1), "=d" (val2) \ + : "c" (msr)) + +#define wrmsr(msr,val1,val2) \ + __asm__ __volatile__("wrmsr" \ + : /* no outputs */ \ + : "c" (msr), "a" (val1), "d" (val2)) + +#define rdtsc(low,high) \ + __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) + +#define rdpmc(counter,low,high) \ + __asm__ __volatile__("rdpmc" \ + : "=a" (low), "=d" (high) \ + : "c" (counter)) + + +/* Put the processor into a state where MTRRs can be safely set. */ +static void set_mtrr_prepare(struct set_mtrr_context *ctxt) +{ + unsigned long tmp; + + /* disable interrupts */ + save_flags(ctxt->flags); cli(); + + /* save value of CR4 and clear Page Global Enable (bit 7) */ + asm volatile ("movl %%cr4, %0\n\t" + "movl %0, %1\n\t" + "andb $0x7f, %b1\n\t" + "movl %1, %%cr4\n\t" + : "=r" (ctxt->cr4val), "=q" (tmp) : : "memory"); + + /* disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect. */ + asm volatile ("movl %%cr0, %0\n\t" + "orl $0x40000000, %0\n\t" + "wbinvd\n\t" + "movl %0, %%cr0\n\t" + "wbinvd\n\t" + : "=r" (tmp) : : "memory"); + + /* disable MTRRs, and set the default type to uncached. */ + rdmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + wrmsr(MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); +} /* End Function set_mtrr_prepare */ + + +/* Restore the processor after a set_mtrr_prepare */ +static void set_mtrr_done(struct set_mtrr_context *ctxt) +{ + unsigned long tmp; + + /* flush caches and TLBs */ + asm volatile ("wbinvd" : : : "memory" ); + + /* restore MTRRdefType */ + wrmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + + /* enable caches */ + asm volatile ("movl %%cr0, %0\n\t" + "andl $0xbfffffff, %0\n\t" + "movl %0, %%cr0\n\t" + : "=r" (tmp) : : "memory"); + + /* restore value of CR4 */ + asm volatile ("movl %0, %%cr4" + : : "r" (ctxt->cr4val) : "memory"); + + /* re-enable interrupts (if enabled previously) */ + restore_flags(ctxt->flags); +} /* End Function set_mtrr_done */ + + +/* this function returns the number of variable MTRRs */ +static unsigned int get_num_var_ranges (void) +{ + unsigned long config, dummy; + + rdmsr(MTRRcap_MSR, config, dummy); + return (config & 0xff); +} /* End Function get_num_var_ranges */ + + +/* non-zero if we have the write-combining memory type. */ +static int have_wrcomb (void) +{ + unsigned long config, dummy; + + rdmsr(MTRRcap_MSR, config, dummy); + return (config & (1<<10)); +} + + +static void get_mtrr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long dummy, mask_lo, base_lo; + + rdmsr(MTRRphysMask_MSR(reg), mask_lo, dummy); + if ((mask_lo & 0x800) == 0) { + /* Invalid (i.e. free) range. */ + *base = 0; + *size = 0; + *type = 0; + return; + } + + rdmsr(MTRRphysBase_MSR(reg), base_lo, dummy); + + /* We ignore the extra address bits (32-35). If someone wants to + run x86 Linux on a machine with >4GB memory, this will be the + least of their problems. */ + + /* Clean up mask_lo so it gives the real address mask. */ + mask_lo = (mask_lo & 0xfffff000UL); + + /* This works correctly if size is a power of two, i.e. a + contiguous range. */ + *size = ~(mask_lo - 1); + + *base = (base_lo & 0xfffff000UL); + *type = (base_lo & 0xff); +} /* End Function get_mtrr */ + + +static void set_mtrr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +/* [SUMMARY] Set variable MTRR register on the local CPU. + <reg> The register to set. + <base> The base address of the region. + <size> The size of the region. If this is 0 the region is disabled. + <type> The type of the region. + <do_safe> If TRUE, do the change safely. If FALSE, safety measures should + be done externally. +*/ +{ + struct set_mtrr_context ctxt; + + if (do_safe) set_mtrr_prepare (&ctxt); + if (size == 0) + { + /* The invalid bit is kept in the mask, so we simply clear the + relevant mask register to disable a range. */ + wrmsr (MTRRphysMask_MSR (reg), 0, 0); + } + else + { + wrmsr (MTRRphysBase_MSR (reg), base | type, 0); + wrmsr (MTRRphysMask_MSR (reg), ~(size - 1) | 0x800, 0); + } + if (do_safe) set_mtrr_done (&ctxt); +} /* End Function set_mtrr_up */ + + +#ifdef __SMP__ + +struct mtrr_var_range +{ + unsigned long base_lo; + unsigned long base_hi; + unsigned long mask_lo; + unsigned long mask_hi; +}; + + +/* Get the MSR pair relating to a var range. */ +__initfunc(static void get_mtrr_var_range (unsigned int index, + struct mtrr_var_range *vr)) +{ + rdmsr (MTRRphysBase_MSR (index), vr->base_lo, vr->base_hi); + rdmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi); +} /* End Function get_mtrr_var_range */ + + +/* Set the MSR pair relating to a var range. Returns TRUE if + changes are made. */ +__initfunc(static int set_mtrr_var_range_testing (unsigned int index, + struct mtrr_var_range *vr)) +{ + unsigned int lo, hi; + int changed = FALSE; + + rdmsr(MTRRphysBase_MSR(index), lo, hi); + + if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) + || (vr->base_hi & 0xfUL) != (hi & 0xfUL)) { + wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); + changed = TRUE; + } + + rdmsr(MTRRphysMask_MSR(index), lo, hi); + + if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL) + || (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) { + wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); + changed = TRUE; + } + + return changed; +} + + +__initfunc(static void get_fixed_ranges(mtrr_type *frs)) +{ + unsigned long *p = (unsigned long *)frs; + int i; + + rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]); + + for (i = 0; i < 2; i++) + rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); + + for (i = 0; i < 8; i++) + rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); +} + + +__initfunc(static int set_fixed_ranges_testing(mtrr_type *frs)) +{ + unsigned long *p = (unsigned long *)frs; + int changed = FALSE; + int i; + unsigned long lo, hi; + + rdmsr(MTRRfix64K_00000_MSR, lo, hi); + if (p[0] != lo || p[1] != hi) { + wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]); + changed = TRUE; + } + + for (i = 0; i < 2; i++) { + rdmsr(MTRRfix16K_80000_MSR + i, lo, hi); + if (p[2 + i*2] != lo || p[3 + i*2] != hi) { + wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); + changed = TRUE; + } + } + + for (i = 0; i < 8; i++) { + rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi); + if (p[6 + i*2] != lo || p[7 + i*2] != hi) { + wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); + changed = TRUE; + } + } + + return changed; +} + + +struct mtrr_state +{ + unsigned int num_var_ranges; + struct mtrr_var_range *var_ranges; + mtrr_type fixed_ranges[NUM_FIXED_RANGES]; + unsigned char enabled; + mtrr_type def_type; +}; + + +/* Grab all of the mtrr state for this cpu into *state. */ +__initfunc(static void get_mtrr_state(struct mtrr_state *state)) +{ + unsigned int nvrs, i; + struct mtrr_var_range *vrs; + unsigned long lo, dummy; + + nvrs = state->num_var_ranges = get_num_var_ranges(); + vrs = state->var_ranges + = kmalloc(nvrs * sizeof(struct mtrr_var_range), GFP_KERNEL); + if (vrs == NULL) + nvrs = state->num_var_ranges = 0; + + for (i = 0; i < nvrs; i++) + get_mtrr_var_range(i, &vrs[i]); + + get_fixed_ranges(state->fixed_ranges); + + rdmsr(MTRRdefType_MSR, lo, dummy); + state->def_type = (lo & 0xff); + state->enabled = (lo & 0xc00) >> 10; +} /* End Function get_mtrr_state */ + + +/* Free resources associated with a struct mtrr_state */ +__initfunc(static void finalize_mtrr_state(struct mtrr_state *state)) +{ + if (state->var_ranges) kfree (state->var_ranges); +} /* End Function finalize_mtrr_state */ + + +__initfunc(static unsigned long set_mtrr_state (struct mtrr_state *state, + struct set_mtrr_context *ctxt)) +/* [SUMMARY] Set the MTRR state for this CPU. + <state> The MTRR state information to read. + <ctxt> Some relevant CPU context. + [NOTE] The CPU must already be in a safe state for MTRR changes. + [RETURNS] 0 if no changes made, else a mask indication what was changed. +*/ +{ + unsigned int i; + unsigned long change_mask = 0; + + for (i = 0; i < state->num_var_ranges; i++) + if (set_mtrr_var_range_testing(i, &state->var_ranges[i])) + change_mask |= MTRR_CHANGE_MASK_VARIABLE; + + if (set_fixed_ranges_testing(state->fixed_ranges)) + change_mask |= MTRR_CHANGE_MASK_FIXED; + + /* set_mtrr_restore restores the old value of MTRRdefType, + so to set it we fiddle with the saved value. */ + if ((ctxt->deftype_lo & 0xff) != state->def_type + || ((ctxt->deftype_lo & 0xc00) >> 10) != state->enabled) + { + ctxt->deftype_lo |= (state->def_type | state->enabled << 10); + change_mask |= MTRR_CHANGE_MASK_DEFTYPE; + } + + return change_mask; +} /* End Function set_mtrr_state */ + + +static atomic_t undone_count; +static void (*handler_func) (struct set_mtrr_context *ctxt, void *info); +static void *handler_info; +static volatile int wait_barrier_execute = FALSE; +static volatile int wait_barrier_cache_enable = FALSE; + +static void sync_handler (void) +/* [SUMMARY] Synchronisation handler. Executed by "other" CPUs. + [RETURNS] Nothing. +*/ +{ + struct set_mtrr_context ctxt; + + set_mtrr_prepare (&ctxt); + /* Notify master CPU that I'm at the barrier and then wait */ + atomic_dec (&undone_count); + while (wait_barrier_execute) barrier (); + /* The master has cleared me to execute */ + (*handler_func) (&ctxt, handler_info); + /* Notify master CPU that I've executed the function */ + atomic_dec (&undone_count); + /* Wait for master to clear me to enable cache and return */ + while (wait_barrier_cache_enable) barrier (); + set_mtrr_done (&ctxt); +} /* End Function sync_handler */ + +static void do_all_cpus (void (*handler) (struct set_mtrr_context *ctxt, + void *info), + void *info, int local) +/* [SUMMARY] Execute a function on all CPUs, with caches flushed and disabled. + [PURPOSE] This function will synchronise all CPUs, flush and disable caches + on all CPUs, then call a specified function. When the specified function + finishes on all CPUs, caches are enabled on all CPUs. + <handler> The function to execute. + <info> An arbitrary information pointer which is passed to <<handler>>. + <local> If TRUE <<handler>> is executed locally. + [RETURNS] Nothing. +*/ +{ + unsigned long timeout; + struct set_mtrr_context ctxt; + + mtrr_hook = sync_handler; + handler_func = handler; + handler_info = info; + wait_barrier_execute = TRUE; + wait_barrier_cache_enable = TRUE; + /* Send a message to all other CPUs and wait for them to enter the + barrier */ + atomic_set (&undone_count, smp_num_cpus - 1); + smp_message_pass (MSG_ALL_BUT_SELF, MSG_MTRR_CHANGE, 0, 0); + /* Wait for it to be done */ + timeout = jiffies + JIFFIE_TIMEOUT; + while ( (atomic_read (&undone_count) > 0) && (jiffies < timeout) ) + barrier (); + if (atomic_read (&undone_count) > 0) + { + panic ("mtrr: timed out waiting for other CPUs\n"); + } + mtrr_hook = NULL; + /* All other CPUs should be waiting for the barrier, with their caches + already flushed and disabled. Prepare for function completion + notification */ + atomic_set (&undone_count, smp_num_cpus - 1); + /* Flush and disable the local CPU's cache and release the barier, which + should cause the other CPUs to execute the function. Also execute it + locally if required */ + set_mtrr_prepare (&ctxt); + wait_barrier_execute = FALSE; + if (local) (*handler) (&ctxt, info); + /* Now wait for other CPUs to complete the function */ + while (atomic_read (&undone_count) > 0) barrier (); + /* Now all CPUs should have finished the function. Release the barrier to + allow them to re-enable their caches and return from their interrupt, + then enable the local cache and return */ + wait_barrier_cache_enable = FALSE; + set_mtrr_done (&ctxt); + handler_func = NULL; + handler_info = NULL; +} /* End Function do_all_cpus */ + + +struct set_mtrr_data +{ + unsigned long smp_base; + unsigned long smp_size; + unsigned int smp_reg; + mtrr_type smp_type; +}; + +static void set_mtrr_handler (struct set_mtrr_context *ctxt, void *info) +{ + struct set_mtrr_data *data = info; + + set_mtrr_up (data->smp_reg, data->smp_base, data->smp_size, data->smp_type, + FALSE); +} /* End Function set_mtrr_handler */ + +static void set_mtrr_smp (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) +{ + struct set_mtrr_data data; + + data.smp_reg = reg; + data.smp_base = base; + data.smp_size = size; + data.smp_type = type; + do_all_cpus (set_mtrr_handler, &data, TRUE); +} /* End Function set_mtrr_smp */ + + +/* A warning that is common to the module and non-module cases. */ +/* Some BIOS's are fucked and don't set all MTRRs the same! */ +#ifdef MODULE +static void mtrr_state_warn (unsigned long mask) +#else +__initfunc(static void mtrr_state_warn (unsigned long mask)) +#endif +{ + if (!mask) return; + if (mask & MTRR_CHANGE_MASK_FIXED) + printk ("mtrr: your CPUs had inconsistent fixed MTRR settings\n"); + if (mask & MTRR_CHANGE_MASK_VARIABLE) + printk ("mtrr: your CPUs had inconsistent variable MTRR settings\n"); + if (mask & MTRR_CHANGE_MASK_DEFTYPE) + printk ("mtrr: your CPUs had inconsistent MTRRdefType settings\n"); + printk ("mtrr: probably your BIOS does not setup all CPUs\n"); +} /* End Function mtrr_state_warn */ + +#ifdef MODULE +/* As a module, copy the MTRR state using an IPI handler. */ + +static volatile unsigned long smp_changes_mask = 0; + +static void copy_mtrr_state_handler (struct set_mtrr_context *ctxt, void *info) +{ + unsigned long mask, count; + struct mtrr_state *smp_mtrr_state = info; + + mask = set_mtrr_state (smp_mtrr_state, ctxt); + /* Use the atomic bitops to update the global mask */ + for (count = 0; count < sizeof mask * 8; ++count) + { + if (mask & 0x01) set_bit (count, &smp_changes_mask); + mask >>= 1; + } +} /* End Function copy_mtrr_state_handler */ + +/* Copies the entire MTRR state of this cpu to all the others. */ +static void copy_mtrr_state (void) +{ + struct mtrr_state ms; + + get_mtrr_state (&ms); + do_all_cpus (copy_mtrr_state_handler, &ms, FALSE); + finalize_mtrr_state (&ms); + mtrr_state_warn (smp_changes_mask); +} /* End Function copy_mtrr_state */ + +#endif /* MODULE */ +#endif /* __SMP__ */ + +static char *attrib_to_str (int x) +{ + return (x <= 6) ? mtrr_strings[x] : "?"; +} /* End Function attrib_to_str */ + +static void init_table (void) +{ + int i, max; + + max = get_num_var_ranges (); + if ( ( usage_table = kmalloc (max * sizeof *usage_table, GFP_KERNEL) ) + == NULL ) + { + printk ("mtrr: could not allocate\n"); + return; + } + for (i = 0; i < max; i++) usage_table[i] = 1; +#ifdef CONFIG_PROC_FS + if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL ) + { + printk ("mtrr: could not allocate\n"); + return; + } + ascii_buf_bytes = 0; + compute_ascii (); +#endif +} /* End Function init_table */ + +int mtrr_add (unsigned long base, unsigned long size, unsigned int type, + char increment) +/* [SUMMARY] Add an MTRR entry. + <base> The starting (base) address of the region. + <size> The size (in bytes) of the region. + <type> The type of the new region. + <increment> If true and the region already exists, the usage count will be + incremented. + [RETURNS] The MTRR register on success, else a negative number indicating + the error code. + [NOTE] This routine uses a spinlock. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize, last; + + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV; + if ( (base & 0xfff) || (size & 0xfff) ) + { + printk ("mtrr: size and base must be multiples of 4kB\n"); + printk ("mtrr: size: %lx base: %lx\n", size, base); + return -EINVAL; + } + if (base + size < 0x100000) + { + printk ("mtrr: cannot set region below 1 MByte (0x%lx,0x%lx)\n", + base, size); + return -EINVAL; + } + /* Check upper bits of base and last are equal and lower bits are 0 for + base and 1 for last */ + last = base + size - 1; + for (lbase = base; !(lbase & 1) && (last & 1); + lbase = lbase >> 1, last = last >> 1); + if (lbase != last) + { + printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n", + base, size); + return -EINVAL; + } + if (type >= MTRR_NUM_TYPES) + { + printk ("mtrr: type: %u illegal\n", type); + return -EINVAL; + } + /* If the type is WC, check that this processor supports it */ + if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () ) + { + printk ("mtrr: your processor doesn't support write-combining\n"); + return -ENOSYS; + } + increment = increment ? 1 : 0; + max = get_num_var_ranges (); + /* Search for existing MTRR */ + spin_lock (&main_lock); + for (i = 0; i < max; ++i) + { + get_mtrr (i, &lbase, &lsize, <ype); + if (base >= lbase + lsize) continue; + if ( (base < lbase) && (base + size <= lbase) ) continue; + /* At this point we know there is some kind of overlap/enclosure */ + if ( (base < lbase) || (base + size > lbase + lsize) ) + { + spin_unlock (&main_lock); + printk ("mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n", + base, size, lbase, lsize); + return -EINVAL; + } + if (ltype != type) + { + spin_unlock (&main_lock); + printk ( "mtrr: type missmatch for %lx,%lx old: %s new: %s\n", + base, size, attrib_to_str (ltype), attrib_to_str (type) ); + return -EINVAL; + } + if (increment) ++usage_table[i]; + compute_ascii (); + spin_unlock (&main_lock); + return i; + } + /* Search for an empty MTRR */ + for (i = 0; i < max; ++i) + { + get_mtrr (i, &lbase, &lsize, <ype); + if (lsize > 0) continue; + set_mtrr (i, base, size, type); + usage_table[i] = 1; + compute_ascii (); + spin_unlock (&main_lock); + return i; + } + spin_unlock (&main_lock); + printk ("mtrr: no more MTRRs available\n"); + return -ENOSPC; +} /* End Function mtrr_add */ + +int mtrr_del (int reg, unsigned long base, unsigned long size) +/* [SUMMARY] Delete MTRR/decrement usage count. + <reg> The register. If this is less than 0 then <<base>> and <<size>> must + be supplied. + <base> The base address of the region. This is ignored if <<reg>> is >= 0. + <size> The size of the region. This is ignored if <<reg>> is >= 0. + [RETURNS] The register on success, else a negative number indicating + the error code. + [NOTE] This routine uses a spinlock. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV; + max = get_num_var_ranges (); + spin_lock (&main_lock); + if (reg < 0) + { + /* Search for existing MTRR */ + for (i = 0; i < max; ++i) + { + get_mtrr (i, &lbase, &lsize, <ype); + if ( (lbase == base) && (lsize == size) ) + { + reg = i; + break; + } + } + if (reg < 0) + { + spin_unlock (&main_lock); + printk ("mtrr: no MTRR for %lx,%lx found\n", base, size); + return -EINVAL; + } + } + if (reg >= max) + { + spin_unlock (&main_lock); + printk ("mtrr: register: %d too big\n", reg); + return -EINVAL; + } + get_mtrr (reg, &lbase, &lsize, <ype); + if (lsize < 1) + { + spin_unlock (&main_lock); + printk ("mtrr: MTRR %d not used\n", reg); + return -EINVAL; + } + if (usage_table[reg] < 1) + { + spin_unlock (&main_lock); + printk ("mtrr: reg: %d has count=0\n", reg); + return -EINVAL; + } + if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0); + compute_ascii (); + spin_unlock (&main_lock); + return reg; +} /* End Function mtrr_del */ + +#ifdef CONFIG_PROC_FS + +static int mtrr_file_add (unsigned long base, unsigned long size, + unsigned int type, char increment, struct file *file) +{ + int reg, max; + unsigned int *fcount = file->private_data; + + max = get_num_var_ranges (); + if (fcount == NULL) + { + if ( ( fcount = kmalloc (max * sizeof *fcount, GFP_KERNEL) ) == NULL ) + { + printk ("mtrr: could not allocate\n"); + return -ENOMEM; + } + memset (fcount, 0, max * sizeof *fcount); + file->private_data = fcount; + } + reg = mtrr_add (base, size, type, 1); + if (reg >= 0) ++fcount[reg]; + return reg; +} /* End Function mtrr_file_add */ + +static int mtrr_file_del (unsigned long base, unsigned long size, + struct file *file) +{ + int reg; + unsigned int *fcount = file->private_data; + + reg = mtrr_del (-1, base, size); + if (reg < 0) return reg; + if (fcount != NULL) --fcount[reg]; + return reg; +} /* End Function mtrr_file_del */ + +static ssize_t mtrr_read (struct file *file, char *buf, size_t len, + loff_t *ppos) +{ + if (*ppos >= ascii_buf_bytes) return 0; + if (*ppos + len > ascii_buf_bytes) len = ascii_buf_bytes - *ppos; + if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) return -EFAULT; + *ppos += len; + return len; +} /* End Function mtrr_read */ + +static ssize_t mtrr_write (struct file *file, const char *buf, size_t len, + loff_t *ppos) +/* Format of control line: + "base=%lx size=%lx type=%s" OR: + "disable=%d" +*/ +{ + int i, err; + unsigned long reg, base, size; + char *ptr; + char line[LINE_SIZE]; + + if ( !suser () ) return -EPERM; + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) return -ESPIPE; + memset (line, 0, LINE_SIZE); + if (len > LINE_SIZE) len = LINE_SIZE; + if ( copy_from_user (line, buf, len - 1) ) return -EFAULT; + ptr = line + strlen (line) - 1; + if (*ptr == '\n') *ptr = '\0'; + if ( !strncmp (line, "disable=", 8) ) + { + reg = simple_strtoul (line + 8, &ptr, 0); + err = mtrr_del (reg, 0, 0); + if (err < 0) return err; + return len; + } + if ( strncmp (line, "base=", 5) ) + { + printk ("mtrr: no \"base=\" in line: \"%s\"\n", line); + return -EINVAL; + } + base = simple_strtoul (line + 5, &ptr, 0); + for (; isspace (*ptr); ++ptr); + if ( strncmp (ptr, "size=", 5) ) + { + printk ("mtrr: no \"size=\" in line: \"%s\"\n", line); + return -EINVAL; + } + size = simple_strtoul (ptr + 5, &ptr, 0); + for (; isspace (*ptr); ++ptr); + if ( strncmp (ptr, "type=", 5) ) + { + printk ("mtrr: no \"type=\" in line: \"%s\"\n", line); + return -EINVAL; + } + ptr += 5; + for (; isspace (*ptr); ++ptr); + for (i = 0; i < MTRR_NUM_TYPES; ++i) + { + if ( strcmp (ptr, mtrr_strings[i]) ) continue; + err = mtrr_add (base, size, i, 1); + if (err < 0) return err; + return len; + } + printk ("mtrr: illegal type: \"%s\"\n", ptr); + return -EINVAL; +} /* End Function mtrr_write */ + +static int mtrr_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + mtrr_type type; + struct mtrr_sentry sentry; + struct mtrr_gentry gentry; + + switch (cmd) + { + default: + return -ENOIOCTLCMD; + case MTRRIOC_ADD_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file); + if (err < 0) return err; + break; + case MTRRIOC_SET_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_add (sentry.base, sentry.size, sentry.type, 0); + if (err < 0) return err; + break; + case MTRRIOC_DEL_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_del (sentry.base, sentry.size, file); + if (err < 0) return err; + break; + case MTRRIOC_GET_ENTRY: + if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) ) + return -EFAULT; + if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL; + get_mtrr (gentry.regnum, &gentry.base, &gentry.size, &type); + gentry.type = type; + if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) ) + return -EFAULT; + break; + } + return 0; +} /* End Function mtrr_ioctl */ + +static int mtrr_open (struct inode *ino, struct file *filep) +{ + MOD_INC_USE_COUNT; + return 0; +} /* End Function mtrr_open */ + +static int mtrr_close (struct inode *ino, struct file *file) +{ + int i, max; + unsigned int *fcount = file->private_data; + + MOD_DEC_USE_COUNT; + if (fcount == NULL) return 0; + max = get_num_var_ranges (); + for (i = 0; i < max; ++i) + { + while (fcount[i] > 0) + { + if (mtrr_del (i, 0, 0) < 0) printk ("mtrr: reg %d not used\n", i); + --fcount[i]; + } + } + kfree (fcount); + file->private_data = NULL; + return 0; +} /* End Function mtrr_close */ + +static struct file_operations mtrr_fops = +{ + NULL, /* Seek */ + mtrr_read, /* Read */ + mtrr_write, /* Write */ + NULL, /* Readdir */ + NULL, /* Poll */ + mtrr_ioctl, /* IOctl */ + NULL, /* MMAP */ + mtrr_open, /* Open */ + mtrr_close, /* Release */ + NULL, /* Fsync */ + NULL, /* Fasync */ + NULL, /* CheckMediaChange */ + NULL, /* Revalidate */ + NULL, /* Lock */ +}; + +static struct inode_operations proc_mtrr_inode_operations = { + &mtrr_fops, /* default property file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +static struct proc_dir_entry proc_root_mtrr = { + PROC_MTRR, 4, "mtrr", + S_IFREG | S_IWUSR | S_IRUGO, 1, 0, 0, + 0, &proc_mtrr_inode_operations +}; + +static void compute_ascii (void) +{ + char factor; + int i, max; + mtrr_type type; + unsigned long base, size; + + ascii_buf_bytes = 0; + max = get_num_var_ranges (); + for (i = 0; i < max; i++) + { + get_mtrr (i, &base, &size, &type); + if (size < 1) usage_table[i] = 0; + else + { + if (size < 0x100000) + { + /* 1MB */ + factor = 'k'; + size >>= 10; + } + else + { + factor = 'M'; + size >>= 20; + } + sprintf + (ascii_buffer + ascii_buf_bytes, + "reg%02i: base=0x%08lx (%4liMB), size=%4li%cB: %s, count=%d\n", + i, base, base>>20, size, factor, + attrib_to_str (type), usage_table[i]); + ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes); + } + } + proc_root_mtrr.size = ascii_buf_bytes; +} /* End Function compute_ascii */ + +#endif /* CONFIG_PROC_FS */ + +EXPORT_SYMBOL(mtrr_add); +EXPORT_SYMBOL(mtrr_del); + +#if defined(__SMP__) && !defined(MODULE) + +static volatile unsigned long smp_changes_mask __initdata = 0; +static struct mtrr_state smp_mtrr_state __initdata = {0, 0}; + +__initfunc(void mtrr_init_boot_cpu (void)) +{ + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; + printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); + + get_mtrr_state (&smp_mtrr_state); +} /* End Function mtrr_init_boot_cpu */ + +__initfunc(void mtrr_init_secondary_cpu (void)) +{ + unsigned long mask, count; + struct set_mtrr_context ctxt; + + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; + /* Note that this is not ideal, since the cache is only flushed/disabled + for this CPU while the MTRRs are changed, but changing this requires + more invasive changes to the way the kernel boots */ + set_mtrr_prepare (&ctxt); + mask = set_mtrr_state (&smp_mtrr_state, &ctxt); + set_mtrr_done (&ctxt); + /* Use the atomic bitops to update the global mask */ + for (count = 0; count < sizeof mask * 8; ++count) + { + if (mask & 0x01) set_bit (count, &smp_changes_mask); + mask >>= 1; + } +} /* End Function mtrr_init_secondary_cpu */ + +#endif + +#ifdef MODULE +int init_module (void) +#else +__initfunc(int mtrr_init(void)) +#endif +{ +# if !defined(__SMP__) || defined(MODULE) + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return 0; + printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); +#endif + +# ifdef __SMP__ +# ifdef MODULE + copy_mtrr_state (); +# else /* MODULE */ + finalize_mtrr_state (&smp_mtrr_state); + mtrr_state_warn (smp_changes_mask); +# endif /* MODULE */ +# endif /* __SMP__ */ + +# ifdef CONFIG_PROC_FS + proc_register (&proc_root, &proc_root_mtrr); +# endif + + init_table (); + return 0; +} + +#ifdef MODULE +void cleanup_module (void) +{ + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; +# ifdef CONFIG_PROC_FS + proc_unregister (&proc_root, PROC_MTRR); +# endif +# ifdef __SMP__ + mtrr_hook = NULL; +# endif +} +#endif diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 6ba4e0ff8..a06477b9d 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -375,12 +375,12 @@ void machine_restart(char * __unused) registers don't have to be reloaded after switching to real mode: the values are consistent for real mode operation already. */ - __asm__ __volatile__ ("movw $0x0010,%%ax\n" - "\tmovw %%ax,%%ds\n" - "\tmovw %%ax,%%es\n" - "\tmovw %%ax,%%fs\n" - "\tmovw %%ax,%%gs\n" - "\tmovw %%ax,%%ss" : : : "eax"); + __asm__ __volatile__ ("movl $0x0010,%%eax\n" + "\tmovl %%ax,%%ds\n" + "\tmovl %%ax,%%es\n" + "\tmovl %%ax,%%fs\n" + "\tmovl %%ax,%%gs\n" + "\tmovl %%ax,%%ss" : : : "eax"); /* Jump to the 16-bit code that we copied earlier. It disables paging and the cache, switches to real mode, and jumps to the BIOS reset @@ -418,43 +418,37 @@ void show_regs(struct pt_regs * regs) 0xffff & regs->xds,0xffff & regs->xes); } +void release_segments(struct mm_struct *mm) +{ + void * ldt; + + /* forget local segments */ + __asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs ; lldt %w0" + : /* no outputs */ + : "r" (0)); + current->tss.ldt = 0; + + ldt = mm->segments; + if (ldt) { + mm->segments = NULL; + vfree(ldt); + } +} + /* * Free current thread data structures etc.. */ - void exit_thread(void) { /* forget lazy i387 state */ if (last_task_used_math == current) last_task_used_math = NULL; - /* forget local segments */ - __asm__ __volatile__("mov %w0,%%fs ; mov %w0,%%gs ; lldt %w0" - : /* no outputs */ - : "r" (0)); - current->tss.ldt = 0; - if (current->ldt) { - void * ldt = current->ldt; - current->ldt = NULL; - vfree(ldt); - } } void flush_thread(void) { int i; - if (current->ldt) { - free_page((unsigned long) current->ldt); - current->ldt = NULL; - for (i=1 ; i<NR_TASKS ; i++) { - if (task[i] == current) { - set_ldt_desc(gdt+(i<<1)+ - FIRST_LDT_ENTRY,&default_ldt, 1); - load_ldt(i); - } - } - } - for (i=0 ; i<8 ; i++) current->debugreg[i] = 0; @@ -479,13 +473,30 @@ void release_thread(struct task_struct *dead_task) { } +void copy_segments(int nr, struct task_struct *p, struct mm_struct *new_mm) +{ + int ldt_size = 1; + void * ldt = &default_ldt; + struct mm_struct * old_mm = current->mm; + + p->tss.ldt = _LDT(nr); + if (old_mm->segments) { + new_mm->segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); + if (new_mm->segments) { + ldt = new_mm->segments; + ldt_size = LDT_ENTRIES; + memcpy(ldt, old_mm->segments, LDT_ENTRIES*LDT_ENTRY_SIZE); + } + } + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, ldt, ldt_size); +} + int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, struct task_struct * p, struct pt_regs * regs) { struct pt_regs * childregs; p->tss.tr = _TSS(nr); - p->tss.ldt = _LDT(nr); p->tss.es = __KERNEL_DS; p->tss.cs = __KERNEL_CS; p->tss.ss = __KERNEL_DS; @@ -508,16 +519,8 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, childregs->eax = 0; childregs->esp = esp; p->tss.back_link = 0; - if (p->ldt) { - p->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); - if (p->ldt != NULL) - memcpy(p->ldt, current->ldt, LDT_ENTRIES*LDT_ENTRY_SIZE); - } set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); - if (p->ldt) - set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512); - else - set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1); + /* * a bitmap offset pointing outside of the TSS limit causes a nicely * controllable SIGSEGV. The first sys_ioperm() call sets up the @@ -583,8 +586,8 @@ void dump_thread(struct pt_regs * regs, struct user * dump) dump->regs.eax = regs->eax; dump->regs.ds = regs->xds; dump->regs.es = regs->xes; - __asm__("mov %%fs,%0":"=r" (dump->regs.fs)); - __asm__("mov %%gs,%0":"=r" (dump->regs.gs)); + __asm__("movl %%fs,%0":"=r" (dump->regs.fs)); + __asm__("movl %%gs,%0":"=r" (dump->regs.gs)); dump->regs.orig_eax = regs->orig_eax; dump->regs.eip = regs->eip; dump->regs.cs = regs->xcs; diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c index 12a777b5c..c17c13590 100644 --- a/arch/i386/kernel/signal.c +++ b/arch/i386/kernel/signal.c @@ -199,7 +199,7 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) && (tmp & 0x4) != 0x4 /* not a LDT selector */ \ && (tmp & 3) != 3) /* not a RPL3 GDT selector */ \ goto badframe; \ - __asm__ __volatile__("mov %w0,%%" #seg : : "r"(tmp)); } + __asm__ __volatile__("movl %w0,%%" #seg : : "r"(tmp)); } GET_SEG(gs); GET_SEG(fs); @@ -337,9 +337,9 @@ setup_sigcontext(struct sigcontext *sc, struct _fpstate *fpstate, unsigned int tmp; tmp = 0; - __asm__("mov %%gs,%w0" : "=r"(tmp): "0"(tmp)); + __asm__("movl %%gs,%w0" : "=r"(tmp): "0"(tmp)); __put_user(tmp, (unsigned int *)&sc->gs); - __asm__("mov %%fs,%w0" : "=r"(tmp): "0"(tmp)); + __asm__("movl %%fs,%w0" : "=r"(tmp): "0"(tmp)); __put_user(tmp, (unsigned int *)&sc->fs); __put_user(regs->xes, (unsigned int *)&sc->es); @@ -427,7 +427,7 @@ static void setup_frame(int sig, struct k_sigaction *ka, regs->eip = (unsigned long) ka->sa.sa_handler; { unsigned long seg = __USER_DS; - __asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg)); + __asm__("movl %w0,%%fs ; movl %w0,%%gs": "=r"(seg) : "0"(seg)); set_fs(USER_DS); regs->xds = seg; regs->xes = seg; @@ -492,7 +492,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs->eip = (unsigned long) ka->sa.sa_handler; { unsigned long seg = __USER_DS; - __asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg)); + __asm__("movl %w0,%%fs ; movl %w0,%%gs": "=r"(seg) : "0"(seg)); set_fs(USER_DS); regs->xds = seg; regs->xes = seg; diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 9ca377128..0793410a6 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -28,6 +28,7 @@ * Alan Cox : Added EBDA scanning */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/timer.h> @@ -47,6 +48,10 @@ #include <asm/smp.h> #include <asm/io.h> +#ifdef CONFIG_MTRR +# include <asm/mtrr.h> +#endif + #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> @@ -128,9 +133,6 @@ unsigned char boot_cpu_id = 0; /* Processor that is doing the boot up */ static int smp_activated = 0; /* Tripped once we need to start cross invalidating */ int apic_version[NR_CPUS]; /* APIC version number */ static volatile int smp_commenced=0; /* Tripped when we start scheduling */ -unsigned long apic_addr = 0xFEE00000; /* Address of APIC (defaults to 0xFEE00000) */ -unsigned long nlong = 0; /* dummy used for apic_reg address + 0x20 */ -unsigned char *apic_reg=((unsigned char *)(&nlong))-0x20;/* Later set to the ioremap() of the APIC */ unsigned long apic_retval; /* Just debugging the assembler.. */ static volatile unsigned char smp_cpu_in_msg[NR_CPUS]; /* True if this processor is sending an IPI */ @@ -150,8 +152,10 @@ const char lk_lockmsg[] = "lock from interrupt context at %p\n"; int mp_bus_id_to_type [MAX_MP_BUSSES] = { -1, }; extern int mp_irq_entries; extern struct mpc_config_intsrc mp_irqs [MAX_IRQ_SOURCES]; +extern int mpc_default_type; int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { -1, }; int mp_current_pci_id = 0; +unsigned long mp_lapic_addr = 0; /* #define SMP_DEBUG */ @@ -272,8 +276,8 @@ __initfunc(static int smp_read_mpc(struct mp_config_table *mpc)) printk("APIC at: 0x%lX\n",mpc->mpc_lapic); - /* set the local APIC address */ - apic_addr = (unsigned long)phys_to_virt((unsigned long)mpc->mpc_lapic); + /* save the local APIC address, it might be non-default */ + mp_lapic_addr = mpc->mpc_lapic; /* * Now process the configuration blocks. @@ -454,7 +458,7 @@ __initfunc(int smp_scan_config(unsigned long base, unsigned long length)) */ cfg=pg0[0]; - pg0[0] = (apic_addr | 7); + pg0[0] = (mp_lapic_addr | 7); local_flush_tlb(); boot_cpu_id = GET_APIC_ID(*((volatile unsigned long *) APIC_ID)); @@ -477,6 +481,14 @@ __initfunc(int smp_scan_config(unsigned long base, unsigned long length)) cpu_present_map=3; num_processors=2; printk("I/O APIC at 0xFEC00000.\n"); + + /* + * Save the default type number, we + * need it later to set the IO-APIC + * up properly: + */ + mpc_default_type = mpf->mpf_feature1; + printk("Bus #0 is "); } switch(mpf->mpf_feature1) @@ -525,11 +537,6 @@ __initfunc(int smp_scan_config(unsigned long base, unsigned long length)) if(mpf->mpf_physptr) smp_read_mpc((void *)mpf->mpf_physptr); - /* - * Now that the boot CPU id is known, - * set some other information about it. - */ - nlong = boot_cpu_id<<24; /* Dummy 'self' for bootup */ __cpu_logical_map[0] = boot_cpu_id; global_irq_holder = boot_cpu_id; current->processor = boot_cpu_id; @@ -667,6 +674,10 @@ extern int cpu_idle(void * unused); */ __initfunc(int start_secondary(void *unused)) { +#ifdef CONFIG_MTRR + /* Must be done before calibration delay is computed */ + mtrr_init_secondary_cpu (); +#endif smp_callin(); while (!smp_commenced) barrier(); @@ -727,7 +738,7 @@ __initfunc(static void do_boot_cpu(int i)) /* start_eip had better be page-aligned! */ start_eip = setup_trampoline(); - printk("Booting processor %d eip %lx: ", i, start_eip); /* So we see what's up */ + printk("Booting processor %d eip %lx\n", i, start_eip); /* So we see what's up */ stack_start.esp = (void *) (1024 + PAGE_SIZE + (char *)idle); /* @@ -906,6 +917,10 @@ __initfunc(void smp_boot_cpus(void)) int i; unsigned long cfg; +#ifdef CONFIG_MTRR + /* Must be done before other processors booted */ + mtrr_init_boot_cpu (); +#endif /* * Initialize the logical to physical cpu number mapping * and the per-CPU profiling counter/multiplier @@ -938,7 +953,7 @@ __initfunc(void smp_boot_cpus(void)) { printk(KERN_NOTICE "SMP motherboard not detected. Using dummy APIC emulation.\n"); io_apic_irqs = 0; - return; + goto smp_done; } /* @@ -951,15 +966,6 @@ __initfunc(void smp_boot_cpus(void)) printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n"); } - /* - * Map the local APIC into kernel space - */ - - apic_reg = ioremap(apic_addr,4096); - - if(apic_reg == NULL) - panic("Unable to map local apic."); - #ifdef SMP_DEBUG { int reg; @@ -1106,6 +1112,12 @@ __initfunc(void smp_boot_cpus(void)) * go and set it up: */ setup_IO_APIC(); + +smp_done: +#ifdef CONFIG_MTRR + /* Must be done after other processors booted */ + mtrr_init (); +#endif } @@ -1196,6 +1208,10 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) irq = 0x40; break; + case MSG_MTRR_CHANGE: + irq = 0x50; + break; + default: printk("Unknown SMP message %d\n", msg); return; @@ -1494,10 +1510,18 @@ asmlinkage void smp_stop_cpu_interrupt(void) for (;;) ; } +void (*mtrr_hook) (void) = NULL; + +asmlinkage void smp_mtrr_interrupt(void) +{ + ack_APIC_irq (); + if (mtrr_hook) (*mtrr_hook) (); +} + /* * This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts * per second. We assume that the caller has already set up the local - * APIC at apic_addr. + * APIC. * * The APIC timer is not exactly sync with the external timer chip, it * closely follows bus clocks. diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index fdcf951f3..754e9371c 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -68,19 +68,19 @@ out: \ #define get_seg_byte(seg,addr) ({ \ register unsigned char __res; \ -__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \ +__asm__("pushl %%fs;movl %%ax,%%fs;movb %%fs:%2,%%al;popl %%fs" \ :"=a" (__res):"0" (seg),"m" (*(addr))); \ __res;}) #define get_seg_long(seg,addr) ({ \ register unsigned long __res; \ -__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \ +__asm__("pushl %%fs;movl %%ax,%%fs;movl %%fs:%2,%%eax;popl %%fs" \ :"=a" (__res):"0" (seg),"m" (*(addr))); \ __res;}) #define _fs() ({ \ register unsigned short __res; \ -__asm__("mov %%fs,%%ax":"=a" (__res):); \ +__asm__("movl %%fs,%%ax":"=a" (__res):); \ __res;}) void page_exception(void); diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c index 5ae87b06a..db7da10fc 100644 --- a/arch/i386/kernel/vm86.c +++ b/arch/i386/kernel/vm86.c @@ -255,7 +255,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk mark_screen_rdonly(tsk); unlock_kernel(); __asm__ __volatile__( - "xorl %%eax,%%eax; mov %%ax,%%fs; mov %%ax,%%gs\n\t" + "xorl %%eax,%%eax; movl %%ax,%%fs; movl %%ax,%%gs\n\t" "movl %0,%%esp\n\t" "jmp ret_from_sys_call" : /* no outputs */ diff --git a/arch/i386/lib/checksum.c b/arch/i386/lib/checksum.c index 88f250d62..c246421a9 100644 --- a/arch/i386/lib/checksum.c +++ b/arch/i386/lib/checksum.c @@ -123,6 +123,8 @@ unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) unsigned int csum_partial_copy_generic (const char *src, char *dst, int len, int sum, int *src_err_ptr, int *dst_err_ptr) { + __u32 tmp_var; + __asm__ __volatile__ ( " testl $2, %%edi # Check alignment. jz 2f # Jump if alignment is ok. @@ -137,7 +139,7 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst, addw %%bx, %%ax adcl $0, %%eax 2: - pushl %%ecx + movl %%ecx, %8 shrl $5, %%ecx jz 2f testl %%esi, %%esi @@ -174,7 +176,7 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst, dec %%ecx jne 1b adcl $0, %%eax - 2: popl %%edx + 2: movl %8, %%edx movl %%edx, %%ecx andl $0x1c, %%edx je 4f @@ -231,9 +233,10 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst, ################################################ " - : "=a" (sum), "=m" (src_err_ptr), "=m" (dst_err_ptr) - : "0" (sum), "c" (len), "S" (src), "D" (dst), - "i" (-EFAULT) + : "=a" (sum) + : "m" (src_err_ptr), "m" (dst_err_ptr), + "0" (sum), "c" (len), "S" (src), "D" (dst), + "i" (-EFAULT), "m"(tmp_var) : "bx", "cx", "dx", "si", "di" ); return(sum); diff --git a/arch/i386/math-emu/fpu_system.h b/arch/i386/math-emu/fpu_system.h index 42303f679..1571b2f38 100644 --- a/arch/i386/math-emu/fpu_system.h +++ b/arch/i386/math-emu/fpu_system.h @@ -20,7 +20,7 @@ of the stack frame of math_emulate() */ #define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg -#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3]) +#define LDT_DESCRIPTOR(s) (((struct desc_struct *)current->mm->segments)[(s) >> 3]) #define SEG_D_SIZE(x) ((x).b & (3 << 21)) #define SEG_G_BIT(x) ((x).b & (1 << 23)) #define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) diff --git a/arch/i386/math-emu/poly.h b/arch/i386/math-emu/poly.h index 397cb9e3e..37ddfa0ef 100644 --- a/arch/i386/math-emu/poly.h +++ b/arch/i386/math-emu/poly.h @@ -83,7 +83,7 @@ extern inline void add_Xsig_Xsig(Xsig *dest, const Xsig *x2) /* Note: the constraints in the asm statement didn't always work properly with gcc 2.5.8. Changing from using edi to using ecx got around the problem, but keep fingers crossed! */ -extern inline int add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp) +extern inline void add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp) { asm volatile ("movl %2,%%ecx; movl %3,%%esi; movl (%%esi),%%eax; addl %%eax,(%%ecx); diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index ef3ac57f4..c33c53b9a 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -90,7 +90,6 @@ void show_mem(void) shared += atomic_read(&mem_map[i].count) - 1; } printk("%d pages of RAM\n",total); - printk("%d free pages\n",free); printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); @@ -174,29 +173,29 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_ * It may also hold the MP configuration table when we are booting SMP. */ #ifdef __SMP__ - if (!smp_scan_config(0x0,0x400)) /* Scan the bottom 1K for a signature */ - { + /* + * FIXME: Linux assumes you have 640K of base ram.. + * this continues the error... + * + * 1) Scan the bottom 1K for a signature + * 2) Scan the top 1K of base RAM + * 3) Scan the 64K of bios + */ + if (!smp_scan_config(0x0,0x400) && + !smp_scan_config(639*0x400,0x400) && + !smp_scan_config(0xF0000,0x10000)) { /* - * FIXME: Linux assumes you have 640K of base ram.. this continues - * the error... + * If it is an SMP machine we should know now, unless the + * configuration is in an EISA/MCA bus machine with an + * extended bios data area. + * + * there is a real-mode segmented pointer pointing to the + * 4K EBDA area at 0x40E, calculate and scan it here: */ - if (!smp_scan_config(639*0x400,0x400)) /* Scan the top 1K of base RAM */ - { - if(!smp_scan_config(0xF0000,0x10000)) /* Scan the 64K of bios */ - { - /* - * If it is an SMP machine we should know now, unless the configuration - * is in an EISA/MCA bus machine with an extended bios data area. - */ - - address = *(unsigned short *)phys_to_virt(0x40E); /* EBDA */ - address<<=4; /* Real mode segments to physical */ - smp_scan_config(address, 0x1000); /* Scan the EBDA */ - } - } + address = *(unsigned short *)phys_to_virt(0x40E); + address<<=4; + smp_scan_config(address, 0x1000); } - -/* smp_alloc_memory(8192); */ #endif start_mem = PAGE_ALIGN(start_mem); address = PAGE_OFFSET; @@ -255,7 +254,52 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_ address += PAGE_SIZE; } } +#ifdef __SMP__ +{ + extern unsigned long mp_lapic_addr; + pte_t pte; + unsigned long apic_area = (unsigned long)APIC_BASE; + + pg_dir = swapper_pg_dir + ((apic_area) >> PGDIR_SHIFT); + memset((void *)start_mem, 0, PAGE_SIZE); + pgd_val(*pg_dir) = _PAGE_TABLE | __pa(start_mem); + start_mem += PAGE_SIZE; + + if (smp_found_config) { + /* + * Map the local APIC to FEE00000. (it's only the default + * value, thanks to Steve Hsieh for finding this out. We + * now save the real local-APIC physical address in smp_scan(), + * and use it here) + */ + pg_table = pte_offset((pmd_t *)pg_dir, apic_area); + pte = mk_pte(__va(mp_lapic_addr), PAGE_KERNEL); + set_pte(pg_table, pte); + + /* + * Map the IO-APIC to FEC00000. + */ + apic_area = 0xFEC00000; /*(unsigned long)IO_APIC_BASE;*/ + pg_table = pte_offset((pmd_t *)pg_dir, apic_area); + pte = mk_pte(__va(apic_area), PAGE_KERNEL); + set_pte(pg_table, pte); + } else { + /* + * No local APIC but we are compiled SMP ... set up a + * fake all zeroes page to simulate the local APIC. + */ + pg_table = pte_offset((pmd_t *)pg_dir, apic_area); + pte = mk_pte(start_mem, PAGE_KERNEL); + memset((void *)start_mem, 0, PAGE_SIZE); + start_mem += PAGE_SIZE; + set_pte(pg_table, pte); + } + + local_flush_tlb(); +} +#endif local_flush_tlb(); + return free_area_init(start_mem, end_mem); } diff --git a/arch/i386/vmlinux.lds b/arch/i386/vmlinux.lds index 0284015ad..3812c81db 100644 --- a/arch/i386/vmlinux.lds +++ b/arch/i386/vmlinux.lds @@ -1,13 +1,12 @@ /* ld script to make i386 Linux kernel * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz> */ -INCLUDE arch/i386/.kernel_offset.lds OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) ENTRY(_start) SECTIONS { - . = __kernel_offset__ + 0x100000; + . = 0xC0000000 + 0x100000; _text = .; /* Text and read-only data */ .text : { *(.text) diff --git a/arch/m68k/amiga/amiga_ksyms.c b/arch/m68k/amiga/amiga_ksyms.c index 9206b8e92..2b9c131d7 100644 --- a/arch/m68k/amiga/amiga_ksyms.c +++ b/arch/m68k/amiga/amiga_ksyms.c @@ -1,4 +1,3 @@ -#include <linux/config.h> #include <linux/module.h> #include <linux/types.h> #include <linux/zorro.h> diff --git a/arch/m68k/atari/atari_ksyms.c b/arch/m68k/atari/atari_ksyms.c index bd22bf672..c54889370 100644 --- a/arch/m68k/atari/atari_ksyms.c +++ b/arch/m68k/atari/atari_ksyms.c @@ -1,4 +1,3 @@ -#include <linux/config.h> #include <linux/module.h> #include <asm/ptrace.h> diff --git a/arch/m68k/config.in b/arch/m68k/config.in index e6c984bcb..8ad01e60b 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -269,17 +269,6 @@ endmenu fi -# Conditionally compile in the Uniform CD-ROM driver -if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then - define_bool CONFIG_CDROM y -else - if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then - define_bool CONFIG_CDROM m - else - define_bool CONFIG_CDROM n - fi -fi - source fs/Config.in if [ "$CONFIG_VME" = "n" ]; then diff --git a/arch/m68k/mac/ksyms.c b/arch/m68k/mac/ksyms.c index c1cc4fcd0..05373b04e 100644 --- a/arch/m68k/mac/ksyms.c +++ b/arch/m68k/mac/ksyms.c @@ -1,4 +1,3 @@ -#include <linux/config.h> #include <linux/module.h> #include <asm/ptrace.h> #include <asm/traps.h> diff --git a/arch/mips/config.in b/arch/mips/config.in index 171170f3b..02a256200 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -183,17 +183,6 @@ if [ "$CONFIG_SGI" != "y" ]; then endmenu fi -# Conditionally compile in the Uniform CD-ROM driver -if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then - define_bool CONFIG_CDROM y -else - if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then - define_bool CONFIG_CDROM m - else - define_bool CONFIG_CDROM n - fi -fi - source fs/Config.in source fs/nls/Config.in diff --git a/arch/mips/defconfig b/arch/mips/defconfig index 8f4d81b1f..d7d10aa89 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -214,6 +214,7 @@ CONFIG_PCNET32=y # AX.25 network device drivers # # CONFIG_MKISS is not set +# CONFIG_6PACK is not set # CONFIG_BPQETHER is not set # CONFIG_DMASCC is not set # CONFIG_SCC is not set @@ -231,7 +232,6 @@ CONFIG_PCNET32=y # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set -CONFIG_CDROM=y # # Filesystems diff --git a/arch/mips/jazz/hw-access.c b/arch/mips/jazz/hw-access.c index c9c423a66..4d2caf21b 100644 --- a/arch/mips/jazz/hw-access.c +++ b/arch/mips/jazz/hw-access.c @@ -1,4 +1,4 @@ -/* +/* $Id: hw-access.c,v 1.7 1998/05/06 02:46:43 ralf Exp $ * Low-level hardware access stuff for Jazz family machines. * * This file is subject to the terms and conditions of the GNU General Public @@ -6,8 +6,6 @@ * for more details. * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle - * - * $Id: hw-access.c,v 1.6 1998/03/04 08:29:09 ralf Exp $ */ #include <linux/delay.h> #include <linux/init.h> @@ -154,6 +152,8 @@ struct feature jazz_feature = { static volatile keyboard_hardware *jazz_kh = (keyboard_hardware *) JAZZ_KEYBOARD_ADDRESS; +#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ + static unsigned char jazz_read_input(void) { return jazz_kh->data; @@ -161,11 +161,21 @@ static unsigned char jazz_read_input(void) static void jazz_write_output(unsigned char val) { + int status; + + do { + status = jazz_kh->command; + } while (status & KBD_STAT_IBF); jazz_kh->data = val; } static void jazz_write_command(unsigned char val) { + int status; + + do { + status = jazz_kh->command; + } while (status & KBD_STAT_IBF); jazz_kh->command = val; } diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index 619aaabaa..cf2ab9fca 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -725,6 +725,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm, current->mm->end_data = 0; current->mm->end_code = 0; current->mm->mmap = NULL; + current->flags &= ~PF_FORKNOEXEC; elf_entry = (unsigned int) elf_ex.e_entry; /* Do this so that we can load the interpreter, if need be. We will @@ -775,8 +776,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm, if (current->binfmt && current->binfmt->module) __MOD_INC_USE_COUNT(current->binfmt->module); - current->suid = current->euid = current->fsuid = bprm->e_uid; - current->sgid = current->egid = current->fsgid = bprm->e_gid; + compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; bprm->p = (unsigned long) create_irix_tables((char *)bprm->p, bprm->argc, bprm->envc, diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 728af4cc9..9dcca6184 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -4,7 +4,7 @@ * Copyright (C) 1992 Linus Torvalds * Copyright (C) 1994, 1995, 1996, 1997 Ralf Baechle * - * $Id: irq.c,v 1.8 1998/03/17 22:07:35 ralf Exp $ + * $Id: irq.c,v 1.9 1998/03/22 23:27:12 ralf Exp $ */ #include <linux/errno.h> #include <linux/init.h> @@ -310,7 +310,15 @@ int probe_irq_off (unsigned long irqs) return i; } +int (*irq_cannonicalize)(int irq); + +static int 8259a_irq_cannonicalize(int irq) +{ + return ((irq == 2) ? 9 : irq); +} + __initfunc(void init_IRQ(void)) { + irq_cannonicalize = 8259a_irq_cannonicalize; irq_setup(); } diff --git a/arch/mips/kernel/pci.c b/arch/mips/kernel/pci.c index 6f7374fd2..71a57b73e 100644 --- a/arch/mips/kernel/pci.c +++ b/arch/mips/kernel/pci.c @@ -5,7 +5,6 @@ * * MIPS implementation of PCI BIOS services for PCI support. */ -#include <linux/bios32.h> #include <linux/config.h> #include <linux/init.h> #include <linux/kernel.h> @@ -36,53 +35,6 @@ int pcibios_present (void) } /* - * Given the vendor and device ids, find the n'th instance of that device - * in the system. - */ -int pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - -/* - * Given the class, find the n'th instance of that device - * in the system. - */ -int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - -/* * The functions below are machine specific and must be reimplented for * each PCI chipset configuration. We just run the hook to the machine * specific implementation. @@ -129,4 +81,9 @@ int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn, return pci_ops->pcibios_write_config_dword(bus, dev_fn, where, val); } +__initfunc(char *pcibios_setup(char *str)) +{ + return str; +} + #endif /* defined(CONFIG_PCI) */ diff --git a/arch/mips/kernel/syscalls.h b/arch/mips/kernel/syscalls.h index 33bec5be8..3a5c55540 100644 --- a/arch/mips/kernel/syscalls.h +++ b/arch/mips/kernel/syscalls.h @@ -7,7 +7,7 @@ * * Copyright (C) 1995, 1996 by Ralf Baechle * - * $Id: syscalls.h,v 1.10 1997/12/16 05:34:38 ralf Exp $ + * $Id: syscalls.h,v 1.11 1998/03/17 22:07:37 ralf Exp $ */ /* @@ -34,7 +34,7 @@ SYS(sys_chdir, 1) SYS(sys_time, 1) SYS(sys_mknod, 3) SYS(sys_chmod, 2) /* 4015 */ -SYS(sys_chown, 3) +SYS(sys_lchown, 3) SYS(sys_ni_syscall, 0) SYS(sys_stat, 2) SYS(sys_lseek, 3) @@ -220,4 +220,5 @@ SYS(sys_rt_sigqueueinfo, 3) SYS(sys_rt_sigsuspend, 2) SYS(sys_pread, 4) /* 4200 */ SYS(sys_pwrite, 4) -SYS(sys_lchown, 3) +SYS(sys_chown, 3) +SYS(sys_getcwd, 2) diff --git a/arch/mips/lib/csum_partial_copy.S b/arch/mips/lib/csum_partial_copy.S index 62ee35395..d5b281574 100644 --- a/arch/mips/lib/csum_partial_copy.S +++ b/arch/mips/lib/csum_partial_copy.S @@ -1,13 +1,13 @@ -/* +/* $Id: csum_partial_copy.S,v 1.5 1998/05/06 02:43:34 ralf Exp $ + * + * Unified implementation of csum_copy_partial, csum_copy_partial_from_user + * and csum_copy_partial_nocheck. + * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1998 Ralf Baechle - * - * $Id: csum_partial_copy.S,v 1.3 1998/05/01 06:54:07 ralf Exp $ - * - * Unified implementation of csum_copy_partial and csum_copy_partial_from_user. */ #include <asm/asm.h> #include <asm/offset.h> @@ -179,6 +179,7 @@ small_csumcpy: .align 5 LEAF(csum_partial_copy_from_user) addu t5, src, a2 # end address for fixup +EXPORT(csum_partial_copy_nocheck) EXPORT(csum_partial_copy) move sum, zero # clear computed sum move t7, zero # clear odd flag diff --git a/arch/mips/lib/ide-std.c b/arch/mips/lib/ide-std.c index 47b103c03..e6bf4dc5f 100644 --- a/arch/mips/lib/ide-std.c +++ b/arch/mips/lib/ide-std.c @@ -20,6 +20,8 @@ static int std_ide_default_irq(ide_ioreg_t base) case 0x170: return 15; case 0x1e8: return 11; case 0x168: return 10; + case 0x1e0: return 8; + case 0x160: return 12; default: return 0; } @@ -32,6 +34,8 @@ static ide_ioreg_t std_ide_default_io_base(int index) case 1: return 0x170; case 2: return 0x1e8; case 3: return 0x168; + case 4: return 0x1e0; + case 5: return 0x160; default: return 0; } diff --git a/arch/mips/sgi/kernel/indy_int.c b/arch/mips/sgi/kernel/indy_int.c index 950744328..65340f786 100644 --- a/arch/mips/sgi/kernel/indy_int.c +++ b/arch/mips/sgi/kernel/indy_int.c @@ -4,7 +4,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: indy_int.c,v 1.6 1998/03/17 22:07:41 ralf Exp $ + * $Id: indy_int.c,v 1.7 1998/04/05 11:23:58 ralf Exp $ */ #include <linux/config.h> #include <linux/init.h> @@ -417,8 +417,16 @@ void free_irq(unsigned int irq, void *dev_id) printk("Trying to free free IRQ%d\n",irq); } +int (*irq_cannonicalize)(int irq); + +static int indy_irq_cannonicalize(int irq) +{ + return irq; /* Sane hardware, sane code ... */ +} + __initfunc(void init_IRQ(void)) { + irq_cannonicalize = indy_irq_cannonicalize; irq_setup(); } diff --git a/arch/mips/sgi/kernel/setup.c b/arch/mips/sgi/kernel/setup.c index 6b2c1846e..5107edb6f 100644 --- a/arch/mips/sgi/kernel/setup.c +++ b/arch/mips/sgi/kernel/setup.c @@ -1,9 +1,8 @@ -/* +/* $Id: setup.c,v 1.8 1998/05/06 02:46:46 ralf Exp $ + * * setup.c: SGI specific setup, including init of the feature struct. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) - * - * $Id: setup.c,v 1.7 1998/03/04 08:47:27 ralf Exp $ */ #include <linux/init.h> #include <linux/kernel.h> @@ -31,6 +30,8 @@ struct feature sgi_feature = { static volatile struct hpc_keyb *sgi_kh = (struct hpc_keyb *) (KSEG1 + 0x1fbd9800 + 64); +#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ + static unsigned char sgi_read_input(void) { return sgi_kh->data; @@ -38,11 +39,21 @@ static unsigned char sgi_read_input(void) static void sgi_write_output(unsigned char val) { + int status; + + do { + status = sgi_kh->command; + } while (status & KBD_STAT_IBF); sgi_kh->data = val; } static void sgi_write_command(unsigned char val) { + int status; + + do { + status = sgi_kh->command; + } while (status & KBD_STAT_IBF); sgi_kh->command = val; } diff --git a/arch/mips/sni/hw-access.c b/arch/mips/sni/hw-access.c index 744518971..b32e7dcce 100644 --- a/arch/mips/sni/hw-access.c +++ b/arch/mips/sni/hw-access.c @@ -1,4 +1,5 @@ -/* +/* $Id: hw-access.c,v 1.5 1998/05/06 02:46:46 ralf Exp $ + * * Low-level hardware access stuff for SNI RM200 PCI * * This file is subject to the terms and conditions of the GNU General Public @@ -6,8 +7,6 @@ * for more details. * * Copyright (C) 1996, 1997, 1998 by Ralf Baechle - * - * $Id: hw-access.c,v 1.3 1997/12/01 17:57:39 ralf Exp $ */ #include <linux/delay.h> #include <linux/init.h> @@ -163,6 +162,8 @@ struct feature sni_rm200_pci_feature = { rtc_write_data }; +#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ + static unsigned char sni_read_input(void) { return inb(KBD_DATA_REG); @@ -170,11 +171,21 @@ static unsigned char sni_read_input(void) static void sni_write_output(unsigned char val) { + int status; + + do { + status = inb(KBD_CNTL_REG); + } while (status & KBD_STAT_IBF); outb(val, KBD_DATA_REG); } static void sni_write_command(unsigned char val) { + int status; + + do { + status = inb(KBD_CNTL_REG); + } while (status & KBD_STAT_IBF); outb(val, KBD_CNTL_REG); } diff --git a/arch/mips/sni/pci.c b/arch/mips/sni/pci.c index e2255b127..0e497450a 100644 --- a/arch/mips/sni/pci.c +++ b/arch/mips/sni/pci.c @@ -5,10 +5,9 @@ * * SNI specific PCI support for RM200/RM300. * - * $Id: pci.c,v 1.3 1998/03/04 08:47:29 ralf Exp $ + * $Id: pci.c,v 1.5 1998/05/04 01:15:23 ralf Exp $ */ #include <linux/config.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/types.h> #include <asm/byteorder.h> diff --git a/arch/ppc/8xx_io/.cvsignore b/arch/ppc/8xx_io/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/arch/ppc/8xx_io/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/arch/ppc/8xx_io/Makefile b/arch/ppc/8xx_io/Makefile new file mode 100644 index 000000000..b33919a5d --- /dev/null +++ b/arch/ppc/8xx_io/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the linux MPC8xx ppc-specific parts of comm processor +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := 8xx_io.a +O_OBJS = commproc.o uart.o enet.o + +include $(TOPDIR)/Rules.make diff --git a/arch/ppc/8xx_io/commproc.c b/arch/ppc/8xx_io/commproc.c new file mode 100644 index 000000000..7e95afd08 --- /dev/null +++ b/arch/ppc/8xx_io/commproc.c @@ -0,0 +1,222 @@ + +/* + * General Purpose functions for the global management of the + * Communication Processor Module. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * In addition to the individual control of the communication + * channels, there are a few functions that globally affect the + * communication processor. + * + * Buffer descriptors must be allocated from the dual ported memory + * space. The allocator for that is here. When the communication + * process is reset, we reclaim the memory available. There is + * currently no deallocator for this memory. + * The amount of space available is platform dependent. On the + * MBX, the EPPC software loads additional microcode into the + * communication processor, and uses some of the DP ram for this + * purpose. Current, the first 512 bytes and the last 256 bytes of + * memory are used. Right now I am conservative and only use the + * memory that can never be used for microcode. If there are + * applications that require more DP ram, we can expand the boundaries + * but then we have to be careful of any downloaded microcode. + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <asm/irq.h> +#include <asm/mbx.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/8xx_immap.h> +#include "commproc.h" + +static uint dp_alloc_base; /* Starting offset in DP ram */ +static uint dp_alloc_top; /* Max offset + 1 */ +static uint host_buffer; /* One page of host buffer */ +static uint host_end; /* end + 1 */ +cpm8xx_t *cpmp; /* Pointer to comm processor space */ + +/* CPM interrupt vector functions. +*/ +struct cpm_action { + void (*handler)(void *); + void *dev_id; +}; +static struct cpm_action cpm_vecs[CPMVEC_NR]; +static void cpm_interrupt(int irq, void * dev, struct pt_regs * regs); +static void cpm_error_interrupt(void *); + +void +mbx_cpm_reset(uint host_page_addr) +{ + volatile immap_t *imp; + volatile cpm8xx_t *commproc; + pte_t *pte; + + imp = (immap_t *)MBX_IMAP_ADDR; + commproc = (cpm8xx_t *)&imp->im_cpm; + +#ifdef notdef + /* We can't do this. It seems to blow away the microcode + * patch that EPPC-Bug loaded for us. EPPC-Bug uses SCC1 for + * Ethernet, SMC1 for the console, and I2C for serial EEPROM. + * Our own drivers quickly reset all of these. + */ + + /* Perform a reset. + */ + commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); + + /* Wait for it. + */ + while (commproc->cp_cpcr & CPM_CR_FLG); +#endif + + /* Set SDMA Bus Request priority 5. + */ + imp->im_siu_conf.sc_sdcr = 1; + + /* Reclaim the DP memory for our use. + */ + dp_alloc_base = CPM_DATAONLY_BASE; + dp_alloc_top = dp_alloc_base + CPM_DATAONLY_SIZE; + + /* Set the host page for allocation. + */ + host_buffer = host_page_addr; /* Host virtual page address */ + host_end = host_page_addr + PAGE_SIZE; + pte = va_to_pte(&init_task, host_page_addr); + pte_val(*pte) |= _PAGE_NO_CACHE; + flush_tlb_page(current->mm->mmap, host_buffer); + + /* Tell everyone where the comm processor resides. + */ + cpmp = (cpm8xx_t *)commproc; + + /* Initialize the CPM interrupt controller. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cicr = + (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) | + ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK; + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr = 0; + + /* Set our interrupt handler with the core CPU. + */ + if (request_irq(CPM_INTERRUPT, cpm_interrupt, 0, "cpm", NULL) != 0) + panic("Could not allocate CPM IRQ!"); + + /* Install our own error handler. + */ + cpm_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL); + + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cicr |= CICR_IEN; +} + +/* CPM interrupt controller interrupt. +*/ +static void +cpm_interrupt(int irq, void * dev, struct pt_regs * regs) +{ + uint vec; + + /* Get the vector by setting the ACK bit and then reading + * the register. + */ + ((volatile immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_civr = 1; + vec = ((volatile immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_civr; + vec >>= 11; + + if (cpm_vecs[vec].handler != 0) + (*cpm_vecs[vec].handler)(cpm_vecs[vec].dev_id); + else + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec); + + /* After servicing the interrupt, we have to remove the status + * indicator. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cisr |= (1 << vec); + +} + +/* The CPM can generate the error interrupt when there is a race condition + * between generating and masking interrupts. All we have to do is ACK it + * and return. This is a no-op function so we don't need any special + * tests in the interrupt handler. + */ +static void +cpm_error_interrupt(void *dev) +{ +} + +/* Install a CPM interrupt handler. +*/ +void +cpm_install_handler(int vec, void (*handler)(void *), void *dev_id) +{ + if (cpm_vecs[vec].handler != 0) + printk("CPM interrupt %x replacing %x\n", + (uint)handler, (uint)cpm_vecs[vec].handler); + cpm_vecs[vec].handler = handler; + cpm_vecs[vec].dev_id = dev_id; + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << vec); +} + +/* Allocate some memory from the dual ported ram. We may want to + * enforce alignment restrictions, but right now everyone is a good + * citizen. + */ +uint +mbx_cpm_dpalloc(uint size) +{ + uint retloc; + + if ((dp_alloc_base + size) >= dp_alloc_top) + return(CPM_DP_NOSPACE); + + retloc = dp_alloc_base; + dp_alloc_base += size; + + return(retloc); +} + +/* We also own one page of host buffer space for the allocation of + * UART "fifos" and the like. + */ +uint +mbx_cpm_hostalloc(uint size) +{ + uint retloc; + + if ((host_buffer + size) >= host_end) + return(0); + + retloc = host_buffer; + host_buffer += size; + + return(retloc); +} + +/* Set a baud rate generator. This needs lots of work. There are + * four BRGs, any of which can be wired to any channel. + * The internal baud rate clock is the system clock divided by 16. + * I need to find a way to get this system clock frequency, which is + * part of the VPD....... + */ +#define BRG_INT_CLK (40000000/16) + +void +mbx_cpm_setbrg(uint brg, uint rate) +{ + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + bp = (uint *)&cpmp->cp_brgc1; + bp += brg; + *bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN; +} diff --git a/arch/ppc/8xx_io/commproc.h b/arch/ppc/8xx_io/commproc.h new file mode 100644 index 000000000..38046a31c --- /dev/null +++ b/arch/ppc/8xx_io/commproc.h @@ -0,0 +1,464 @@ + +/* + * MPC8xx Communication Processor Module. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * This file contains structures and information for the communication + * processor channels. Some CPM control and status is available + * throught the MPC8xx internal memory map. See immap.h for details. + * This file only contains what I need for the moment, not the total + * CPM capabilities. I (or someone else) will add definitions as they + * are needed. -- Dan + * + * On the MBX board, EPPC-Bug loads CPM microcode into the first 512 + * bytes of the DP RAM and relocates the I2C parameter area to the + * IDMA1 space. The remaining DP RAM is available for buffer descriptors + * or other use. + */ +#ifndef __CPM_8XX__ +#define __CPM_8XX__ + +#include <asm/8xx_immap.h> + +/* CPM Command register. +*/ +#define CPM_CR_RST ((ushort)0x8000) +#define CPM_CR_OPCODE ((ushort)0x0f00) +#define CPM_CR_CHAN ((ushort)0x00f0) +#define CPM_CR_FLG ((ushort)0x0001) + +/* Some commands (there are more...later) +*/ +#define CPM_CR_INIT_TRX ((ushort)0x0000) +#define CPM_CR_INIT_RX ((ushort)0x0001) +#define CPM_CR_INIT_TX ((ushort)0x0002) +#define CPM_CR_STOP_TX ((ushort)0x0004) +#define CPM_CR_RESTART_TX ((ushort)0x0006) +#define CPM_CR_SET_GADDR ((ushort)0x0008) + +/* Channel numbers. +*/ +#define CPM_CR_CH_SCC1 ((ushort)0x0000) +#define CPM_CR_CH_I2C ((ushort)0x0001) /* I2C and IDMA1 */ +#define CPM_CR_CH_SCC2 ((ushort)0x0004) +#define CPM_CR_CH_SPI ((ushort)0x0005) /* SPI / IDMA2 / Timers */ +#define CPM_CR_CH_SCC3 ((ushort)0x0008) +#define CPM_CR_CH_SMC1 ((ushort)0x0009) /* SMC1 / DSP1 */ +#define CPM_CR_CH_SCC4 ((ushort)0x000c) +#define CPM_CR_CH_SMC2 ((ushort)0x000d) /* SMC2 / DSP2 */ + +#define mk_cr_cmd(CH, CMD) ((CMD << 8) | (CH << 4)) + +/* The dual ported RAM is multi-functional. Some areas can be (and are + * being) used for microcode. There is an area that can only be used + * as data ram for buffer descriptors, which is all we use right now. + * Currently the first 512 and last 256 bytes are used for microcode. + */ +#define CPM_DATAONLY_BASE ((uint)0x0800) +#define CPM_DATAONLY_SIZE ((uint)0x0700) +#define CPM_DP_NOSPACE ((uint)0x7fffffff) + +/* Export the base address of the communication processor registers + * and dual port ram. + */ +extern cpm8xx_t *cpmp; /* Pointer to comm processor */ +uint mbx_cpm_dpalloc(uint size); +uint mbx_cpm_hostalloc(uint size); +void mbx_cpm_setbrg(uint brg, uint rate); + +/* Buffer descriptors used by many of the CPM protocols. +*/ +typedef struct cpm_buf_desc { + ushort cbd_sc; /* Status and Control */ + ushort cbd_datlen; /* Data length in buffer */ + uint cbd_bufaddr; /* Buffer address in host memory */ +} cbd_t; + +#define BD_SC_EMPTY ((ushort)0x8000) /* Recieve is empty */ +#define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */ +#define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */ +#define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */ +#define BD_SC_CM ((ushort)0x0200) /* Continous mode */ +#define BD_SC_ID ((ushort)0x0100) /* Rec'd too many idles */ +#define BD_SC_P ((ushort)0x0100) /* xmt preamble */ +#define BD_SC_BR ((ushort)0x0020) /* Break received */ +#define BD_SC_FR ((ushort)0x0010) /* Framing error */ +#define BD_SC_PR ((ushort)0x0008) /* Parity error */ +#define BD_SC_OV ((ushort)0x0002) /* Overrun */ +#define BD_SC_CD ((ushort)0x0001) /* ?? */ + +/* Define enough so I can at least use the MBX serial port as a UART. + * The MBX uses SMC1 as the host serial port. + */ +typedef struct smc_uart { + ushort smc_rbase; /* Rx Buffer descriptor base address */ + ushort smc_tbase; /* Tx Buffer descriptor base address */ + u_char smc_rfcr; /* Rx function code */ + u_char smc_tfcr; /* Tx function code */ + ushort smc_mrblr; /* Max receive buffer length */ + uint smc_rstate; /* Internal */ + uint smc_idp; /* Internal */ + ushort smc_rbptr; /* Internal */ + ushort smc_ibc; /* Internal */ + uint smc_rxtmp; /* Internal */ + uint smc_tstate; /* Internal */ + uint smc_tdp; /* Internal */ + ushort smc_tbptr; /* Internal */ + ushort smc_tbc; /* Internal */ + uint smc_txtmp; /* Internal */ + ushort smc_maxidl; /* Maximum idle characters */ + ushort smc_tmpidl; /* Temporary idle counter */ + ushort smc_brklen; /* Last received break length */ + ushort smc_brkec; /* rcv'd break condition counter */ + ushort smc_brkcr; /* xmt break count register */ + ushort smc_rmask; /* Temporary bit mask */ +} smc_uart_t; + +#define PROFF_SMC1 ((uint)0x0280) /* Offset in Parameter RAM */ +#define PROFF_SMC2 ((uint)0x0380) + +/* Function code bits. +*/ +#define SMC_EB ((u_char)0x10) /* Set big endian byte order */ + +/* SMC uart mode register. +*/ +#define SMCMR_REN ((ushort)0x0001) +#define SMCMR_TEN ((ushort)0x0002) +#define SMCMR_DM ((ushort)0x000c) +#define SMCMR_SM_GCI ((ushort)0x0000) +#define SMCMR_SM_UART ((ushort)0x0020) +#define SMCMR_SM_TRANS ((ushort)0x0030) +#define SMCMR_SM_MASK ((ushort)0x0030) +#define SMCMR_PM_EVEN ((ushort)0x0100) /* Even parity, else odd */ +#define SMCMR_PEN ((ushort)0x0200) /* Parity enable */ +#define SMCMR_SL ((ushort)0x0400) /* Two stops, else one */ +#define SMCR_CLEN_MASK ((ushort)0x7800) /* Character length */ +#define smcr_mk_clen(C) (((C) << 11) & SMCR_CLEN_MASK) + +/* SMC Event and Mask register. +*/ +#define SMCM_TXE ((unsigned char)0x10) +#define SMCM_BSY ((unsigned char)0x14) +#define SMCM_TX ((unsigned char)0x02) +#define SMCM_RX ((unsigned char)0x01) + +/* Baud rate generators. +*/ +#define CPM_BRG_RST ((uint)0x00020000) +#define CPM_BRG_EN ((uint)0x00010000) +#define CPM_BRG_EXTC_INT ((uint)0x00000000) +#define CPM_BRG_EXTC_CLK2 ((uint)0x00004000) +#define CPM_BRG_EXTC_CLK6 ((uint)0x00008000) +#define CPM_BRG_ATB ((uint)0x00002000) +#define CPM_BRG_CD_MASK ((uint)0x00001ffe) +#define CPM_BRG_DIV16 ((uint)0x00000001) + +/* SCCs. +*/ +#define SCC_GSMRH_IRP ((uint)0x00040000) +#define SCC_GSMRH_GDE ((uint)0x00010000) +#define SCC_GSMRH_TCRC_CCITT ((uint)0x00008000) +#define SCC_GSMRH_TCRC_BISYNC ((uint)0x00004000) +#define SCC_GSMRH_TCRC_HDLC ((uint)0x00000000) +#define SCC_GSMRH_REVD ((uint)0x00002000) +#define SCC_GSMRH_TRX ((uint)0x00001000) +#define SCC_GSMRH_TTX ((uint)0x00000800) +#define SCC_GSMRH_CDP ((uint)0x00000400) +#define SCC_GSMRH_CTSP ((uint)0x00000200) +#define SCC_GSMRH_CDS ((uint)0x00000100) +#define SCC_GSMRH_CTSS ((uint)0x00000080) +#define SCC_GSMRH_TFL ((uint)0x00000040) +#define SCC_GSMRH_RFW ((uint)0x00000020) +#define SCC_GSMRH_TXSY ((uint)0x00000010) +#define SCC_GSMRH_SYNL16 ((uint)0x0000000c) +#define SCC_GSMRH_SYNL8 ((uint)0x00000008) +#define SCC_GSMRH_SYNL4 ((uint)0x00000004) +#define SCC_GSMRH_RTSM ((uint)0x00000002) +#define SCC_GSMRH_RSYN ((uint)0x00000001) + +#define SCC_GSMRL_SIR ((uint)0x80000000) /* SCC2 only */ +#define SCC_GSMRL_EDGE_NONE ((uint)0x60000000) +#define SCC_GSMRL_EDGE_NEG ((uint)0x40000000) +#define SCC_GSMRL_EDGE_POS ((uint)0x20000000) +#define SCC_GSMRL_EDGE_BOTH ((uint)0x00000000) +#define SCC_GSMRL_TCI ((uint)0x10000000) +#define SCC_GSMRL_TSNC_3 ((uint)0x0c000000) +#define SCC_GSMRL_TSNC_4 ((uint)0x08000000) +#define SCC_GSMRL_TSNC_14 ((uint)0x04000000) +#define SCC_GSMRL_TSNC_INF ((uint)0x00000000) +#define SCC_GSMRL_RINV ((uint)0x02000000) +#define SCC_GSMRL_TINV ((uint)0x01000000) +#define SCC_GSMRL_TPL_128 ((uint)0x00c00000) +#define SCC_GSMRL_TPL_64 ((uint)0x00a00000) +#define SCC_GSMRL_TPL_48 ((uint)0x00800000) +#define SCC_GSMRL_TPL_32 ((uint)0x00600000) +#define SCC_GSMRL_TPL_16 ((uint)0x00400000) +#define SCC_GSMRL_TPL_8 ((uint)0x00200000) +#define SCC_GSMRL_TPL_NONE ((uint)0x00000000) +#define SCC_GSMRL_TPP_ALL1 ((uint)0x00180000) +#define SCC_GSMRL_TPP_01 ((uint)0x00100000) +#define SCC_GSMRL_TPP_10 ((uint)0x00080000) +#define SCC_GSMRL_TPP_ZEROS ((uint)0x00000000) +#define SCC_GSMRL_TEND ((uint)0x00040000) +#define SCC_GSMRL_TDCR_32 ((uint)0x00030000) +#define SCC_GSMRL_TDCR_16 ((uint)0x00020000) +#define SCC_GSMRL_TDCR_8 ((uint)0x00010000) +#define SCC_GSMRL_TDCR_1 ((uint)0x00000000) +#define SCC_GSMRL_RDCR_32 ((uint)0x0000c000) +#define SCC_GSMRL_RDCR_16 ((uint)0x00008000) +#define SCC_GSMRL_RDCR_8 ((uint)0x00004000) +#define SCC_GSMRL_RDCR_1 ((uint)0x00000000) +#define SCC_GSMRL_RENC_DFMAN ((uint)0x00003000) +#define SCC_GSMRL_RENC_MANCH ((uint)0x00002000) +#define SCC_GSMRL_RENC_FM0 ((uint)0x00001000) +#define SCC_GSMRL_RENC_NRZI ((uint)0x00000800) +#define SCC_GSMRL_RENC_NRZ ((uint)0x00000000) +#define SCC_GSMRL_TENC_DFMAN ((uint)0x00000600) +#define SCC_GSMRL_TENC_MANCH ((uint)0x00000400) +#define SCC_GSMRL_TENC_FM0 ((uint)0x00000200) +#define SCC_GSMRL_TENC_NRZI ((uint)0x00000100) +#define SCC_GSMRL_TENC_NRZ ((uint)0x00000000) +#define SCC_GSMRL_DIAG_LE ((uint)0x000000c0) /* Loop and echo */ +#define SCC_GSMRL_DIAG_ECHO ((uint)0x00000080) +#define SCC_GSMRL_DIAG_LOOP ((uint)0x00000040) +#define SCC_GSMRL_DIAG_NORM ((uint)0x00000000) +#define SCC_GSMRL_ENR ((uint)0x00000020) +#define SCC_GSMRL_ENT ((uint)0x00000010) +#define SCC_GSMRL_MODE_ENET ((uint)0x0000000c) +#define SCC_GSMRL_MODE_DDCMP ((uint)0x00000009) +#define SCC_GSMRL_MODE_BISYNC ((uint)0x00000008) +#define SCC_GSMRL_MODE_V14 ((uint)0x00000007) +#define SCC_GSMRL_MODE_AHDLC ((uint)0x00000006) +#define SCC_GSMRL_MODE_PROFIBUS ((uint)0x00000005) +#define SCC_GSMRL_MODE_UART ((uint)0x00000004) +#define SCC_GSMRL_MODE_SS7 ((uint)0x00000003) +#define SCC_GSMRL_MODE_ATALK ((uint)0x00000002) +#define SCC_GSMRL_MODE_HDLC ((uint)0x00000000) + +#define SCC_TODR_TOD ((ushort)0x8000) + +typedef struct scc_param { + ushort scc_rbase; /* Rx Buffer descriptor base address */ + ushort scc_tbase; /* Tx Buffer descriptor base address */ + u_char scc_rfcr; /* Rx function code */ + u_char scc_tfcr; /* Tx function code */ + ushort scc_mrblr; /* Max receive buffer length */ + uint scc_rstate; /* Internal */ + uint scc_idp; /* Internal */ + ushort scc_rbptr; /* Internal */ + ushort scc_ibc; /* Internal */ + uint scc_rxtmp; /* Internal */ + uint scc_tstate; /* Internal */ + uint scc_tdp; /* Internal */ + ushort scc_tbptr; /* Internal */ + ushort scc_tbc; /* Internal */ + uint scc_txtmp; /* Internal */ + uint scc_rcrc; /* Internal */ + uint scc_tcrc; /* Internal */ +} sccp_t; + +/* Function code bits. +*/ +#define SCC_EB ((u_char)0x10) /* Set big endian byte order */ + +/* CPM Ethernet through SCC1. + */ +typedef struct scc_enet { + sccp_t sen_genscc; + uint sen_cpres; /* Preset CRC */ + uint sen_cmask; /* Constant mask for CRC */ + uint sen_crcec; /* CRC Error counter */ + uint sen_alec; /* alignment error counter */ + uint sen_disfc; /* discard frame counter */ + ushort sen_pads; /* Tx short frame pad character */ + ushort sen_retlim; /* Retry limit threshold */ + ushort sen_retcnt; /* Retry limit counter */ + ushort sen_maxflr; /* maximum frame length register */ + ushort sen_minflr; /* minimum frame length register */ + ushort sen_maxd1; /* maximum DMA1 length */ + ushort sen_maxd2; /* maximum DMA2 length */ + ushort sen_maxd; /* Rx max DMA */ + ushort sen_dmacnt; /* Rx DMA counter */ + ushort sen_maxb; /* Max BD byte count */ + ushort sen_gaddr1; /* Group address filter */ + ushort sen_gaddr2; + ushort sen_gaddr3; + ushort sen_gaddr4; + uint sen_tbuf0data0; /* Save area 0 - current frame */ + uint sen_tbuf0data1; /* Save area 1 - current frame */ + uint sen_tbuf0rba; /* Internal */ + uint sen_tbuf0crc; /* Internal */ + ushort sen_tbuf0bcnt; /* Internal */ + ushort sen_paddrh; /* physical address (MSB) */ + ushort sen_paddrm; + ushort sen_paddrl; /* physical address (LSB) */ + ushort sen_pper; /* persistence */ + ushort sen_rfbdptr; /* Rx first BD pointer */ + ushort sen_tfbdptr; /* Tx first BD pointer */ + ushort sen_tlbdptr; /* Tx last BD pointer */ + uint sen_tbuf1data0; /* Save area 0 - current frame */ + uint sen_tbuf1data1; /* Save area 1 - current frame */ + uint sen_tbuf1rba; /* Internal */ + uint sen_tbuf1crc; /* Internal */ + ushort sen_tbuf1bcnt; /* Internal */ + ushort sen_txlen; /* Tx Frame length counter */ + ushort sen_iaddr1; /* Individual address filter */ + ushort sen_iaddr2; + ushort sen_iaddr3; + ushort sen_iaddr4; + ushort sen_boffcnt; /* Backoff counter */ + + /* NOTE: Some versions of the manual have the following items + * incorrectly documented. Below is the proper order. + */ + ushort sen_taddrh; /* temp address (MSB) */ + ushort sen_taddrm; + ushort sen_taddrl; /* temp address (LSB) */ +} scc_enet_t; + +#define PROFF_SCC1 ((uint)0x0000) /* Offset in Parameter RAM */ + +/* Bits in parallel I/O port registers that have to be set/cleared + * to configure the pins for SCC1 use. The TCLK and RCLK seem unique + * to the MBX860 board. Any two of the four available clocks could be + * used, and the MPC860 cookbook manual has an example using different + * clock pins. + */ +#define PA_ENET_RXD ((ushort)0x0001) +#define PA_ENET_TXD ((ushort)0x0002) +#define PA_ENET_TCLK ((ushort)0x0200) +#define PA_ENET_RCLK ((ushort)0x0800) +#define PC_ENET_TENA ((ushort)0x0001) +#define PC_ENET_CLSN ((ushort)0x0010) +#define PC_ENET_RENA ((ushort)0x0020) + +/* Control bits in the SICR to route TCLK (CLK2) and RCLK (CLK4) to + * SCC1. Also, make sure GR1 (bit 24) and SC1 (bit 25) are zero. + */ +#define SICR_ENET_MASK ((uint)0x000000ff) +#define SICR_ENET_CLKRT ((uint)0x0000003d) + +/* SCC Event register as used by Ethernet. +*/ +#define SCCE_ENET_GRA ((ushort)0x0080) /* Graceful stop complete */ +#define SCCE_ENET_TXE ((ushort)0x0010) /* Transmit Error */ +#define SCCE_ENET_RXF ((ushort)0x0008) /* Full frame received */ +#define SCCE_ENET_BSY ((ushort)0x0004) /* All incoming buffers full */ +#define SCCE_ENET_TXB ((ushort)0x0002) /* A buffer was transmitted */ +#define SCCE_ENET_RXB ((ushort)0x0001) /* A buffer was received */ + +/* SCC Mode Register (PMSR) as used by Ethernet. +*/ +#define SCC_PMSR_HBC ((ushort)0x8000) /* Enable heartbeat */ +#define SCC_PMSR_FC ((ushort)0x4000) /* Force collision */ +#define SCC_PMSR_RSH ((ushort)0x2000) /* Receive short frames */ +#define SCC_PMSR_IAM ((ushort)0x1000) /* Check individual hash */ +#define SCC_PMSR_ENCRC ((ushort)0x0800) /* Ethernet CRC mode */ +#define SCC_PMSR_PRO ((ushort)0x0200) /* Promiscuous mode */ +#define SCC_PMSR_BRO ((ushort)0x0100) /* Catch broadcast pkts */ +#define SCC_PMSR_SBT ((ushort)0x0080) /* Special backoff timer */ +#define SCC_PMSR_LPB ((ushort)0x0040) /* Set Loopback mode */ +#define SCC_PMSR_SIP ((ushort)0x0020) /* Sample Input Pins */ +#define SCC_PMSR_LCW ((ushort)0x0010) /* Late collision window */ +#define SCC_PMSR_NIB22 ((ushort)0x000a) /* Start frame search */ +#define SCC_PMSR_FDE ((ushort)0x0001) /* Full duplex enable */ + +/* Buffer descriptor control/status used by Ethernet receive. +*/ +#define BD_ENET_RX_EMPTY ((ushort)0x8000) +#define BD_ENET_RX_WRAP ((ushort)0x2000) +#define BD_ENET_RX_INTR ((ushort)0x1000) +#define BD_ENET_RX_LAST ((ushort)0x0800) +#define BD_ENET_RX_FIRST ((ushort)0x0400) +#define BD_ENET_RX_MISS ((ushort)0x0100) +#define BD_ENET_RX_LG ((ushort)0x0020) +#define BD_ENET_RX_NO ((ushort)0x0010) +#define BD_ENET_RX_SH ((ushort)0x0008) +#define BD_ENET_RX_CR ((ushort)0x0004) +#define BD_ENET_RX_OV ((ushort)0x0002) +#define BD_ENET_RX_CL ((ushort)0x0001) +#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ + +/* Buffer descriptor control/status used by Ethernet transmit. +*/ +#define BD_ENET_TX_READY ((ushort)0x8000) +#define BD_ENET_TX_PAD ((ushort)0x4000) +#define BD_ENET_TX_WRAP ((ushort)0x2000) +#define BD_ENET_TX_INTR ((ushort)0x1000) +#define BD_ENET_TX_LAST ((ushort)0x0800) +#define BD_ENET_TX_TC ((ushort)0x0400) +#define BD_ENET_TX_DEF ((ushort)0x0200) +#define BD_ENET_TX_HB ((ushort)0x0100) +#define BD_ENET_TX_LC ((ushort)0x0080) +#define BD_ENET_TX_RL ((ushort)0x0040) +#define BD_ENET_TX_RCMASK ((ushort)0x003c) +#define BD_ENET_TX_UN ((ushort)0x0002) +#define BD_ENET_TX_CSL ((ushort)0x0001) +#define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */ + +/* SCC Event and Mask registers when it is used as a UART. +*/ +#define UART_SCCM_GLR ((ushort)0x1000) +#define UART_SCCM_GLT ((ushort)0x0800) +#define UART_SCCM_AB ((ushort)0x0200) +#define UART_SCCM_IDL ((ushort)0x0100) +#define UART_SCCM_GRA ((ushort)0x0080) +#define UART_SCCM_BRKE ((ushort)0x0040) +#define UART_SCCM_BRKS ((ushort)0x0020) +#define UART_SCCM_CCR ((ushort)0x0008) +#define UART_SCCM_BSY ((ushort)0x0004) +#define UART_SCCM_TX ((ushort)0x0002) +#define UART_SCCM_RX ((ushort)0x0001) + +/* CPM interrupts. There are nearly 32 interrupts generated by CPM + * channels or devices. All of these are presented to the PPC core + * as a single interrupt. The CPM interrupt handler dispatches its + * own handlers, in a similar fashion to the PPC core handler. We + * use the table as defined in the manuals (i.e. no special high + * priority and SCC1 == SCCa, etc...). + */ +#define CPMVEC_NR 32 +#define CPMVEC_PIO_PC15 ((ushort)0x1f) +#define CPMVEC_SCC1 ((ushort)0x1e) +#define CPMVEC_SCC2 ((ushort)0x1d) +#define CPMVEC_SCC3 ((ushort)0x1c) +#define CPMVEC_SCC4 ((ushort)0x1b) +#define CPMVEC_PIO_PC14 ((ushort)0x1a) +#define CPMVEC_TIMER1 ((ushort)0x19) +#define CPMVEC_PIO_PC13 ((ushort)0x18) +#define CPMVEC_PIO_PC12 ((ushort)0x17) +#define CPMVEC_SDMA_CB_ERR ((ushort)0x16) +#define CPMVEC_IDMA1 ((ushort)0x15) +#define CPMVEC_IDMA2 ((ushort)0x14) +#define CPMVEC_TIMER2 ((ushort)0x12) +#define CPMVEC_RISCTIMER ((ushort)0x11) +#define CPMVEC_I2C ((ushort)0x10) +#define CPMVEC_PIO_PC11 ((ushort)0x0f) +#define CPMVEC_PIO_PC10 ((ushort)0x0e) +#define CPMVEC_TIMER3 ((ushort)0x0c) +#define CPMVEC_PIO_PC9 ((ushort)0x0b) +#define CPMVEC_PIO_PC8 ((ushort)0x0a) +#define CPMVEC_PIO_PC7 ((ushort)0x09) +#define CPMVEC_TIMER4 ((ushort)0x07) +#define CPMVEC_PIO_PC6 ((ushort)0x06) +#define CPMVEC_SPI ((ushort)0x05) +#define CPMVEC_SMC1 ((ushort)0x04) +#define CPMVEC_SMC2 ((ushort)0x03) +#define CPMVEC_PIO_PC5 ((ushort)0x02) +#define CPMVEC_PIO_PC4 ((ushort)0x01) +#define CPMVEC_ERROR ((ushort)0x00) + +extern void cpm_install_handler(int vec, void (*handler)(void *), void *dev_id); + +/* CPM interrupt configuration vector. +*/ +#define CICR_SCD_SCC4 ((uint)0x00c00000) /* SCC4 @ SCCd */ +#define CICR_SCC_SCC3 ((uint)0x00200000) /* SCC3 @ SCCc */ +#define CICR_SCB_SCC2 ((uint)0x00040000) /* SCC2 @ SCCb */ +#define CICR_SCA_SCC1 ((uint)0x00000000) /* SCC1 @ SCCa */ +#define CICR_IRL_MASK ((uint)0x0000e000) /* Core interrrupt */ +#define CICR_HP_MASK ((uint)0x00001f00) /* Hi-pri int. */ +#define CICR_IEN ((uint)0x00000080) /* Int. enable */ +#define CICR_SPS ((uint)0x00000001) /* SCC Spread */ +#endif /* __CPM_8XX__ */ diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c new file mode 100644 index 000000000..1c43ddce0 --- /dev/null +++ b/arch/ppc/8xx_io/enet.c @@ -0,0 +1,912 @@ +/* + * Ethernet driver for Motorola MPC8xx. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * I copied the basic skeleton from the lance driver, because I did not + * know how to write the Linux driver, but I did know how the LANCE worked. + * This version of the driver is specific to the MBX implementation, + * since the board contains control registers external to the processor + * for the control of the MC68160 SIA/transceiver. The MPC860 manual + * describes connections using the internal parallel port I/O. + * + * The MBX860 uses the CPM SCC1 serial port for the Ethernet interface. + * Buffer descriptors are kept in the CPM dual port RAM, and the frame + * buffers are in the host memory. + * + * Right now, I am very watseful with the buffers. I allocate memory + * pages and then divide them into 2K frame buffers. This way I know I + * have buffers large enough to hold one frame within one buffer descriptor. + * Once I get this working, I will use 64 or 128 byte CPM buffers, which + * will be much more memory efficient and will easily handle lots of + * small packets. + * + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/init.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <asm/8xx_immap.h> +#include <asm/pgtable.h> +#include <asm/mbx.h> +#include "commproc.h" +#include <linux/delay.h> + +/* + * Theory of Operation + * + * The MPC8xx CPM performs the Ethernet processing on SCC1. It can use + * an aribtrary number of buffers on byte boundaries, but must have at + * least two receive buffers to prevent constand overrun conditions. + * + * The buffer descriptors are allocated from the CPM dual port memory + * with the data buffers allocated from host memory, just like all other + * serial communication protocols. The host memory buffers are allocated + * from the free page pool, and then divided into smaller receive and + * transmit buffers. The size of the buffers should be a power of two, + * since that nicely divides the page. This creates a ring buffer + * structure similar to the LANCE and other controllers. + * + * Like the LANCE driver: + * The driver runs as two independent, single-threaded flows of control. One + * is the send-packet routine, which enforces single-threaded use by the + * dev->tbusy flag. The other thread is the interrupt handler, which is single + * threaded by the hardware and other software. + * + * The send packet thread has partial control over the Tx ring and 'dev->tbusy' + * flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next + * queue slot is empty, it clears the tbusy flag when finished otherwise it sets + * the 'lp->tx_full' flag. + * + * The MBX has a control register external to the MPC8xx that has some + * control of the Ethernet interface. Control Register 1 has the + * following format: + * bit 0 - Set to enable Ethernet transceiver + * bit 1 - Set to enable Ethernet internal loopback + * bit 2 - Set to auto select AUI or TP port + * bit 3 - if bit 2 is 0, set to select TP port + * bit 4 - Set to disable full duplex (loopback) + * bit 5 - Set to disable XCVR collision test + * bit 6, 7 - Used for RS-232 control. + * + * EPPC-Bug sets this register to 0x98 for normal Ethernet operation, + * so we should not have to touch it. + * + * The following I/O is used by the MBX implementation of the MPC8xx to + * the MC68160 transceiver. It DOES NOT exactly follow the cookbook + * example from the MPC860 manual. + * Port A, 15 - SCC1 Ethernet Rx + * Port A, 14 - SCC1 Ethernet Tx + * Port A, 6 (CLK2) - SCC1 Ethernet Tx Clk + * Port A, 4 (CLK4) - SCC1 Ethernet Rx Clk + * Port C, 15 - SCC1 Ethernet Tx Enable + * Port C, 11 - SCC1 Ethernet Collision + * Port C, 10 - SCC1 Ethernet Rx Enable + */ + +/* The number of Tx and Rx buffers. These are allocated from the page + * pool. The code may assume these are power of two, so it it best + * to keep them that size. + * We don't need to allocate pages for the transmitter. We just use + * the skbuffer directly. + */ +#define CPM_ENET_RX_PAGES 4 +#define CPM_ENET_RX_FRSIZE 2048 +#define CPM_ENET_RX_FRPPG (PAGE_SIZE / CPM_ENET_RX_FRSIZE) +#define RX_RING_SIZE (CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES) +#define TX_RING_SIZE 8 /* Must be power of two */ +#define TX_RING_MOD_MASK 7 /* for this to work */ + +/* The CPM stores dest/src/type, data, and checksum for receive packets. + */ +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 + +/* The CPM buffer descriptors track the ring buffers. The rx_bd_base and + * tx_bd_base always point to the base of the buffer descriptors. The + * cur_rx and cur_tx point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct cpm_enet_private { + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + ushort skb_cur; + ushort skb_dirty; + + /* CPM dual port RAM relative addresses. + */ + cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ + cbd_t *tx_bd_base; + cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ + cbd_t *dirty_tx; /* The ring entries to be free()ed. */ + scc_t *sccp; + struct net_device_stats stats; + char tx_full; + unsigned long lock; +}; + +static int cpm_enet_open(struct device *dev); +static int cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev); +static int cpm_enet_rx(struct device *dev); +static void cpm_enet_interrupt(void *dev_id); +static int cpm_enet_close(struct device *dev); +static struct net_device_stats *cpm_enet_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + +/* GET THIS FROM THE VPD!!!! +*/ +static ushort my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 }; + +/* Initialize the CPM Ethernet on SCC1. If EPPC-Bug loaded us, or performed + * some other network I/O, a whole bunch of this has already been set up. + * It is no big deal if we do it again, we just have to disable the + * transmit and receive to make sure we don't catch the CPM with some + * inconsistent control information. + */ +__initfunc(int cpm_enet_init(void)) +{ + struct device *dev; + struct cpm_enet_private *cep; + int i, j; + unsigned char *eap; + unsigned long mem_addr; + pte_t *pte; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile scc_t *sccp; + volatile scc_enet_t *ep; + volatile immap_t *immap; + + cp = cpmp; /* Get pointer to Communication Processor */ + + immap = (immap_t *)MBX_IMAP_ADDR; /* and to internal registers */ + + /* Allocate some private information. + */ + cep = (struct cpm_enet_private *)kmalloc(sizeof(*cep), GFP_KERNEL); + memset(cep, 0, sizeof(*cep)); + + /* Create an Ethernet device instance. + */ + dev = init_etherdev(0, 0); + + /* Get pointer to SCC1 area in parameter RAM. + */ + ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_SCC1]); + + /* And another to the SCC register area. + */ + sccp = (volatile scc_t *)(&cp->cp_scc[0]); + cep->sccp = (scc_t *)sccp; /* Keep the pointer handy */ + + /* Disable receive and transmit in case EPPC-Bug started it. + */ + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* Cookbook style from the MPC860 manual..... + * Not all of this is necessary if EPPC-Bug has initialized + * the network. + */ + + /* Configure port A pins for Txd and Rxd. + */ + immap->im_ioport.iop_papar |= (PA_ENET_RXD | PA_ENET_TXD); + immap->im_ioport.iop_padir &= ~(PA_ENET_RXD | PA_ENET_TXD); + immap->im_ioport.iop_paodr &= ~PA_ENET_TXD; + + /* Configure port C pins to enable CLSN and RENA. + */ + immap->im_ioport.iop_pcpar &= ~(PC_ENET_CLSN | PC_ENET_RENA); + immap->im_ioport.iop_pcdir &= ~(PC_ENET_CLSN | PC_ENET_RENA); + immap->im_ioport.iop_pcso |= (PC_ENET_CLSN | PC_ENET_RENA); + + /* Configure port A for TCLK and RCLK. + */ + immap->im_ioport.iop_papar |= (PA_ENET_TCLK | PA_ENET_RCLK); + immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK); + + /* Configure Serial Interface clock routing. + * First, clear all SCC1 bits to zero, then set the ones we want. + */ + cp->cp_sicr &= ~SICR_ENET_MASK; + cp->cp_sicr |= SICR_ENET_CLKRT; + + /* Manual says set SDDR, but I can't find anything with that + * name. I think it is a misprint, and should be SDCR. This + * has already been set by the communication processor initialization. + */ + + /* Allocate space for the buffer descriptors in the DP ram. + * These are relative offsets in the DP ram address space. + * Initialize base addresses for the buffer descriptors. + */ + i = mbx_cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE); + ep->sen_genscc.scc_rbase = i; + cep->rx_bd_base = (cbd_t *)&cp->cp_dpmem[i]; + + i = mbx_cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE); + ep->sen_genscc.scc_tbase = i; + cep->tx_bd_base = (cbd_t *)&cp->cp_dpmem[i]; + + cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; + cep->cur_rx = cep->rx_bd_base; + + /* Issue init Rx BD command for SCC1. + * Manual says to perform an Init Rx parameters here. We have + * to perform both Rx and Tx because the SCC may have been + * already running. + * In addition, we have to do it later because we don't yet have + * all of the BD control/status set properly. + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_RX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + */ + + /* Initialize function code registers for big-endian. + */ + ep->sen_genscc.scc_rfcr = SCC_EB; + ep->sen_genscc.scc_tfcr = SCC_EB; + + /* Set maximum bytes per receive buffer. + * This appears to be an Ethernet frame size, not the buffer + * fragment size. It must be a multiple of four. + */ + ep->sen_genscc.scc_mrblr = PKT_MAXBLR_SIZE; + + /* Set CRC preset and mask. + */ + ep->sen_cpres = 0xffffffff; + ep->sen_cmask = 0xdebb20e3; + + ep->sen_crcec = 0; /* CRC Error counter */ + ep->sen_alec = 0; /* alignment error counter */ + ep->sen_disfc = 0; /* discard frame counter */ + + ep->sen_pads = 0x8888; /* Tx short frame pad character */ + ep->sen_retlim = 15; /* Retry limit threshold */ + + ep->sen_maxflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ + ep->sen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ + + ep->sen_maxd1 = PKT_MAXBUF_SIZE; /* maximum DMA1 length */ + ep->sen_maxd2 = PKT_MAXBUF_SIZE; /* maximum DMA2 length */ + + /* Clear hash tables. + */ + ep->sen_gaddr1 = 0; + ep->sen_gaddr2 = 0; + ep->sen_gaddr3 = 0; + ep->sen_gaddr4 = 0; + ep->sen_iaddr1 = 0; + ep->sen_iaddr2 = 0; + ep->sen_iaddr3 = 0; + ep->sen_iaddr4 = 0; + + /* Set Ethernet station address. This must come from the + * Vital Product Data (VPD) EEPROM.....as soon as I get the + * I2C interface working..... + * + * Since we performed a diskless boot, the Ethernet controller + * has been initialized and we copy the address out into our + * own structure. + */ +#ifdef notdef + ep->sen_paddrh = my_enet_addr[0]; + ep->sen_paddrm = my_enet_addr[1]; + ep->sen_paddrl = my_enet_addr[2]; +#else + eap = (unsigned char *)&(ep->sen_paddrh); + for (i=5; i>=0; i--) + dev->dev_addr[i] = *eap++; +#endif + + ep->sen_pper = 0; /* 'cause the book says so */ + ep->sen_taddrl = 0; /* temp address (LSB) */ + ep->sen_taddrm = 0; + ep->sen_taddrh = 0; /* temp address (MSB) */ + + /* Now allocate the host memory pages and initialize the + * buffer descriptors. + */ + bdp = cep->tx_bd_base; + for (i=0; i<TX_RING_SIZE; i++) { + + /* Initialize the BD for every fragment in the page. + */ + bdp->cbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp++; + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + bdp = cep->rx_bd_base; + for (i=0; i<CPM_ENET_RX_PAGES; i++) { + + /* Allocate a page. + */ + mem_addr = __get_free_page(GFP_KERNEL); + + /* Make it uncached. + */ + pte = va_to_pte(&init_task, mem_addr); + pte_val(*pte) |= _PAGE_NO_CACHE; + flush_tlb_page(current->mm->mmap, mem_addr); + + /* Initialize the BD for every fragment in the page. + */ + for (j=0; j<CPM_ENET_RX_FRPPG; j++) { + bdp->cbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR; + bdp->cbd_bufaddr = __pa(mem_addr); + mem_addr += CPM_ENET_RX_FRSIZE; + bdp++; + } + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + /* Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + cep->skb_cur = cep->skb_dirty = 0; + + sccp->scc_scce = 0xffff; /* Clear any pending events */ + + /* Enable interrupts for transmit error, complete frame + * received, and any transmit buffer we have also set the + * interrupt flag. + */ + sccp->scc_sccm = (SCCE_ENET_TXE | SCCE_ENET_RXF | SCCE_ENET_TXB); + + /* Install our interrupt handler. + */ + cpm_install_handler(CPMVEC_SCC1, cpm_enet_interrupt, dev); + + /* Set GSMR_H to enable all normal operating modes. + * Set GSMR_L to enable Ethernet to MC68160. + */ + sccp->scc_gsmrh = 0; + sccp->scc_gsmrl = (SCC_GSMRL_TCI | SCC_GSMRL_TPL_48 | SCC_GSMRL_TPP_10 | SCC_GSMRL_MODE_ENET); + + /* Set sync/delimiters. + */ + sccp->scc_dsr = 0xd555; + + /* Set processing mode. Use Ethernet CRC, catch broadcast, and + * start frame search 22 bit times after RENA. + */ + sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_BRO | SCC_PMSR_NIB22); + + /* It is now OK to enable the Ethernet transmitter. + */ + immap->im_ioport.iop_pcpar |= PC_ENET_TENA; + immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA; + + dev->base_addr = (unsigned long)ep; + dev->priv = cep; + dev->name = "CPM_ENET"; + + /* The CPM Ethernet specific entries in the device structure. */ + dev->open = cpm_enet_open; + dev->hard_start_xmit = cpm_enet_start_xmit; + dev->stop = cpm_enet_close; + dev->get_stats = cpm_enet_get_stats; + dev->set_multicast_list = set_multicast_list; + + /* And last, enable the transmit and receive processing. + */ + sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + printk("CPM ENET Version 0.1, "); + for (i=0; i<5; i++) + printk("%02x:", dev->dev_addr[i]); + printk("%02x\n", dev->dev_addr[5]); + + return 0; +} + +static int +cpm_enet_open(struct device *dev) +{ + + /* I should reset the ring buffers here, but I don't yet know + * a simple way to do that. + */ + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + return 0; /* Always succeed */ +} + +static int +cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct cpm_enet_private *cep = (struct cpm_enet_private *)dev->priv; + volatile cbd_t *bdp; + unsigned long flags; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 20) + return 1; + printk("%s: transmit timed out.\n", dev->name); + cep->stats.tx_errors++; +#ifndef final_version + { + int i; + cbd_t *bdp; + printk(" Ring data dump: cur_tx %x%s cur_rx %x.\n", + cep->cur_tx, cep->tx_full ? " (full)" : "", + cep->cur_rx); + bdp = cep->tx_bd_base; + for (i = 0 ; i < TX_RING_SIZE; i++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + bdp = cep->rx_bd_base; + for (i = 0 ; i < RX_RING_SIZE; i++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + } +#endif + + dev->tbusy=0; + dev->trans_start = jiffies; + + return 0; + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + return 1; + } + + if (test_and_set_bit(0, (void*)&cep->lock) != 0) { + printk("%s: tx queue lock!.\n", dev->name); + /* don't clear dev->tbusy flag. */ + return 1; + } + + /* Fill in a Tx ring entry */ + bdp = cep->cur_tx; + +#ifndef final_version + if (bdp->cbd_sc & BD_ENET_TX_READY) { + /* Ooops. All transmit buffers are full. Bail out. + * This should not happen, since dev->tbusy should be set. + */ + printk("%s: tx queue full!.\n", dev->name); + cep->lock = 0; + return 1; + } +#endif + + /* Clear all of the status flags. + */ + bdp->cbd_sc &= ~BD_ENET_TX_STATS; + + /* If the frame is short, tell CPM to pad it. + */ + if (skb->len <= ETH_ZLEN) + bdp->cbd_sc |= BD_ENET_TX_PAD; + else + bdp->cbd_sc &= ~BD_ENET_TX_PAD; + + /* Set buffer length and buffer pointer. + */ + bdp->cbd_datlen = skb->len; + bdp->cbd_bufaddr = __pa(skb->data); + + /* Save skb pointer. + */ + cep->tx_skbuff[cep->skb_cur] = skb; + + cep->stats.tx_bytes += skb->len; + cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK; + + /* Push the data cache so the CPM does not get stale memory + * data. + */ + /*flush_dcache_range(skb->data, skb->data + skb->len);*/ + + /* Send it on its way. Tell CPM its ready, interrupt when done, + * its the last BD of the frame, and to put the CRC on the end. + */ + bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); + + dev->trans_start = jiffies; + + /* If this was the last BD in the ring, start at the beginning again. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + save_flags(flags); + cli(); + cep->lock = 0; + if (bdp->cbd_sc & BD_ENET_TX_READY) + cep->tx_full = 1; + else + dev->tbusy=0; + restore_flags(flags); + + cep->cur_tx = (cbd_t *)bdp; + + return 0; +} + +/* The interrupt handler. + * This is called from the CPM handler, not the MPC core interrupt. + */ +static void +cpm_enet_interrupt(void *dev_id) +{ + struct device *dev = dev_id; + struct cpm_enet_private *cep; + volatile cbd_t *bdp; + ushort int_events; + int must_restart; + + cep = (struct cpm_enet_private *)dev->priv; + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + dev->interrupt = 1; + + /* Get the interrupt events that caused us to be here. + */ + int_events = cep->sccp->scc_scce; + must_restart = 0; + + /* Handle receive event in its own function. + */ + if (int_events & SCCE_ENET_RXF) + cpm_enet_rx(dev_id); + + /* Check for a transmit error. The manual is a little unclear + * about this, so the debug code until I get it figured out. It + * appears that if TXE is set, then TXB is not set. However, + * if carrier sense is lost during frame transmission, the TXE + * bit is set, "and continues the buffer transmission normally." + * I don't know if "normally" implies TXB is set when the buffer + * descriptor is closed.....trial and error :-). + */ + if (int_events & SCCE_ENET_TXE) { + + /* Transmission errors. + */ + bdp = cep->dirty_tx; +#ifndef final_version + printk("CPM ENET xmit error %x\n", bdp->cbd_sc); + if (bdp->cbd_sc & BD_ENET_TX_READY) + printk("HEY! Enet xmit interrupt and TX_READY.\n"); +#endif + if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ + cep->stats.tx_heartbeat_errors++; + if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ + cep->stats.tx_window_errors++; + if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ + cep->stats.tx_aborted_errors++; + if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ + cep->stats.tx_fifo_errors++; + if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ + cep->stats.tx_carrier_errors++; + + cep->stats.tx_errors++; + + /* No heartbeat or Lost carrier are not really bad errors. + * The others require a restart transmit command. + */ + if (bdp->cbd_sc & + (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) + must_restart = 1; + } + + /* Transmit OK, or non-fatal error. Update the buffer descriptors. + */ + if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) { + cep->stats.tx_packets++; + bdp = cep->dirty_tx; +#ifndef final_version + if (bdp->cbd_sc & BD_ENET_TX_READY) + printk("HEY! Enet xmit interrupt and TX_READY.\n"); +#endif + /* Deferred means some collisions occurred during transmit, + * but we eventually sent the packet OK. + */ + if (bdp->cbd_sc & BD_ENET_TX_DEF) + cep->stats.collisions++; + + /* Free the sk buffer associated with this last transmit. + */ + dev_kfree_skb(cep->tx_skbuff[cep->skb_dirty]/*, FREE_WRITE*/); + cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK; + + /* Update pointer to next buffer descriptor to be transmitted. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + /* I don't know if we can be held off from processing these + * interrupts for more than one frame time. I really hope + * not. In such a case, we would now want to check the + * currently available BD (cur_tx) and determine if any + * buffers between the dirty_tx and cur_tx have also been + * sent. We would want to process anything in between that + * does not have BD_ENET_TX_READY set. + */ + + /* Since we have freed up a buffer, the ring is no longer + * full. + */ + if (cep->tx_full && dev->tbusy) { + cep->tx_full = 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + + cep->dirty_tx = (cbd_t *)bdp; + } + + if (must_restart) { + volatile cpm8xx_t *cp; + + /* Some transmit errors cause the transmitter to shut + * down. We now issue a restart transmit. Since the + * errors close the BD and update the pointers, the restart + * _should_ pick up without having to reset any of our + * pointers either. + */ + cp = cpmp; + cp->cp_cpcr = + mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } + + /* Check for receive busy, i.e. packets coming but no place to + * put them. This "can't happen" because the receive interrupt + * is tossing previous frames. + */ + if (int_events & SCCE_ENET_BSY) { + cep->stats.rx_dropped++; + printk("CPM ENET: BSY can't happen.\n"); + } + + /* Write the SCC event register with the events we have handled + * to clear them. Maybe we should do this sooner? + */ + cep->sccp->scc_scce = int_events; + + dev->interrupt = 0; + + return; +} + +/* During a receive, the cur_rx points to the current incoming buffer. + * When we update through the ring, if the next incoming buffer has + * not been given to the system, we just set the empty indicator, + * effectively tossing the packet. + */ +static int +cpm_enet_rx(struct device *dev) +{ + struct cpm_enet_private *cep; + volatile cbd_t *bdp; + struct sk_buff *skb; + ushort pkt_len; + + cep = (struct cpm_enet_private *)dev->priv; + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = cep->cur_rx; + +for (;;) { + if (bdp->cbd_sc & BD_ENET_RX_EMPTY) + break; + +#ifndef final_version + /* Since we have allocated space to hold a complete frame, both + * the first and last indicators should be set. + */ + if ((bdp->cbd_sc & (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) != + (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) + printk("CPM ENET: rcv is not first+last\n"); +#endif + + /* Frame too long or too short. + */ + if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) + cep->stats.rx_length_errors++; + if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ + cep->stats.rx_frame_errors++; + if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ + cep->stats.rx_crc_errors++; + if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ + cep->stats.rx_crc_errors++; + + /* Report late collisions as a frame error. + * On this error, the BD is closed, but we don't know what we + * have in the buffer. So, just drop this frame on the floor. + */ + if (bdp->cbd_sc & BD_ENET_RX_CL) { + cep->stats.rx_frame_errors++; + } + else { + + /* Process the incoming frame. + */ + cep->stats.rx_packets++; + pkt_len = bdp->cbd_datlen; + cep->stats.rx_bytes += pkt_len; + + /* This does 16 byte alignment, much more than we need. + */ + skb = dev_alloc_skb(pkt_len); + + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + cep->stats.rx_dropped++; + } + else { + skb->dev = dev; + skb_put(skb,pkt_len); /* Make room */ + eth_copy_and_sum(skb, + (unsigned char *)__va(bdp->cbd_bufaddr), + pkt_len, 0); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + } + } + + /* Clear the status flags for this buffer. + */ + bdp->cbd_sc &= ~BD_ENET_RX_STATS; + + /* Mark the buffer empty. + */ + bdp->cbd_sc |= BD_ENET_RX_EMPTY; + + /* Update BD pointer to next entry. + */ + if (bdp->cbd_sc & BD_ENET_RX_WRAP) + bdp = cep->rx_bd_base; + else + bdp++; + + } + cep->cur_rx = (cbd_t *)bdp; + + return 0; +} + +static int +cpm_enet_close(struct device *dev) +{ + /* Don't know what to do yet. + */ + + return 0; +} + +static struct net_device_stats *cpm_enet_get_stats(struct device *dev) +{ + struct cpm_enet_private *cep = (struct cpm_enet_private *)dev->priv; + + return &cep->stats; +} + +/* Set or clear the multicast filter for this adaptor. + * Skeleton taken from sunlance driver. + * The CPM Ethernet implementation allows Multicast as well as individual + * MAC address filtering. Some of the drivers check to make sure it is + * a group multicast address, and discard those that are not. I guess I + * will do the same for now, but just remove the test if you want + * individual filtering as well (do the upper net layers want or support + * this kind of feature?). + */ + +static void set_multicast_list(struct device *dev) +{ + struct cpm_enet_private *cep; + struct dev_mc_list *dmi; + u_char *mcptr, *tdptr; + volatile scc_enet_t *ep; + int i, j; + + cep = (struct cpm_enet_private *)dev->priv; + + /* Get pointer to SCC1 area in parameter RAM. + */ + ep = (scc_enet_t *)dev->base_addr; + + if (dev->flags&IFF_PROMISC) { + /* Log any net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + cep->sccp->scc_pmsr |= SCC_PMSR_PRO; + } else { + + cep->sccp->scc_pmsr &= ~SCC_PMSR_PRO; + + if (dev->flags & IFF_ALLMULTI) { + /* Catch all multicast addresses, so set the + * filter to all 1's. + */ + ep->sen_gaddr1 = 0xffff; + ep->sen_gaddr2 = 0xffff; + ep->sen_gaddr3 = 0xffff; + ep->sen_gaddr4 = 0xffff; + } + else { + /* Clear filter and add the addresses in the list. + */ + ep->sen_gaddr1 = 0; + ep->sen_gaddr2 = 0; + ep->sen_gaddr3 = 0; + ep->sen_gaddr4 = 0; + + dmi = dev->mc_list; + + for (i=0; i<dev->mc_count; i++) { + + /* Only support group multicast for now. + */ + if (!(dmi->dmi_addr[0] & 1)) + continue; + + /* The address in dmi_addr is LSB first, + * and taddr is MSB first. We have to + * copy bytes MSB first from dmi_addr. + */ + mcptr = (u_char *)dmi->dmi_addr + 5; + tdptr = (u_char *)&ep->sen_taddrh; + for (j=0; j<6; j++) + *tdptr++ = *mcptr--; + + /* Ask CPM to run CRC and set bit in + * filter mask. + */ + cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG; + while (cpmp->cp_cpcr & CPM_CR_FLG); + } + } + } +} diff --git a/arch/ppc/8xx_io/uart.c b/arch/ppc/8xx_io/uart.c new file mode 100644 index 000000000..a60eda695 --- /dev/null +++ b/arch/ppc/8xx_io/uart.c @@ -0,0 +1,2530 @@ +/* + * UART driver for MPC860 CPM SCC or SMC + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * I used the serial.c driver as the framework for this driver. + * Give credit to those guys. + * The original code was written for the MBX860 board. I tried to make + * it generic, but there may be some assumptions in the structures that + * have to be fixed later. + * To save porting time, I did not bother to change any object names + * that are not accessed outside of this file. + * It still needs lots of work........When it was easy, I included code + * to support the SCCs, but this has never been tested, nor is it complete. + * Only the SCCs support modem control, so that is not complete either. + * + * This module exports the following rs232 io functions: + * + * int rs_8xx_init(void); + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <asm/uaccess.h> + +#include "commproc.h" + +#ifdef CONFIG_SERIAL_CONSOLE +#include <linux/console.h> + +/* this defines the index into rs_table for the port to use +*/ +#ifndef CONFIG_SERIAL_CONSOLE_PORT +#define CONFIG_SERIAL_CONSOLE_PORT 0 +#endif +#endif + +#define TX_WAKEUP ASYNC_SHARE_IRQ + +static char *serial_name = "CPM UART driver"; +static char *serial_version = "0.01"; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +/* + * Serial driver configuration section. Here are the various options: + */ +#define SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + +#define _INLINE_ inline + +#define DBG_CNT(s) + +/* We overload some of the items in the data structure to meet our + * needs. For example, the port address is the CPM parameter ram + * offset for the SCC or SMC. The maximum number of ports is 4 SCCs and + * 2 SMCs. The "hub6" field is used to indicate the channel number, with + * 0 and 1 indicating the SMCs and 2, 3, 4, and 5 are the SCCs. + * Since these ports are so versatile, I don't yet have a strategy for + * their management. For example, SCC1 is used for Ethernet. Right + * now, just don't put them in the table. Of course, right now I just + * want the SMC to work as a uart :-).. + * The "type" field is currently set to 0, for PORT_UNKNOWN. It is + * not currently used. I should probably use it to indicate the port + * type of CMS or SCC. + * The SMCs do not support any modem control signals. + */ +#define smc_scc_num hub6 +#define SCC_NUM_BASE 2 + +static struct serial_state rs_table[] = { + /* UART CLK PORT IRQ FLAGS NUM */ + { 0, 0, PROFF_SMC1, CPMVEC_SMC1, 0, 0 }, /* SMC1 ttyS0 */ + { 0, 0, PROFF_SMC2, CPMVEC_SMC2, 0, 1 }, /* SMC1 ttyS0 */ +}; + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + +/* The number of buffer descriptors and their sizes. +*/ +#define RX_NUM_FIFO 4 +#define RX_BUF_SIZE 32 +#define TX_NUM_FIFO 4 +#define TX_BUF_SIZE 32 + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* The async_struct in serial.h does not really give us what we + * need, so define our own here. + */ +typedef struct serial_info { + int magic; + int flags; + struct serial_state *state; + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int timeout; + int line; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + unsigned short closing_wait2; + unsigned long event; + unsigned long last_active; + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + + /* CPM Buffer Descriptor pointers. + */ + cbd_t *rx_bd_base; + cbd_t *rx_cur; + cbd_t *tx_bd_base; + cbd_t *tx_cur; +} ser_info_t; + +static void change_speed(ser_info_t *info); +static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout); + +static inline int serial_paranoia_check(ser_info_t *info, + kdev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null async_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +/* + * This is used to figure out the divisor speeds and the timeouts, + * indexed by the termio value. The generic CPM functions are responsible + * for setting and assigning baud rate generators for us. + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; + + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void rs_8xx_stop(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + int idx; + unsigned long flags; + volatile scc_t *sccp; + volatile smc_t *smcp; + + if (serial_paranoia_check(info, tty->device, "rs_stop")) + return; + + save_flags(flags); cli(); + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + smcp->smc_smcm &= ~SMCM_TX; + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm &= ~UART_SCCM_TX; + } + restore_flags(flags); +} + +static void rs_8xx_start(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + int idx; + unsigned long flags; + volatile scc_t *sccp; + volatile smc_t *smcp; + + if (serial_paranoia_check(info, tty->device, "rs_stop")) + return; + + save_flags(flags); cli(); + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + smcp->smc_smcm |= SMCM_TX; + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm |= UART_SCCM_TX; + } + restore_flags(flags); +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c + * + * and look at the resulting assemble code in serial.s. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static _INLINE_ void rs_sched_event(ser_info_t *info, + int event) +{ + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static _INLINE_ void receive_chars(ser_info_t *info) +{ + struct tty_struct *tty = info->tty; + unsigned char ch, *cp; + int ignored = 0; + int i; + ushort status; + struct async_icount *icount; + volatile cbd_t *bdp; + + icount = &info->state->icount; + + /* Just loop through the closed BDs and copy the characters into + * the buffer. + */ + bdp = info->rx_cur; + for (;;) { + if (bdp->cbd_sc & BD_SC_EMPTY) /* If this one is empty */ + break; /* we are all done */ + + /* The read status mask tell us what we should do with + * incoming characters, especially if errors occur. + * One special case is the use of BD_SC_EMPTY. If + * this is not set, we are supposed to be ignoring + * inputs. In this case, just mark the buffer empty and + * continue. + if (!(info->read_status_mask & BD_SC_EMPTY)) { + bdp->cbd_sc |= BD_SC_EMPTY; + bdp->cbd_sc &= + ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); + + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->rx_bd_base; + else + bdp++; + continue; + } + */ + + /* Get the number of characters and the buffer pointer. + */ + i = bdp->cbd_datlen; + cp = (unsigned char *)__va(bdp->cbd_bufaddr); + status = bdp->cbd_sc; + + /* Check to see if there is room in the tty buffer for + * the characters in our BD buffer. If not, we exit + * now, leaving the BD with the characters. We'll pick + * them up again on the next receive interrupt (which could + * be a timeout). + */ + if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE) + break; + + while (i-- > 0) { + ch = *cp++; + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (status & (BD_SC_BR | BD_SC_FR | + BD_SC_PR | BD_SC_OV)) { + /* + * For statistics only + */ + if (status & BD_SC_BR) + icount->brk++; + else if (status & BD_SC_PR) + icount->parity++; + else if (status & BD_SC_FR) + icount->frame++; + if (status & BD_SC_OV) + icount->overrun++; + + /* + * Now check to see if character should be + * ignored, and mask off conditions which + * should be ignored. + if (status & info->ignore_status_mask) { + if (++ignored > 100) + break; + continue; + } + */ + status &= info->read_status_mask; + + if (status & (BD_SC_BR)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (status & BD_SC_PR) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (status & BD_SC_FR) + *tty->flip.flag_buf_ptr = TTY_FRAME; + if (status & BD_SC_OV) { + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = + TTY_OVERRUN; + } + } + } + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + + /* This BD is ready to be used again. Clear status. + * Get next BD. + */ + bdp->cbd_sc |= BD_SC_EMPTY; + bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); + + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->rx_bd_base; + else + bdp++; + } + + info->rx_cur = (cbd_t *)bdp; + + queue_task(&tty->flip.tqueue, &tq_timer); +} + +static _INLINE_ void transmit_chars(ser_info_t *info) +{ + + if (info->flags & TX_WAKEUP) { + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + } + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif +} + +#ifdef notdef + /* I need to do this for the SCCs, so it is left as a reminder. + */ +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + struct async_icount *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + /* update input line counters */ + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + icount->cts++; + wake_up_interruptible(&info->delta_msr_wait); + } + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) + printk("ttys%d CD now %s...", info->line, + (status & UART_MSR_DCD) ? "on" : "off"); +#endif + if (status & UART_MSR_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SERIAL_DEBUG_OPEN + printk("scheduling hangup..."); +#endif + queue_task(&info->tqueue_hangup, + &tq_scheduler); + } + } + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx start..."); +#endif + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if (!(status & UART_MSR_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx stop..."); +#endif + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + } + } +} +#endif + +/* + * This is the serial driver's interrupt routine for a single port + */ +static void rs_8xx_interrupt(void *dev_id) +{ + u_char events; + int idx; + ser_info_t *info; + volatile smc_t *smcp; + + info = (ser_info_t *)dev_id; + + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + } + else { + panic("SCC UART Interrupt....not ready"); + } + + events = smcp->smc_smce; +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d, %x)...", + info->state->smc_scc_num, events); +#endif + if (events & SMCM_RX) + receive_chars(info); + if (events & SMCM_TX) + transmit_chars(info); + smcp->smc_smce = events; +#ifdef modem_control + check_modem_status(info); +#endif + info->last_active = jiffies; +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + + +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using rs_sched_event(), and they get done here. + */ +static void do_serial_bh(void) +{ + run_task_queue(&tq_serial); +} + +static void do_softint(void *private_) +{ + ser_info_t *info = (ser_info_t *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_serial_hangup() -> tty->hangup() -> rs_hangup() + * + */ +static void do_serial_hangup(void *private_) +{ + struct async_struct *info = (struct async_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + tty_hangup(tty); +} + +static void rs_8xx_timer(void) +{ + printk("rs_8xx_timer\n"); +} + + +static int startup(ser_info_t *info) +{ + unsigned long flags; + int retval=0; + int idx; + struct serial_state *state= info->state; + volatile smc_t *smcp; + volatile scc_t *sccp; + volatile smc_uart_t *up; + + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + goto errout; + } + +#ifdef maybe + if (!state->port || !state->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + goto errout; + } +#endif + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttys%d (irq %d)...", info->line, state->irq); +#endif + + +#ifdef modem_control + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#endif + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + + /* + * and set the speed of the serial port + */ + change_speed(info); + + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + + /* Enable interrupts and I/O. + */ + smcp->smc_smcm |= (SMCM_RX | SMCM_TX); + smcp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN); + + /* We can tune the buffer length and idle characters + * to take advantage of the entire incoming buffer size. + * If mrblr is something other than 1, maxidl has to be + * non-zero or we never get an interrupt. The maxidl + * is the number of character times we wait after reception + * of the last character before we decide no more characters + * are coming. + */ + up = (smc_uart_t *)&cpmp->cp_dparam[state->port]; + up->smc_mrblr = 1; /* receive buffer length */ + up->smc_maxidl = 0; /* wait forever for next char */ + up->smc_brkcr = 1; /* number of break chars */ + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm |= UART_SCCM_RX; + } + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(ser_info_t * info) +{ + unsigned long flags; + struct serial_state *state; + int idx; + volatile smc_t *smcp; + volatile scc_t *sccp; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + + /* Disable interrupts and I/O. + */ + smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); +#ifdef CONFIG_SERIAL_CONSOLE + /* We can't disable the transmitter if this is the + * system console. + */ + if (idx != CONFIG_SERIAL_CONSOLE_PORT) +#endif + smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm &= ~UART_SCCM_RX; + } + + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(ser_info_t *info) +{ + int baud_rate; + unsigned cflag, cval, prev_mode; + int i, bits, idx; + unsigned long flags; + volatile smc_t *smcp; + volatile scc_t *sccp; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + + /* Character length programmed into the mode register is the + * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, + * 1 or 2 stop bits, minus 1. + * The value 'bits' counts this for us. + */ + cval = 0; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: bits = 5; break; + case CS6: bits = 6; break; + case CS7: bits = 7; break; + case CS8: bits = 8; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: bits = 8; break; + } + if (cflag & CSTOPB) { + cval |= SMCMR_SL; /* Two stops */ + bits++; + } + if (cflag & PARENB) { + cval |= SMCMR_PEN; + bits++; + } + if (!(cflag & PARODD)) + cval |= SMCMR_PM_EVEN; + + /* Determine divisor based on baud rate */ + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 4) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; + } + + baud_rate = baud_table[i]; + + info->timeout = (TX_BUF_SIZE*HZ*bits); + info->timeout += HZ/50; /* Add .02 seconds of slop */ + +#ifdef modem_control + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + } else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + serial_out(info, UART_IER, info->IER); +#endif + + /* + * Set up parity check flag + */ +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + info->read_status_mask = (BD_SC_EMPTY | BD_SC_OV); + if (I_INPCK(info->tty)) + info->read_status_mask |= BD_SC_FR | BD_SC_PR; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= BD_SC_BR; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= BD_SC_PR | BD_SC_FR; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= BD_SC_BR; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= BD_SC_OV; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + info->read_status_mask &= ~BD_SC_EMPTY; + save_flags(flags); cli(); + + /* Start bit has not been added (so don't, because we would just + * subtract it later), and we need to add one for the number of + * stops bits (there is always at least one). + */ + bits++; + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + + /* Set the mode register. We want to keep a copy of the + * enables, because we want to put them back if they were + * present. + */ + prev_mode = smcp->smc_smcmr; + smcp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART; + smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN)); + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm &= ~UART_SCCM_RX; + } + + mbx_cpm_setbrg(info->state->smc_scc_num, baud_rate); + + restore_flags(flags); +} + +static void rs_8xx_put_char(struct tty_struct *tty, unsigned char ch) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + volatile cbd_t *bdp; + + if (serial_paranoia_check(info, tty->device, "rs_put_char")) + return; + + if (!tty) + return; + + bdp = info->tx_cur; + while (bdp->cbd_sc & BD_SC_READY); + + *((char *)__va(bdp->cbd_bufaddr)) = ch; + bdp->cbd_datlen = 1; + bdp->cbd_sc |= BD_SC_READY; + + /* Get next BD. + */ + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->tx_bd_base; + else + bdp++; + + info->tx_cur = (cbd_t *)bdp; + +} + +static int rs_8xx_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + ser_info_t *info = (ser_info_t *)tty->driver_data; + volatile cbd_t *bdp; + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty) + return 0; + + bdp = info->tx_cur; + + while (1) { + c = MIN(count, TX_BUF_SIZE); + + if (c <= 0) + break; + + if (bdp->cbd_sc & BD_SC_READY) { + info->flags |= TX_WAKEUP; + break; + } + + if (from_user) { + if (c != + copy_from_user(__va(bdp->cbd_bufaddr), buf, c)) { + if (!ret) + ret = -EFAULT; + break; + } + } else { + memcpy(__va(bdp->cbd_bufaddr), buf, c); + } + + bdp->cbd_datlen = c; + bdp->cbd_sc |= BD_SC_READY; + + buf += c; + count -= c; + ret += c; + + /* Get next BD. + */ + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->tx_bd_base; + else + bdp++; + info->tx_cur = (cbd_t *)bdp; + } + return ret; +} + +static int rs_8xx_write_room(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + int ret; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + + if ((info->tx_cur->cbd_sc & BD_SC_READY) == 0) { + info->flags &= ~TX_WAKEUP; + ret = TX_BUF_SIZE; + } + else { + info->flags |= TX_WAKEUP; + ret = 0; + } + return ret; +} + +/* I could track this with transmit counters....maybe later. +*/ +static int rs_8xx_chars_in_buffer(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) + return 0; + return 0; +} + +static void rs_8xx_flush_buffer(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) + return; + + /* There is nothing to "flush", whatever we gave the CPM + * is on its way out. + */ + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + info->flags &= ~TX_WAKEUP; +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_8xx_send_xchar(struct tty_struct *tty, char ch) +{ + volatile cbd_t *bdp; + + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_send_char")) + return; + + bdp = info->tx_cur; + while (bdp->cbd_sc & BD_SC_READY); + + *((char *)__va(bdp->cbd_bufaddr)) = ch; + bdp->cbd_datlen = 1; + bdp->cbd_sc |= BD_SC_READY; + + /* Get next BD. + */ + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->tx_bd_base; + else + bdp++; + + info->tx_cur = (cbd_t *)bdp; +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void rs_8xx_throttle(struct tty_struct * tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_throttle")) + return; + + if (I_IXOFF(tty)) + rs_8xx_send_xchar(tty, STOP_CHAR(tty)); + +#ifdef modem_control + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; + + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); +#endif +} + +static void rs_8xx_unthrottle(struct tty_struct * tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + rs_8xx_send_xchar(tty, START_CHAR(tty)); + } +#ifdef modem_control + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +#ifdef maybe +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int get_lsr_info(struct async_struct * info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + + cli(); + status = serial_in(info, UART_LSR); + sti(); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + return put_user(result,value); +} +#endif + +static int get_modem_info(ser_info_t *info, unsigned int *value) +{ + unsigned int result = 0; +#ifdef modem_control + unsigned char control, status; + + control = info->MCR; + cli(); + status = serial_in(info, UART_MSR); + sti(); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) + | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) + | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) + | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); +#endif + return put_user(result,value); +} + +static int set_modem_info(ser_info_t *info, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + + error = get_user(arg, value); + if (error) + return error; +#ifdef modem_control + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; +#endif + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +#ifdef TIOCM_OUT1 + UART_MCR_OUT1 | + UART_MCR_OUT2 | +#endif + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +#ifdef TIOCM_OUT1 + | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) + | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +#endif + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); +#endif + return 0; +} + +/* Sending a break is a two step process on the SMC/SCC. It is accomplished + * by sending a STOP TRANSMIT command followed by a RESTART TRANSMIT + * command. We take advantage of the begin/end functions to make this + * happen. + */ +static void begin_break(ser_info_t *info) +{ + volatile cpm8xx_t *cp; + ushort chan; + ushort num; + + cp = cpmp; + + if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) { + if (num == 0) + chan = CPM_CR_CH_SMC1; + else + chan = CPM_CR_CH_SMC2; + } + else { + num -= SCC_NUM_BASE; + switch (num) { + case 0: chan = CPM_CR_CH_SCC1; break; + case 1: chan = CPM_CR_CH_SCC2; break; + case 2: chan = CPM_CR_CH_SCC3; break; + case 3: chan = CPM_CR_CH_SCC4; break; + default: return; + } + } + cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); +} + +static void end_break(ser_info_t *info) +{ + volatile cpm8xx_t *cp; + ushort chan; + ushort num; + + cp = cpmp; + + if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) { + if (num == 0) + chan = CPM_CR_CH_SMC1; + else + chan = CPM_CR_CH_SMC2; + } + else { + num -= SCC_NUM_BASE; + switch (num) { + case 0: chan = CPM_CR_CH_SCC1; break; + case 1: chan = CPM_CR_CH_SCC2; break; + case 2: chan = CPM_CR_CH_SCC3; break; + case 3: chan = CPM_CR_CH_SCC4; break; + default: return; + } + } + cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); +} + +/* + * This routine sends a break character out the serial port. + */ +static void send_break(ser_info_t *info, int duration) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; +#ifdef SERIAL_DEBUG_SEND_BREAK + printk("rs_send_break(%d) jiff=%lu...", duration, jiffies); +#endif + begin_break(info); + schedule(); + end_break(info); +#ifdef SERIAL_DEBUG_SEND_BREAK + printk("done jiffies=%lu\n", jiffies); +#endif +} + + +static int rs_8xx_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + ser_info_t *info = (ser_info_t *)tty->driver_data; + int retval; + struct async_icount cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); /* 1/4 second */ + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCSBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + begin_break(info); + return 0; + case TIOCCBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + end_break(info); + return 0; + case TIOCGSOFTCAR: + return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); + case TIOCSSOFTCAR: + error = get_user(arg, (unsigned int *) arg); + if (error) + return error; + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); +#ifdef maybe + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); +#endif + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: +#ifdef modem_control + cli(); + /* note the counters on entry */ + cprev = info->state->icount; + sti(); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + cli(); + cnow = info->state->icount; /* atomic copy */ + sti(); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ +#else + return 0; +#endif + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + cli(); + cnow = info->state->icount; + sti(); + p_cuser = (struct serial_icounter_struct *) arg; + error = put_user(cnow.cts, &p_cuser->cts); + if (error) return error; + error = put_user(cnow.dsr, &p_cuser->dsr); + if (error) return error; + error = put_user(cnow.rng, &p_cuser->rng); + if (error) return error; + error = put_user(cnow.dcd, &p_cuser->dcd); + if (error) return error; + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* FIX UP modem control here someday...... +*/ +static void rs_8xx_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if ( (tty->termios->c_cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info); + +#ifdef modem_control + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(tty->termios->c_cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (tty->termios->c_cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!tty->hw_stopped || + !(tty->termios->c_cflag & CRTSCTS)) { + info->MCR |= UART_MCR_RTS; + } + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_8xx_start(tty); + } +#endif + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_8xx_close(struct tty_struct *tty, struct file * filp) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + struct serial_state *state; + unsigned long flags; + int idx; + volatile smc_t *smcp; + volatile scc_t *sccp; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + DBG_CNT("before DEC-hung"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close ttys%d, count = %d\n", info->line, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->read_status_mask &= ~BD_SC_EMPTY; + if (info->flags & ASYNC_INITIALIZED) { + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + smcp->smc_smcm &= ~SMCM_RX; + smcp->smc_smcmr &= ~SMCMR_REN; + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm &= ~UART_SCCM_RX; + } + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + rs_8xx_wait_until_sent(tty, info->timeout); + } + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + info->close_delay; + schedule(); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; + restore_flags(flags); +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + volatile cbd_t *bdp; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + +#ifdef maybe + if (info->state->type == PORT_UNKNOWN) + return; +#endif + + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = 1; + if (timeout) + char_time = MIN(char_time, timeout); +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + + /* We go through the loop at least once because we can't tell + * exactly when the last character exits the shifter. There can + * be at least two characters waiting to be sent after the buffers + * are empty. + */ + do { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + current->state = TASK_INTERRUPTIBLE; +/* current->counter = 0; /* make us low-priority */ + current->timeout = jiffies + char_time; + schedule(); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + bdp = info->tx_cur; + } while (bdp->cbd_sc & BD_SC_READY); + current->state = TASK_RUNNING; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_8xx_hangup(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + state = info->state; + + rs_8xx_flush_buffer(tty); + shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + ser_info_t *info) +{ +#ifdef DO_THIS_LATER + struct wait_queue wait = { current, NULL }; +#endif + struct serial_state *state = info->state; + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; +#else + return -EAGAIN; +#endif + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + * If this is an SMC port, we don't have modem control to wait + * for, so just get out here. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR)) || + (info->state->smc_scc_num < SCC_NUM_BASE)) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (state->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, state->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; +#ifdef DO_THIS_LATER + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + state->line, state->count); +#endif + cli(); + if (!tty_hung_up_p(filp)) + state->count--; + sti(); + info->blocked_open++; + while (1) { + cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); + sti(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (serial_in(info, UART_MSR) & + UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif +#endif /* DO_THIS_LATER */ + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, ser_info_t **ret_info) +{ + struct serial_state *sstate; + + sstate = rs_table + line; + if (sstate->info) { + sstate->count++; + *ret_info = (ser_info_t *)sstate->info; + return 0; + } + else { + return -ENOMEM; + } +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int rs_8xx_open(struct tty_struct *tty, struct file * filp) +{ + ser_info_t *info; + int retval, line; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + return -ENODEV; + retval = get_async_struct(line, &info); + if (retval) + return retval; + if (serial_paranoia_check(info, tty->device, "rs_open")) + return -ENODEV; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); +#endif + tty->driver_data = info; + info->tty = tty; + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) + return retval; + + MOD_INC_USE_COUNT; + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->state->normal_termios; + else + *tty->termios = info->state->callout_termios; + change_speed(info); + } + + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + return 0; +} + +/* + * /proc fs routines.... + */ + +static int inline line_info(char *buf, struct serial_state *state) +{ +#ifdef notdef + struct async_struct *info = state->info, scr_info; + char stat_buf[30], control, status; +#endif + int ret; + + ret = sprintf(buf, "%d: uart:%s port:%X irq:%d", + state->line, + (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC", + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + +#ifdef notdef + /* + * Figure out the current RS-232 lines + */ + if (!info) { + info = &scr_info; /* This is just for serial_{in,out} */ + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->quot = 0; + info->tty = 0; + } + cli(); + status = serial_in(info, UART_MSR); + control = info ? info->MCR : serial_in(info, UART_MCR); + sti(); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); + + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); +#endif + return ret; +} + +int rs_8xx_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + len += line_info(page + len, &rs_table[i]); + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * rs_init() and friends + * + * rs_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s\n", serial_name, serial_version); +} + +/* + * The serial driver boot-time initialization code! + */ +__initfunc(int rs_8xx_init(void)) +{ + struct serial_state * state; + ser_info_t *info; + uint mem_addr, dp_addr; + int i, j; + ushort chan; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + volatile smc_uart_t *up; + + init_bh(SERIAL_BH, do_serial_bh); +#if 0 + timer_table[RS_TIMER].fn = rs_8xx_timer; + timer_table[RS_TIMER].expires = 0; +#endif + + show_serial_version(); + + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.driver_name = "serial"; + serial_driver.name = "ttyS"; + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.num = NR_PORTS; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = rs_8xx_open; + serial_driver.close = rs_8xx_close; + serial_driver.write = rs_8xx_write; + serial_driver.put_char = rs_8xx_put_char; + serial_driver.write_room = rs_8xx_write_room; + serial_driver.chars_in_buffer = rs_8xx_chars_in_buffer; + serial_driver.flush_buffer = rs_8xx_flush_buffer; + serial_driver.ioctl = rs_8xx_ioctl; + serial_driver.throttle = rs_8xx_throttle; + serial_driver.unthrottle = rs_8xx_unthrottle; + serial_driver.send_xchar = rs_8xx_send_xchar; + serial_driver.set_termios = rs_8xx_set_termios; + serial_driver.stop = rs_8xx_stop; + serial_driver.start = rs_8xx_start; + serial_driver.hangup = rs_8xx_hangup; + serial_driver.wait_until_sent = rs_8xx_wait_until_sent; + serial_driver.read_proc = rs_8xx_read_proc; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cua"; + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; + + if (tty_register_driver(&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver(&callout_driver)) + panic("Couldn't register callout driver\n"); + + cp = cpmp; /* Get pointer to Communication Processor */ + + /* Configure SMCs Tx/Rx instead of port B parallel I/O. + */ + cp->cp_pbpar |= 0x00000cc0; + cp->cp_pbdir &= ~0x00000cc0; + cp->cp_pbodr &= ~0x00000cc0; + + /* Wire BRG1 to SMC1 and BRG2 to SMC2. + */ + cp->cp_simode = 0x10000000; + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = PORT_UNKNOWN; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = callout_driver.init_termios; + state->normal_termios = serial_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + printk(KERN_INFO "ttyS%02d at 0x%04x is a %s\n", + i, state->port, + (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC"); +#ifdef CONFIG_SERIAL_CONSOLE + /* If we just printed the message on the console port, and + * we are about to initialize it for general use, we have + * to wait a couple of character times for the CR/NL to + * make it out of the transmit buffer. + */ + if (i == CONFIG_SERIAL_CONSOLE_PORT) + udelay(2000); +#endif + info = kmalloc(sizeof(ser_info_t), GFP_KERNEL); + if (info) { + memset(info, 0, sizeof(ser_info_t)); + info->magic = SERIAL_MAGIC; + info->flags = state->flags; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->tqueue_hangup.routine = do_serial_hangup; + info->tqueue_hangup.data = info; + info->line = i; + info->state = state; + state->info = (struct async_struct *)info; + + /* Right now, assume we are using SMCs. + */ + sp = &cp->cp_smc[state->smc_scc_num]; + + up = (smc_uart_t *)&cp->cp_dparam[state->port]; + + /* We need to allocate a transmit and receive buffer + * descriptors from dual port ram, and a character + * buffer area from host mem. + */ + dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * RX_NUM_FIFO); + + /* Allocate space for FIFOs in the host memory. + */ + mem_addr = mbx_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE); + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + up->smc_rbase = dp_addr; + info->rx_cur = info->rx_bd_base = (cbd_t *)bdp; + + for (j=0; j<(RX_NUM_FIFO-1); j++) { + bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; + mem_addr += RX_BUF_SIZE; + bdp++; + } + bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; + + dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * TX_NUM_FIFO); + + /* Allocate space for FIFOs in the host memory. + */ + mem_addr = mbx_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE); + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + up->smc_tbase = dp_addr; + info->tx_cur = info->tx_bd_base = (cbd_t *)bdp; + + for (j=0; j<(TX_NUM_FIFO-1); j++) { + bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_sc = BD_SC_INTRPT; + mem_addr += TX_BUF_SIZE; + bdp++; + } + bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT); + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + + /* Set this to 1 for now, so we get single character + * interrupts. Using idle charater time requires + * some additional tuning. + */ + up->smc_mrblr = 1; /* receive buffer length */ + up->smc_maxidl = 0; /* wait forever for next char */ + up->smc_brkcr = 1; /* number of break chars */ + + /* Send the CPM an initialize command. + */ + if (state->smc_scc_num == 0) + chan = CPM_CR_CH_SMC1; + else + chan = CPM_CR_CH_SMC2; + cp->cp_cpcr = mk_cr_cmd(chan, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Disable all interrupts and clear all pending + * events. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Install interrupt handler. + */ + cpm_install_handler(state->irq, rs_8xx_interrupt, info); + + /* Set up the baud rate generator. + */ + mbx_cpm_setbrg(state->smc_scc_num, 9600); + + /* If the port is the console, enable Rx and Tx. + */ +#ifdef CONFIG_SERIAL_CONSOLE + if (i == CONFIG_SERIAL_CONSOLE_PORT) + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; +#endif + } + } + return 0; +} + +/* + * The serial console driver used during boot. Note that these names + * clash with those found in "serial.c", so we currently can't support + * the 16xxx uarts and these at the same time. I will fix this to become + * an indirect function call from tty_io.c (or something). + */ + +#ifdef CONFIG_SERIAL_CONSOLE + +/* + * Print a string to the serial port trying not to disturb any possible + * real use of the port... + */ +static void serial_console_write(struct console *c, const char *s, + unsigned count) +{ + struct serial_state *ser; + ser_info_t *info; + unsigned i; + volatile cbd_t *bdp, *bdbase; + volatile smc_uart_t *up; + volatile u_char *cp; + + ser = rs_table + c->index; + + /* If the port has been initialized for general use, we have + * to use the buffer descriptors allocated there. Otherwise, + * we simply use the single buffer allocated. + */ + if ((info = (ser_info_t *)ser->info) != NULL) { + bdp = info->tx_cur; + bdbase = info->tx_bd_base; + } + else { + /* Pointer to UART in parameter ram. + */ + up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; + + /* Get the address of the host memory buffer. + */ + bdp = bdbase = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + } + + /* + * We need to gracefully shut down the transmitter, disable + * interrupts, then send our bytes out. + */ + + /* + * Now, do each character. This is not as bad as it looks + * since this is a holding FIFO and not a transmitting FIFO. + * We could add the complexity of filling the entire transmit + * buffer, but we would just wait longer between accesses...... + */ + for (i = 0; i < count; i++, s++) { + + /* Wait for transmitter fifo to empty. + * Ready indicates output is ready, and xmt is doing + * that, not that it is ready for us to send. + */ + while (bdp->cbd_sc & BD_SC_READY); + + /* Send the character out. */ + cp = __va(bdp->cbd_bufaddr); + *cp = *s; + bdp->cbd_datlen = 1; + bdp->cbd_sc |= BD_SC_READY; + + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = bdbase; + else + bdp++; + + /* if a LF, also do CR... */ + if (*s == 10) { + while (bdp->cbd_sc & BD_SC_READY); + cp = __va(bdp->cbd_bufaddr); + *cp = 13; + bdp->cbd_datlen = 1; + bdp->cbd_sc |= BD_SC_READY; + + if (bdp->cbd_sc & BD_SC_WRAP) { + bdp = bdbase; + } + else { + bdp++; + } + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + while (bdp->cbd_sc & BD_SC_READY); + + if (info) + info->tx_cur = (cbd_t *)bdp; +} + +/* + * Receive character from the serial port. This only works well + * before the port is initialize for real use. + */ +static int serial_console_wait_key(struct console *co) +{ + struct serial_state *ser; + u_char c, *cp; + ser_info_t *info; + volatile cbd_t *bdp; + volatile smc_uart_t *up; + + ser = rs_table + co->index; + + /* Pointer to UART in parameter ram. + */ + up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; + + /* Get the address of the host memory buffer. + * If the port has been initialized for general use, we must + * use information from the port structure. + */ + if ((info = ser->info)) + bdp = info->rx_cur; + else + bdp = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* + * We need to gracefully shut down the receiver, disable + * interrupts, then read the input. + */ + while (bdp->cbd_sc & BD_SC_EMPTY); /* Wait for a character */ + cp = __va(bdp->cbd_bufaddr); + + if (info) { + if (bdp->cbd_sc & BD_SC_WRAP) { + bdp = info->rx_bd_base; + } + else { + bdp++; + } + info->rx_cur = (cbd_t *)bdp; + } + + c = *cp; + return((int)c); +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTYAUX_MAJOR, 64 + c->index); +} + +/* This must always be called before the rs_8xx_init() function, otherwise + * it blows away the port control information. +*/ +__initfunc(static int serial_console_setup(struct console *co, char *options)) +{ + struct serial_state *ser; + uint mem_addr, dp_addr; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + volatile smc_uart_t *up; + + co->cflag = CREAD|CLOCAL|B9600|CS8; + + ser = rs_table + co->index; + + cp = cpmp; /* Get pointer to Communication Processor */ + + /* Right now, assume we are using SMCs. + */ + sp = &cp->cp_smc[ser->smc_scc_num]; + + /* When we get here, the CPM has been reset, so we need + * to configure the port. + * We need to allocate a transmit and receive buffer descriptor + * from dual port ram, and a character buffer area from host mem. + */ + up = (smc_uart_t *)&cp->cp_dparam[ser->port]; + cp->cp_pbpar = 0x00c0; /* Enable SMC1 instead of Port B I/O */ + + /* Allocate space for two buffer descriptors in the DP ram. + */ + dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * 2); + + /* Allocate space for two 2 byte FIFOs in the host memory. + */ + mem_addr = mbx_cpm_hostalloc(4); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + bdp->cbd_bufaddr = __pa(mem_addr); + (bdp+1)->cbd_bufaddr = __pa(mem_addr+2); + + /* For the receive, set empty and wrap. + * For transmit, set wrap. + */ + bdp->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + (bdp+1)->cbd_sc = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dp_addr; /* Base of receive buffer desc. */ + up->smc_tbase = dp_addr+sizeof(cbd_t); /* Base of xmt buffer desc. */ + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + + /* Set this to 1 for now, so we get single character interrupts. + */ + up->smc_mrblr = 1; /* receive buffer length */ + up->smc_maxidl = 0; /* wait forever for next char */ + + /* Send the CPM an initialize command. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Set up the baud rate generator. + */ + mbx_cpm_setbrg(ser->smc_scc_num, 9600); + + /* And finally, enable Rx and Tx. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + return 0; +} + +static struct console sercons = { + "ttyS", + serial_console_write, + NULL, + serial_console_device, + serial_console_wait_key, + NULL, + serial_console_setup, + CON_PRINTBUFFER, + CONFIG_SERIAL_CONSOLE_PORT, + 0, + NULL +}; + +/* + * Register console. + */ +__initfunc (long console_8xx_init(long kmem_start, long kmem_end)) +{ + register_console(&sercons); + return kmem_start; +} + +#endif diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index 832513c8d..cd81e45a4 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -12,14 +12,7 @@ # Rewritten by Cort Dougan and Paul Mackerras # -ifdef CONFIG_CHRP -# XXX for now -KERNELBASE =0x90000000 -KERNELLOAD =0x90010000 -else -KERNELBASE =0xc0000000 -KERNELLOAD =0xc0000000 -endif +KERNELLOAD =0xc0000000 # PowerPC (cross) tools ifneq ($(shell uname -m),ppc) @@ -32,21 +25,13 @@ ASFLAGS = LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic CFLAGSINC = -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__ CFLAGS := $(CFLAGS) -D__powerpc__ -fsigned-char -msoft-float -pipe \ - -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring \ - -DKERNELBASE=$(KERNELBASE) + -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring CPP = $(CC) -E $(CFLAGS) -ifdef CONFIG_601 -CFLAGS := $(CFLAGS) -mcpu=601 -DCPU=601 +ifdef CONFIG_8xx +CFLAGS := $(CFLAGS) -mcpu=860 endif -ifdef CONFIG_603 -CFLAGS := $(CFLAGS) -mcpu=603 -DCPU=603 -endif - -ifdef CONFIG_604 -CFLAGS := $(CFLAGS) -mcpu=604 -DCPU=604 -endif HEAD := arch/ppc/kernel/head.o @@ -64,6 +49,11 @@ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot MAKECOFFBOOT = $(MAKE) -C arch/$(ARCH)/coffboot MAKECHRPBOOT = $(MAKE) -C arch/$(ARCH)/chrpboot +ifdef CONFIG_8xx +SUBDIRS += arch/ppc/8xx_io +DRIVERS += arch/ppc/8xx_io/8xx_io.a drivers/net/net.a +endif + checks: @$(MAKE) -C arch/$(ARCH)/kernel checks @@ -83,6 +73,19 @@ prep_config: rm -f .config arch/ppc/defconfig ln -s prep_defconfig arch/ppc/defconfig +chrp_config: + rm -f .config arch/ppc/defconfig + ln -s chrp_defconfig arch/ppc/defconfig + +common_config: + rm -f .config arch/ppc/common_defconfig + ln -s common_defconfig arch/ppc/defconfig + +mbx_config: + rm -f .config arch/ppc/defconfig + ln -s mbx_defconfig arch/ppc/defconfig + + tags: etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h} diff --git a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile index 02f95f577..84be805b2 100644 --- a/arch/ppc/boot/Makefile +++ b/arch/ppc/boot/Makefile @@ -25,31 +25,70 @@ ZOFF = 0 ZSZ = 0 IOFF = 0 ISZ = 0 +ifeq ($(CONFIG_MBX),y) +ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000 +else #ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000 ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00600000 +endif GZIP_FLAGS = -v9 -OBJECTS := head.o misc.o vreset.o kbd.o ../coffboot/zlib.o # inflate.o unzip.o -#OBJECTS := crt0.o start.o vreset.o +OBJECTS := head.o misc.o ../coffboot/zlib.o # inflate.o unzip.o CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc +ifeq ($(CONFIG_MBX),y) +OBJECTS += mbxtty.o +CFLAGS += -DCONFIG_MBX +else +OBJECTS += vreset.o kbd.o +endif + all: zImage +ifeq ($(CONFIG_ALL_PPC),y) +CONFIG_PREP = y +endif ifeq ($(CONFIG_PREP),y) -mkprep : mkprep.c - $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c +zvmlinux.initrd: zvmlinux + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp zvmlinux.initrd + $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset zvmlinux.initrd initrd` \ + -DINITRD_SIZE=`sh size zvmlinux.initrd initrd` \ + -DZIMAGE_OFFSET=`sh offset zvmlinux.initrd image` \ + -DZIMAGE_SIZE=`sh size zvmlinux.initrd image` \ + -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp $@ + rm zvmlinux.initrd.tmp -floppy: $(TOPDIR)/vmlinux zImage - dd if=zImage of=/dev/fd0H1440 bs=64b +else +zvmlinux.initrd: +endif -znetboot : zImage - cp zImage /tftpboot/zImage.prep +zImage: zvmlinux mkprep +ifeq ($(CONFIG_PREP),y) + ./mkprep -pbp zvmlinux zImage +endif +ifeq ($(CONFIG_MBX),y) + ln -sf zvmlinux zImage +endif -znetboot.initrd : zImage.initrd - cp zImage.initrd /tftpboot/zImage.prep +zImage.initrd: zvmlinux.initrd mkprep +ifeq ($(CONFIG_PREP),y) + ./mkprep -pbp zvmlinux.initrd zImage.initrd +endif +ifeq ($(CONFIG_MBX),y) + ln -sf zvmlinux.initrd zImage.initrd +endif zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # @@ -63,61 +102,39 @@ zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # then with the offset rebuild the bootloader so we know where the kernel is # $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ - -DZIMAGE_OFFSET=`./offset zvmlinux image` \ - -DZIMAGE_SIZE=`./size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \ + -DZIMAGE_OFFSET=`sh offset zvmlinux image` \ + -DZIMAGE_SIZE=`sh size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \ -c -o misc.o misc.c $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ zvmlinux.tmp $@ rm zvmlinux.tmp -zvmlinux.initrd: zvmlinux - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp zvmlinux.initrd - $(CC) $(CFLAGS) -DINITRD_OFFSET=`./offset zvmlinux.initrd initrd` \ - -DINITRD_SIZE=`./size zvmlinux.initrd initrd` \ - -DZIMAGE_OFFSET=`./offset zvmlinux.initrd image` \ - -DZIMAGE_SIZE=`./size zvmlinux.initrd image` \ - -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp $@ - rm zvmlinux.initrd.tmp - -zImage: zvmlinux mkprep - ./mkprep -pbp zvmlinux zImage - -zImage.initrd: zvmlinux.initrd mkprep - ./mkprep -pbp zvmlinux.initrd zImage.initrd -else -mkprep: - -floppy: - -znetboot: - -znetboot.initrd: - -zvmlinux: - -zvmlinux.initrd: - -zImage: - -zImage.initrd: +floppy: $(TOPDIR)/vmlinux zImage +ifeq ($(CONFIG_PREP),y) + dd if=zImage of=/dev/fd0H1440 bs=64b endif +mkprep : mkprep.c +ifeq ($(CONFIG_PREP),y) + $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c +endif +znetboot : zImage +ifeq ($(CONFIG_PREP),y) + cp zImage /tftpboot/zImage.prep +endif +ifeq ($(CONFIG_MBX),y) + cp zImage /tftpboot/zImage.mbx +endif -# just here to match coffboot/Makefile -vmlinux.coff: - -vmlinux.coff.initrd: +znetboot.initrd : zImage.initrd +ifeq ($(CONFIG_PREP),y) + cp zImage.initrd /tftpboot/zImage.prep +endif +ifeq ($(CONFIG_MBX),y) + cp zImage.initrd /tftpboot/zImage.mbx +endif clean: rm -f vmlinux* zvmlinux* mkprep zImage* @@ -128,3 +145,7 @@ fastdep: dep: $(CPP) -M *.S *.c > .depend +# just here to match coffboot/Makefile +vmlinux.coff: + +vmlinux.coff.initrd: diff --git a/arch/ppc/boot/head.S b/arch/ppc/boot/head.S index ff0030154..26a0be180 100644 --- a/arch/ppc/boot/head.S +++ b/arch/ppc/boot/head.S @@ -1,19 +1,43 @@ +#include <linux/config.h> #include "../kernel/ppc_defs.h" #include "../kernel/ppc_asm.tmpl" #include <asm/processor.h> +#include <asm/cache.h> .text /* * This code is loaded by the ROM loader at some arbitrary location. * Move it to high memory so that it can load the kernel at 0x0000. + * + * The MBX EPPC-Bug understands ELF, so it loads us into the location + * specified in the header. This is a two step process. First, EPPC-Bug + * loads the file into the intermediate buffer memory location specified + * by the environment parameters. When it discovers this is an ELF + * binary, it relocates to the link address for us. Unfortunately, the + * header does not move with the file, so we have to find the + * intermediate load location and read the header from there. From + * information provided by Motorola (thank you), we know this intermediate + * location can be found from the NVRAM environment. + * All of these addresses must be somewhat carefully chosen to make sure + * we don't overlap the regions. I chose to load the kernel at 0, the + * compressed image loads at 0x00100000, and the MBX intermediate buffer + * was set to 0x00200000. Provided the loaded kernel image never grows + * over one megabyte (which I am going to ensure never happens :-), these + * will work fine. When we get called from EPPC-Bug, registers are: + * R1 - Stack pointer at a high memory address. + * R3 - Pointer to Board Information Block. + * R4 - Pointer to argument string. + * Interrupts masked, cache and MMU disabled. */ .globl start start: bl start_ start_: - mr r11,r3 /* Save pointer to residual data */ + mr r11,r3 /* Save pointer to residual/board data */ + +#ifndef CONFIG_MBX mfmsr r3 /* Turn off interrupts */ li r4,0 ori r4,r4,MSR_EE @@ -33,7 +57,6 @@ start_: bne 00b mflr r21 mfctr r22 - bl flush_instruction_cache mtlr r21 mtctr r22 bctr /* Jump to code */ @@ -70,6 +93,8 @@ relocate: mtlr r3 /* Easiest way to do an absolute jump */ blr start_ldr: +#endif /* ndef CONFIG_MBX */ + /* Clear all of BSS */ lis r3,edata@h ori r3,r3,edata@l @@ -89,12 +114,21 @@ start_ldr: li r2,0x000F /* Mask pointer to 16-byte boundary */ andc r1,r1,r2 /* Run loader */ +#ifdef CONFIG_MBX + bl serial_init /* Init MBX serial port */ +#define ILAP_ADDRESS 0xfa000020 + lis r8, ILAP_ADDRESS@h + lwz r8, ILAP_ADDRESS@l(r8) + li r9,end@h + ori r9,r9,end@l + sub r7,r8,r9 + addis r8, r8, 1 /* Add 64K */ +#endif mr r3,r8 /* Load point */ mr r4,r7 /* Program length */ mr r5,r6 /* Checksum */ mr r6,r11 /* Residual data */ bl decompress_kernel - /* changed to use r3 (as firmware does) for kernel as ptr to residual -- Cort*/ lis r6,cmd_line@h @@ -111,45 +145,25 @@ start_ldr: lis r2,initrd_end@h ori r2,r2,initrd_end@l lwz r5,0(r2) - - li r9,0x00c /* Kernel code starts here */ + + /* tell kernel we're prep */ + /* + * get start address of kernel code which is stored as a coff + * entry. see boot/head.S -- Cort + */ + li r9,0x0 + lwz r9,0(r9) mtlr r9 +#ifndef CONFIG_MBX + li r9,0 + lis r10,0xdeadc0de@h + ori r10,r10,0xdeadc0de@l + stw r10,0(r9) +#endif blr hang: b hang - .globl _get_SP -_get_SP: - mr r3,r1 - blr - - .globl _get_PVR -_get_PVR: - mfspr r3,PVR - blr - - .globl _get_MSR -_get_MSR: - mfmsr r3 - blr - - .globl _put_MSR -_put_MSR: - sync - mtmsr r3 - blr - - .globl _get_HID0 -_get_HID0: - mfspr r3,HID0 - blr - - .globl _put_HID0 -_put_HID0: - sync - mtspr HID0,r3 - blr - /* * Delay for a number of microseconds * -- Use the BUS timer (assumes 66MHz) @@ -189,98 +203,4 @@ udelay: blt 2b 3: blr -/* - * 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. - */ -#define NUM_CACHE_LINES 128*8 -#define CACHE_LINE_SIZE 32 -#if 0 -cache_flush_buffer: - .space NUM_CACHE_LINES*CACHE_LINE_SIZE /* CAUTION! these need to match hardware */ -#else -#define cache_flush_buffer 0x1000 -#endif - - -/* - * Flush instruction cache - * *** I'm really paranoid here! - */ -_GLOBAL(flush_instruction_cache) - mflr r5 - bl 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 */ - mtspr HID0,r3 - andc r3,r3,r4 - ori r3,r3,HID0_ICE /* Enable cache */ - mtspr HID0,r3 - mtlr r5 - blr - -/* - * Flush data cache - * *** I'm really paranoid here! - */ -_GLOBAL(flush_data_cache) - lis r3,cache_flush_buffer@h - ori r3,r3,cache_flush_buffer@l - li r4,NUM_CACHE_LINES - mtctr r4 -#if 0 -00: dcbz 0,r3 /* Flush cache line with minimal BUS traffic */ -#else -00: lwz r4,0(r3) -#endif - addi r3,r3,CACHE_LINE_SIZE /* Next line, please */ - bdnz 00b -10: 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) - */ -_GLOBAL(flush_page) - 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 - addi r3,r3,CACHE_LINE_SIZE - bdnz 00b - blr - -/* - * Execute a [foreign] function - * - * run(p1, p2, cp, ep, entry) - * - */ - .globl run -run: - mtctr r7 /* Entry point */ -#define IS_PreP 0x50726550 /* 'PreP' */ - lis r30,IS_PreP>>16 - ori r30,r30,IS_PreP&0xFFFF - mr 11,r5 - mr 12,r6 - mr r28,r5 - mr r29,r6 - mr 11,r5 - mr 12,r6 - bctr - .comm .stack,4096*2,4 diff --git a/arch/ppc/boot/mbxtty.c b/arch/ppc/boot/mbxtty.c new file mode 100644 index 000000000..ee6c59c90 --- /dev/null +++ b/arch/ppc/boot/mbxtty.c @@ -0,0 +1,118 @@ + + +/* Minimal serial functions needed to send messages out the serial + * port on the MBX console. + * + * The MBX uxes SMC1 for the serial port. We reset the port and use + * only the first BD that EPPC-Bug set up as a character FIFO. + * + * It's a big hack, but I don't have time right now....I want a kernel + * that boots. + */ +#include <linux/types.h> +#include <asm/mbx.h> +#include "../8xx_io/commproc.h" + +#define CPM_CPCR ((volatile ushort *)0xfa2009c0) +#define SMC1_MODE ((volatile ushort *)0xfa200a82) +#define SMC1_TBDF ((volatile bd_t *)0xfa202c90) +#define SMC1_RBDF ((volatile bd_t *)0xfa202c10) + +static cpm8xx_t *cpmp = (cpm8xx_t *)&(((immap_t *)MBX_IMAP_ADDR)->im_cpm); + +void +serial_init(void) +{ + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp; + + cp = cpmp; + sp = (smc_t*)&(cp->cp_smc[0]); + up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC1]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcm &= ~(SMCMR_REN | SMCMR_TEN); + + tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase]; + rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase]; + + /* Issue a stop transmit, and wait for it. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. + */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Enable transmitter/receiver. + */ + sp->smc_smcm |= SMCMR_REN | SMCMR_TEN; +} + +void +serial_putchar(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; + tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + + /* Wait for last character to go. + */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY); + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +char +serial_getc() +{ + volatile cbd_t *rbdf; + volatile char *buf; + volatile smc_uart_t *up; + char c; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* Wait for character to show up. + */ + buf = (char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY); + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} diff --git a/arch/ppc/boot/misc.c b/arch/ppc/boot/misc.c index bc6aa088f..33c7c3e0d 100644 --- a/arch/ppc/boot/misc.c +++ b/arch/ppc/boot/misc.c @@ -4,25 +4,35 @@ * Adapted for PowerPC by Gary Thomas * * Rewritten by Cort Dougan (cort@cs.nmt.edu) - * Soon to be replaced by a single bootloader for chrp/prep/pmac. -- Cort + * One day to be replaced by a single bootloader for chrp/prep/pmac. -- Cort */ #include "../coffboot/zlib.h" #include "asm/residual.h" #include <elf.h> +#include <linux/config.h> +#ifdef CONFIG_MBX +#include <asm/mbx.h> +bd_t hold_board_info; +#endif /* this is where the INITRD gets moved to for safe keeping */ -#define INITRD_DESTINATION /*0x00f00000*/ 0x01f00000 +#define INITRD_DESTINATION /*0x00f00000*/ 0x01800000 +#ifdef CONFIG_8xx +char *avail_ram = (char *) 0x00200000; +char *end_avail = (char *) 0x00400000; +#else /* CONFIG_8xx */ /* this will do for now - Cort */ char *avail_ram = (char *) 0x00800000; /* start with 8M */ /* assume 15M max since this is where we copy the initrd to -- Cort */ char *end_avail = (char *) INITRD_DESTINATION; +#endif /* CONFIG_8xx */ +char cmd_line[256]; RESIDUAL hold_residual; unsigned long initrd_start = 0, initrd_end = 0; char *zimage_start; int zimage_size; -char cmd_line[256]; char *vidmem = (char *)0xC00B8000; int lines, cols; @@ -47,6 +57,7 @@ void exit() while(1); } +#ifndef CONFIG_MBX static void clear_screen() { int i, j; @@ -144,6 +155,39 @@ void puts(const char *s) orig_x = x; orig_y = y; } +#else +/* The MBX is just the serial port. +*/ +tstc(void) +{ + return (serial_tstc()); +} + +getc(void) +{ + while (1) { + if (serial_tstc()) return (serial_getc()); + } +} + +void +putc(const char c) +{ + serial_putchar(c); +} + +void puts(const char *s) +{ + char c; + + while ( ( c = *s++ ) != '\0' ) { + serial_putchar(c); + if ( c == '\n' ) + serial_putchar('\r'); + } +} + +#endif /* CONFIG_MBX */ void * memcpy(void * __dest, __const void * __src, int __n) @@ -253,6 +297,8 @@ void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) inflateEnd(&s); } +unsigned char sanity[0x2000]; + unsigned long decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual) { @@ -260,31 +306,60 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R extern unsigned long start; char *cp, ch; unsigned long i; - - + lines = 25; cols = 80; orig_x = 0; orig_y = 24; - /* Turn off MMU. Since we are mapped 1-1, this is OK. */ - flush_instruction_cache(); - _put_HID0(_get_HID0() & ~0x0000C000); - _put_MSR(_get_MSR() & ~0x0030); - +#ifndef CONFIG_8xx vga_init(0xC0000000); - /*clear_screen();*/ + /* copy the residual data */ + if (residual) + memcpy(&hold_residual,residual,sizeof(RESIDUAL)); +#endif /* CONFIG_8xx */ +#ifdef CONFIG_MBX + /* copy board data */ + if (residual) + _bcopy((char *)residual, (char *)&hold_board_info, + sizeof(hold_board_info)); +#endif /* CONFIG_8xx */ + - puts("loaded at: "); puthex(load_addr); + puts("loaded at: "); puthex(load_addr); puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); - - puts("relocated to: "); puthex((unsigned long)&start); + puts("relocated to: "); puthex((unsigned long)&start); puts(" "); puthex((unsigned long)((unsigned long)&start + (4*num_words))); puts("\n"); - + + if ( residual ) + { + puts("board data at: "); puthex((unsigned long)residual); + puts(" "); +#ifdef CONFIG_MBX + puthex((unsigned long)((unsigned long)residual + sizeof(bd_t))); +#else + puthex((unsigned long)((unsigned long)residual + sizeof(RESIDUAL))); +#endif + puts("\n"); + puts("relocated to: "); +#ifdef CONFIG_MBX + puthex((unsigned long)&hold_board_info); +#else + puthex((unsigned long)&hold_residual); +#endif + puts(" "); +#ifdef CONFIG_MBX + puthex((unsigned long)((unsigned long)&hold_board_info + sizeof(bd_t))); +#else + puthex((unsigned long)((unsigned long)&hold_residual + sizeof(RESIDUAL))); +#endif + puts("\n"); + } + zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); zimage_size = ZIMAGE_SIZE; - puts("zimage at: "); puthex((unsigned long)zimage_start); + puts("zimage at: "); puthex((unsigned long)zimage_start); puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); if ( INITRD_OFFSET ) @@ -296,18 +371,19 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R /* relocate initrd */ if ( initrd_start ) { - puts("initrd at: "); puthex(initrd_start); + puts("initrd at: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); memcpy ((void *)INITRD_DESTINATION,(void *)initrd_start, INITRD_SIZE ); initrd_end = INITRD_DESTINATION + INITRD_SIZE; initrd_start = INITRD_DESTINATION; - puts("Moved initrd to: "); puthex(initrd_start); + puts("Moved initrd to: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); } - +#ifndef CONFIG_MBX CRT_tstc(); /* Forces keyboard to be initialized */ +#endif puts("\nLinux/PPC load: "); timer = 0; cp = cmd_line; @@ -339,9 +415,12 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R if ( initrd_start > (16<<20)) puts("initrd_start located > 16M\n"); - puts("Uncompressing Linux..."); - gunzip(0, 0x400000, zimage_start, &zimage_size); + + /* these _bcopy() calls are here so I can add breakpoints to the boot for mbx -- Cort */ + /*_bcopy( (char *)0x100,(char *)&sanity, 0x2000-0x100);*/ + gunzip(0, 0x400000, zimage_start, &zimage_size); + /*_bcopy( (char *)&sanity,(char *)0x100,0x2000-0x100);*/ puts("done.\n"); puts("Now booting the kernel\n"); return (unsigned long)&hold_residual; diff --git a/arch/ppc/boot/vreset.c b/arch/ppc/boot/vreset.c index 3bc3936c6..6086372a0 100644 --- a/arch/ppc/boot/vreset.c +++ b/arch/ppc/boot/vreset.c @@ -434,16 +434,15 @@ vga_init(unsigned char *ISA_mem) { int slot; struct VgaRegs *VgaTextRegs; - +#if 0 if ((_get_PVR()>>16) == PPC_601) { return(old_vga_init(ISA_mem)); } - -#if 1 +#endif + /* 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... */ @@ -522,11 +521,6 @@ vga_init(unsigned char *ISA_mem) return (1); /* 'CRT' I/O supported */ } -static int -NOP(int x) -{ -} - /* * Write to VGA Attribute registers. */ @@ -852,166 +846,5 @@ void printslots(void) 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] = { - 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, - 0x00, 0x4F, 0x0D, 0x0E, 0x00, 0x00, 0x00, 0x00, /*0x07, 0x80, */ - 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}; - -#if 0 -static const unsigned char color_LUT[] = - { - 0x00, 0x00, 0x00, /* 0 - black */ - 0x00, 0x00, 0x2A, /* 1 - blue */ - 0x00, 0x2A, 0x00, /* 2 - green */ - 0x00, 0x2A, 0x2A, /* 3 - cyan */ - 0x2A, 0x00, 0x00, /* 4 - red */ - 0x2A, 0x00, 0x2A, /* 5 - magenta */ - 0x2A, 0x2A, 0x00, /* 6 - brown */ - 0x2A, 0x2A, 0x2A, /* 7 - white */ - 0x00, 0x00, 0x15, /* 8 - gray */ - 0x00, 0x00, 0x3F, /* 9 - light blue */ - 0x00, 0x2A, 0x15, /* 10 - light green */ - 0x00, 0x2A, 0x3F, /* 11 - light cyan */ - 0x2A, 0x00, 0x15, /* 12 - light red */ - 0x2A, 0x00, 0x3F, /* 13 - light magenta */ - 0x2A, 0x2A, 0x15, /* 14 - yellow */ - 0x2A, 0x2A, 0x3F, /* 15 - bright white */ - }; -#endif - -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; - - /* 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(0x3C2, 0x67); - -#if 0 - /* Initialize DAC */ - outb(0x3C6,0xFF); - inb(0x3C7); - outb(0x3C8,0x00); - inb(0x3C7); - for (i=0; i<sizeof(color_LUT); i++) { - outb(0x3C9, color_LUT[i]); - } - for (i; i<768; i += 3) { - outb(0x3C9, 0x3F); /* White? */ - outb(0x3C9, 0x3F); /* White? */ - outb(0x3C9, 0x3F); /* White? */ - } - - /* Load font */ - NOP(inb(0x3DA)); /* Reset Address/Data FlipFlop for Attribute ctlr */ - outb(0x3C0,0x30); outb(0x3C0, 0x01); /* graphics mode */ - outw(0x3C4, 0x0001); /* reset sequencer */ - outw(0x3C4, 0x0204); /* write to plane 2 */ - outw(0x3C4, 0x0407); /* 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, 0x0600); /* set graphics */ - for (i = 0; i < sizeof(font); i += 16) { - for (j = 0; j < 16; j++) { - font_page[(2*i)+j] = font[i+j]; - } - } -#else - outw(0x3C4, 0x0120); /* disable video */ - setTextCLUT(2); /* load color lookup table */ - loadFont(ISA_mem); /* load font */ -#endif - - for (i = 0; i < 24; i++) { - outb(0x3D4, i); - outb(0x3D5, CRTC[i]); - } - for (i = 0; i < 5; i++) { - outb(0x3C4, i); - outb(0x3C5, SEQ[i]); - } - for (i = 0; i < 9; i++) { - outb(0x3CE, i); - outb(0x3CF, GC[i]); - } - value = inb(0x3DA); /* reset flip-flop */ - for (i = 0; i < 16; i++) { - outb(0x3C0, i); - outb(0x3C0, AC[i]); - } - for (i = 16; i < 21; i++) { - outb(0x3C0, i | 0x20); - outb(0x3C0, AC[i]); } - clearVideoMemory(); - outw(0x3C4, 0x0100); /* re-enable video */ - outb(0x3C2, 0x23); - return (1); /* Keyboard should work */ } diff --git a/arch/ppc/chrp_defconfig b/arch/ppc/chrp_defconfig new file mode 100644 index 000000000..ccbc0f942 --- /dev/null +++ b/arch/ppc/chrp_defconfig @@ -0,0 +1,321 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_6xx=y +# CONFIG_8xx is not set +# CONFIG_PMAC is not set +# CONFIG_PREP is not set +CONFIG_CHRP=y +# CONFIG_ALL_PPC is not set +# CONFIG_MBX is not set +CONFIG_MACH_SPECIFIC=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_PCI_OLD_PROC=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set +# CONFIG_ABSTRACT_CONSOLE is not set +CONFIG_PMAC_CONSOLE=y +CONFIG_MAC_KEYBOARD=y +# CONFIG_MAC_FLOPPY is not set +CONFIG_MACMOUSE=y +CONFIG_PROC_DEVICETREE=y +# CONFIG_XMON is not set +CONFIG_CONTROL_VIDEO=y +CONFIG_PLATINUM_VIDEO=y +CONFIG_VALKYRIE_VIDEO=y +CONFIG_ATY_VIDEO=y +CONFIG_IMSTT_VIDEO=y +CONFIG_CHIPS_VIDEO=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_IDEPCI is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_HD is not set + +# +# NEW devices (io_request, all ALPHA and dangerous) +# +# CONFIG_IO_REQUEST is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +CONFIG_NET_ALIAS=y +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_ALIAS=y +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_RARP=y +CONFIG_IP_NOSR=y +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK 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_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set +# CONFIG_CPU_IS_SLOW is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NET_PROFILE 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=y +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set + +# +# 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=m +# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set +# CONFIG_OVERRIDE_CMDS is not set +# CONFIG_AIC7XXX_PAGE_ENABLE is not set +CONFIG_AIC7XXX_PROC_STATS=y +CONFIG_AIC7XXX_RESET_DELAY=15 +# 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_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_NCR53C8XX is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I 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_DEBUG is not set +CONFIG_SCSI_MESH=y +CONFIG_SCSI_MESH_SYNC_RATE=10 +CONFIG_SCSI_MAC53C94=y + +# +# Network device support +# +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 is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +# CONFIG_PCNET32 is not set +# 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_TLAN 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_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO 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 +CONFIG_CDROM=y + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=m +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +CONFIG_NFSD=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set +# CONFIG_SMB_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=y +# CONFIG_ROMFS_FS is not set +CONFIG_AUTOFS_FS=y +# CONFIG_UFS_FS is not set +# CONFIG_ADFS_FS is not set +CONFIG_MAC_PARTITION=y +CONFIG_NLS=y + +# +# Native Language Support +# +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_SOFTCURSOR is not set +CONFIG_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_MOUSE is not set +# CONFIG_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +CONFIG_NVRAM=y +# CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/chrpboot/main.c b/arch/ppc/chrpboot/main.c index 629256eaa..05a2f85d3 100644 --- a/arch/ppc/chrpboot/main.c +++ b/arch/ppc/chrpboot/main.c @@ -16,11 +16,11 @@ void gunzip(void *, int, unsigned char *, int *); #define get_16be(x) (*(unsigned short *)(x)) #define get_32be(x) (*(unsigned *)(x)) -#define RAM_START 0x90000000 -#define RAM_END 0x90800000 /* only 8M mapped with BATs */ +#define RAM_START 0x00000000 +#define RAM_END 0x00800000 /* only 8M mapped with BATs */ -#define RAM_FREE 0x90540000 /* after image of chrpboot */ -#define PROG_START 0x90010000 +#define RAM_FREE 0x00540000 /* after image of chrpboot */ +#define PROG_START 0x00010000 char *avail_ram; char *end_avail; @@ -40,7 +40,7 @@ chrpboot(int a1, int a2, void *prom) unsigned initrd_start, initrd_size; printf("chrpboot starting\n\r"); - setup_bats(); + /* setup_bats(); */ if (initrd_len) { initrd_size = initrd_len; diff --git a/arch/ppc/chrpboot/misc.S b/arch/ppc/chrpboot/misc.S index 11a612af1..b2094b9d3 100644 --- a/arch/ppc/chrpboot/misc.S +++ b/arch/ppc/chrpboot/misc.S @@ -23,10 +23,10 @@ setup_bats: b 5f 4: ori 4,4,0xff /* set up BAT registers for 604 */ li 5,2 - mtdbatu 0,4 - mtdbatl 0,5 -5: mtibatu 0,4 - mtibatl 0,5 + mtdbatu 3,4 + mtdbatl 3,5 +5: mtibatu 3,4 + mtibatl 3,5 isync blr diff --git a/arch/ppc/coffboot/main.c b/arch/ppc/coffboot/main.c index ab0ee6c4a..e70844b62 100644 --- a/arch/ppc/coffboot/main.c +++ b/arch/ppc/coffboot/main.c @@ -18,9 +18,10 @@ void gunzip(void *, int, unsigned char *, int *); #define get_32be(x) (*(unsigned *)(x)) #define RAM_START 0xc0000000 -#define RAM_END 0xc0800000 /* only 8M mapped with BATs */ +#define PROG_START RAM_START +#define RAM_END (RAM_START + 0x800000) /* only 8M mapped with BATs */ -#define RAM_FREE 0xc0540000 /* after image of coffboot */ +#define RAM_FREE (RAM_START + 0x540000) /* after image of coffboot */ char *avail_ram; char *end_avail; @@ -47,7 +48,7 @@ coffboot(int a1, int a2, void *prom) printf("error getting load-base\n"); exit(); } - setup_bats(); + setup_bats(RAM_START); eh = (struct external_filehdr *) loadbase; ns = get_16be(eh->f_nscns); @@ -82,7 +83,7 @@ coffboot(int a1, int a2, void *prom) im = (unsigned char *)(loadbase + get_32be(isect->s_scnptr)); len = get_32be(isect->s_size); - dst = (void *) RAM_START; + dst = (void *) PROG_START; if (im[0] == 0x1f && im[1] == 0x8b) { void *cp = (void *) RAM_FREE; @@ -98,7 +99,7 @@ coffboot(int a1, int a2, void *prom) flush_cache(dst, len); - sa = *(unsigned *)dst + RAM_START; + sa = *(unsigned *)dst + PROG_START; printf("start address = 0x%x\n", sa); #if 0 diff --git a/arch/ppc/coffboot/misc.S b/arch/ppc/coffboot/misc.S index ae08ee2fa..1a9f07129 100644 --- a/arch/ppc/coffboot/misc.S +++ b/arch/ppc/coffboot/misc.S @@ -9,24 +9,25 @@ .text /* - * Use the BAT0 registers to map the 1st 8MB of RAM to 0xc0000000. + * Use the BAT0 registers to map the 1st 8MB of RAM to + * the address given as the 1st argument. */ .globl setup_bats setup_bats: + mr 4,3 mfpvr 3 rlwinm 3,3,16,16,31 /* r3 = 1 for 601, 4 for 604 */ cmpi 0,3,1 - lis 4,0xc000 bne 4f ori 4,4,4 /* set up BAT registers for 601 */ li 5,0x7f b 5f 4: ori 4,4,0xff /* set up BAT registers for 604 */ li 5,2 - mtdbatu 0,4 - mtdbatl 0,5 -5: mtibatu 0,4 - mtibatl 0,5 + mtdbatu 3,4 + mtdbatl 3,5 +5: mtibatu 3,4 + mtibatl 3,5 isync blr diff --git a/arch/ppc/common_defconfig b/arch/ppc/common_defconfig new file mode 100644 index 000000000..4f9983126 --- /dev/null +++ b/arch/ppc/common_defconfig @@ -0,0 +1,331 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_6xx=y +# CONFIG_8xx is not set +# CONFIG_PMAC is not set +# CONFIG_PREP is not set +# CONFIG_CHRP is not set +CONFIG_ALL_PPC=y +# CONFIG_MBX is not set + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_PCI_OLD_PROC=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set +CONFIG_ABSTRACT_CONSOLE=y +CONFIG_FB=y +CONFIG_VGA_CONSOLE=y +CONFIG_FB_COMPAT_XPMAC=y +CONFIG_MAC_KEYBOARD=y +CONFIG_MAC_FLOPPY=y +CONFIG_MACMOUSE=y +CONFIG_PROC_DEVICETREE=y +# CONFIG_XMON is not set + +# +# 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_IDEPCI is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_HD is not set + +# +# NEW devices (io_request, all ALPHA and dangerous) +# +# CONFIG_IO_REQUEST is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +CONFIG_IP_ACCT=y +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +CONFIG_SYN_COOKIES=y +CONFIG_INET_RARP=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_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_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set +# CONFIG_CPU_IS_SLOW is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NET_PROFILE 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 is not set +# CONFIG_SCSI_LOGGING is not set + +# +# 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_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set +CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y +CONFIG_SCSI_NCR53C8XX_IOMAPPED=y +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 +CONFIG_SCSI_NCR53C8XX_SYNC=5 +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I 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=y +CONFIG_SCSI_MESH_SYNC_RATE=5 +CONFIG_SCSI_MAC53C94=y + +# +# Network device support +# +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 is not set +CONFIG_LANCE=y +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN 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_TLAN 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_PPP=y +# CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO 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 +CONFIG_CDROM=y + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +CONFIG_NFSD=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set +# CONFIG_SMB_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=y +# CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_ADFS_FS is not set +CONFIG_MAC_PARTITION=y +CONFIG_NLS=y + +# +# Native Language Support +# +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set + +# +# Frame buffer devices +# +CONFIG_FB_OPEN_FIRMWARE=y +# CONFIG_FB_S3TRIO is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FBCON_ADVANCED is not set +CONFIG_FBCON_MFB=y +CONFIG_FBCON_CFB8=y + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_SOFTCURSOR is not set +CONFIG_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD 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_PC110_PAD is not set +# CONFIG_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/config.in b/arch/ppc/config.in index 72bab8928..4ab631988 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -1,30 +1,37 @@ -# $Id: config.in,v 1.36 1997/12/29 21:36:52 geert Exp $ +# $Id: config.in,v 1.47 1998/04/10 10:19:04 geert Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # mainmenu_name "Linux/PowerPC Kernel Configuration" + + mainmenu_option next_comment comment 'Platform support' define_bool CONFIG_PPC y -if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then - define_bool CONFIG_CROSSCOMPILE y -else - define_bool CONFIG_NATIVE y -fi - -define_bool CONFIG_MACH_SPECIFIC y -bool 'Build PowerMac Kernel (not PReP or CHRP)?' CONFIG_PMAC -bool 'Build PReP Kernel (not PowerMac or CHRP)?' CONFIG_PREP -bool 'Build CHRP Kernel (not PReP or PowerMac)?' CONFIG_CHRP +#if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then +# define_bool CONFIG_CROSSCOMPILE y +#else +# define_bool CONFIG_NATIVE y +#fi choice 'Processor type' \ - "Common CONFIG_COMMON \ - 601 CONFIG_601 \ - 603 CONFIG_603 \ - 604 CONFIG_604" Common + "6xx/7xx CONFIG_6xx \ + 860/821 CONFIG_8xx" 6xx/7xx + +choice 'Machine Type' \ + "PowerMac CONFIG_PMAC \ + PReP CONFIG_PREP \ + CHRP CONFIG_CHRP \ + PowerMac/PReP/CHRP CONFIG_ALL_PPC \ + APUS CONFIG_APUS \ + MBX CONFIG_MBX" PReP endmenu +if [ "$CONFIG_ALL_PPC" != "y" ]; then + define_bool CONFIG_MACH_SPECIFIC y +fi + mainmenu_option next_comment comment 'General setup' @@ -35,9 +42,16 @@ if [ "$CONFIG_MODULES" = "y" ]; then bool 'Kernel module loader' CONFIG_KMOD fi -define_bool CONFIG_PCI y +if [ "$CONFIG_APUS" = "y" ]; then + define_bool CONFIG_PCI n +else + define_bool CONFIG_PCI y +fi if [ "$CONFIG_PREP" = "y" ]; then - bool 'PCI bridge optimization' CONFIG_PCI_OPTIMIZE + bool 'PCI quirks' CONFIG_PCI_QUIRKS + if [ "$CONFIG_PCI_QUIRKS" = "y" ]; then + bool ' PCI bridge optimization' CONFIG_PCI_OPTIMIZE + fi fi bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC bool 'Networking support' CONFIG_NET @@ -51,6 +65,14 @@ define_bool CONFIG_KERNEL_ELF y tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA +tristate 'Parallel port support' CONFIG_PARPORT +if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT + if [ "$CONFIG_PARPORT_PC" != "n" ]; then + bool ' Support foreign hardware' CONFIG_PARPORT_OTHER + fi +fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'New unified console driver (EXPERIMENTAL)' CONFIG_ABSTRACT_CONSOLE fi @@ -68,6 +90,7 @@ else # if compiling specifically for prep or chrp, or supporting all arch's if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then bool 'Support for frame buffer devices' CONFIG_FB + bool 'Support for VGA devices' CONFIG_VGA_CONSOLE bool 'Backward compatibility mode for Xpmac' CONFIG_FB_COMPAT_XPMAC else bool 'Support for PowerMac console' CONFIG_PMAC_CONSOLE @@ -79,13 +102,10 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'Support for PowerMac mouse (EXPERIMENTAL)' CONFIG_MACMOUSE fi bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE +bool 'Include kgdb kernel debugger' CONFIG_KGDB bool 'Include xmon kernel debugger' CONFIG_XMON -if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then - if [ "$CONFIG_FB" != "y" ]; then - define_bool CONFIG_VGA_CONSOLE y - fi -else +if [ "$CONFIG_ABSTRACT_CONSOLE" != "y" ]; then if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then bool 'Support for Apple "control" display' CONFIG_CONTROL_VIDEO bool 'Support for Apple "platinum" display' CONFIG_PLATINUM_VIDEO @@ -149,17 +169,6 @@ if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then fi endmenu -# Conditionally compile in the Uniform CD-ROM driver -if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then - define_bool CONFIG_CDROM y -else - if [ "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then - define_bool CONFIG_CDROM m - else - define_bool CONFIG_CDROM n - fi -fi - source fs/Config.in source fs/nls/Config.in diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig index 7e5caa71d..e498b5249 100644 --- a/arch/ppc/defconfig +++ b/arch/ppc/defconfig @@ -6,22 +6,21 @@ # Platform support # CONFIG_PPC=y -CONFIG_NATIVE=y -CONFIG_MACH_SPECIFIC=y +# CONFIG_6xx is not set +CONFIG_8xx=y # CONFIG_PMAC is not set -CONFIG_PREP=y +# CONFIG_PREP is not set # CONFIG_CHRP is not set -CONFIG_COMMON=y +# CONFIG_ALL_PPC is not set +CONFIG_MBX=y +CONFIG_MACH_SPECIFIC=y # # General setup # -CONFIG_EXPERIMENTAL=y -CONFIG_MODULES=y -CONFIG_MODVERSIONS=y -CONFIG_KMOD=y +# CONFIG_EXPERIMENTAL is not set +# CONFIG_MODULES is not set CONFIG_PCI=y -# CONFIG_PCI_OPTIMIZE is not set CONFIG_PCI_OLD_PROC=y CONFIG_NET=y # CONFIG_SYSCTL is not set @@ -31,6 +30,7 @@ CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set # CONFIG_PMAC_CONSOLE is not set # CONFIG_MAC_KEYBOARD is not set # CONFIG_MAC_FLOPPY is not set @@ -46,25 +46,17 @@ CONFIG_VGA_CONSOLE=y # # 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_FD is not set +# CONFIG_BLK_DEV_IDE is not set +# CONFIG_BLK_DEV_HD_ONLY is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set -# CONFIG_BLK_DEV_EZ is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set # CONFIG_BLK_DEV_HD is not set # @@ -79,83 +71,30 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set -# CONFIG_IP_PNP is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set # CONFIG_IP_ACCT is not set -# CONFIG_IP_MASQUERADE is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_ALIAS is not set # CONFIG_SYN_COOKIES is not set -CONFIG_INET_RARP=y -# CONFIG_IP_NOSR is not set -CONFIG_SKB_LARGE=y -# CONFIG_IPV6 is not set +# CONFIG_INET_RARP is not set +CONFIG_IP_NOSR=y +# CONFIG_SKB_LARGE 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_CPU_IS_SLOW is not set -# CONFIG_NET_SCHED 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 is not set - -# -# 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 is not set -CONFIG_SCSI_NCR53C8XX=y -# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set -CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y -CONFIG_SCSI_NCR53C8XX_IOMAPPED=y -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 -CONFIG_SCSI_NCR53C8XX_SYNC=5 -# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT 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 is not set # # Network device support @@ -164,34 +103,28 @@ CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set -# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y # CONFIG_NET_VENDOR_3COM is not set -CONFIG_LANCE=y +# CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL 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_TLAN is not set -# CONFIG_ES3210 is not set -# CONFIG_ZNET is not set +# CONFIG_NET_EISA 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=m -# CONFIG_NET_RADIO is not set +# CONFIG_PPP is not set # CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set # CONFIG_TR is not set -# CONFIG_SHAPER is not set +# CONFIG_WAN_DRIVERS is not set +# CONFIG_LAPBETHER is not set +# CONFIG_X25_ASY is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set # # ISDN subsystem @@ -202,6 +135,7 @@ CONFIG_PPP=m # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set +# CONFIG_CDROM is not set # # Filesystems @@ -209,63 +143,52 @@ CONFIG_PPP=m # CONFIG_QUOTA is not set # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=y -# CONFIG_JOLIET is not set +# CONFIG_ISO9660_FS is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set -CONFIG_PROC_FS=y +# CONFIG_PROC_FS is not set CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set +# CONFIG_HFS_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 is not set - -# -# Native Language Support -# # CONFIG_NLS is not set # # Character devices # -CONFIG_VT=y -CONFIG_VT_CONSOLE=y -# CONFIG_SOFTCURSOR is not set +# CONFIG_VT is not set 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=y +# CONFIG_SERIAL_EXTENDED 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_PC110_PAD is not set -# CONFIG_UMISC is not set +# CONFIG_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 # CONFIG_VIDEO_DEV is not set -# CONFIG_VIDEO_BT848 is not set # CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set # # Sound diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 37776109d..3561fd26a 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -11,14 +11,32 @@ $(CC) $(CFLAGS) -D__ASSEMBLY__ -c $< -o $*.o O_TARGET := kernel.o -O_OBJS := misc.o traps.o process.o signal.o syscalls.o \ - align.o ptrace.o irq.o openpic.o bitops.o ppc_htab.o idle.o \ - time.o prep_time.o pmac_time.o chrp_time.o \ - setup.o prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \ - pci.o prep_pci.o pmac_pci.o chrp_pci.o \ - residual.o prom.o OX_OBJS := ppc_ksyms.o + +O_OBJS := traps.o irq.o idle.o time.o process.o signal.o syscalls.o misc.o \ + bitops.o ppc_htab.o setup.o ptrace.o align.o + +ifdef CONFIG_PCI +O_OBJS += pci.o +endif +ifdef CONFIG_KGDB +O_OBJS += ppc-stub.o +endif + +ifeq ($(CONFIG_MBX),y) +O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o +else +ifeq ($(CONFIG_APUS),y) +O_OBJS += prom.o openpic.o +else +O_OBJS += prep_time.o pmac_time.o chrp_time.o \ + prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \ + prep_pci.o pmac_pci.o chrp_pci.o \ + residual.o prom.o openpic.o +endif +endif + ifdef SMP O_OBJS += smp.o endif @@ -38,10 +56,10 @@ ppc_defs.h: mk_defs.c ppc_defs.head \ rm mk_defs.s find_name : find_name.c - $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o find_name find_name.c + $(HOSTCC) -o find_name find_name.c checks: checks.c - $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c + $(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c ./checks include $(TOPDIR)/Rules.make diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c index 39cb04f77..70284674b 100644 --- a/arch/ppc/kernel/align.c +++ b/arch/ppc/kernel/align.c @@ -194,9 +194,13 @@ fix_alignment(struct pt_regs *regs) return -EFAULT; /* bad address */ } +#ifdef __SMP__ + if ((flags & F) && (regs->msr & MSR_FP) ) + smp_giveup_fpu(current); +#else if ((flags & F) && last_task_used_math == current) giveup_fpu(); - +#endif if (flags & M) return 0; /* too hard for now */ @@ -255,12 +259,22 @@ fix_alignment(struct pt_regs *regs) * the kernel with -msoft-float so it doesn't use the * fp regs for copying 8-byte objects. */ case LD+F+S: +#ifdef __SMP__ + if (regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else giveup_fpu(); +#endif cvt_fd(&data.f, ¤t->tss.fpr[reg]); /* current->tss.fpr[reg] = data.f; */ break; case ST+F+S: +#ifdef __SMP__ + if (regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else giveup_fpu(); +#endif cvt_df(¤t->tss.fpr[reg], &data.f); /* data.f = current->tss.fpr[reg]; */ break; diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index 16b379254..6036efc28 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -4,13 +4,13 @@ #include <linux/kernel.h> #include <linux/pci.h> -#include <linux/bios32.h> #include <linux/delay.h> #include <linux/string.h> #include <linux/init.h> #include <linux/openpic.h> #include <asm/io.h> +#include <asm/pgtable.h> #include <asm/irq.h> #include <asm/hydra.h> #include <asm/prom.h> @@ -22,8 +22,12 @@ volatile struct Hydra *Hydra = NULL; - #if 1 +/* + * The VLSI Golden Gate II has only 512K of PCI configuration space, so we + * limit the bus number to 3 bits + */ + int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { @@ -32,11 +36,6 @@ int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, return PCIBIOS_DEVICE_NOT_FOUND; } *val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset)); - if (offset == PCI_INTERRUPT_LINE) { - /* PCI interrupts are controlled by the OpenPIC */ - if (*val) - *val = openpic_to_irq(*val); - } return PCIBIOS_SUCCESSFUL; } @@ -228,10 +227,11 @@ __initfunc(int w83c553f_init(void)) unsigned char t8; unsigned short t16; unsigned int t32; - if (pcibios_find_device(PCI_VENDOR_ID_WINBOND, - PCI_DEVICE_ID_WINBOND_83C553, 0, &bus, &dev) - == PCIBIOS_SUCCESSFUL) { - dev++; + struct pci_dev *pdev; + if ((pdev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, NULL))) { + bus = pdev->bus->number; + dev = pdev->devfn + 1; chrp_pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32); if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) { #if 0 diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 6c5d2afa5..91ca5d595 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -60,21 +60,6 @@ extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ extern int rd_image_start; /* starting block # of image */ #endif - -int chrp_ide_irq = 0; - -void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) -{ - ide_ioreg_t port = base; - int i = 8; - - while (i--) - *p++ = port++; - *p++ = base + 0x206; - if (irq != NULL) - *irq = chrp_ide_irq; -} - static const char *gg2_memtypes[4] = { "FPM", "SDRAM", "EDO", "BEDO" }; @@ -89,6 +74,34 @@ static const char *gg2_cachemodes[4] = { "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" }; +#if 0 +#ifdef CONFIG_BLK_DEV_IDE +int chrp_ide_ports_known; +ide_ioreg_t chrp_ide_regbase[MAX_HWIFS]; +ide_ioreg_t chrp_idedma_regbase; /* one for both channels */ +unsigned int chrp_ide_irq; + +void chrp_ide_probe(void) +{ +} + +void chrp_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + int i; + + *p = 0; + if (base == 0) + return; + for (i = 0; i < 8; ++i) + *p++ = base + i * 0x10; + *p = base + 0x160; + if (irq != NULL) { + *irq = chrp_ide_irq; + } +} +#endif /* CONFIG_BLK_DEV_IDE */ +#endif + int chrp_get_cpuinfo(char *buffer) { @@ -139,12 +152,63 @@ chrp_get_cpuinfo(char *buffer) } /* L2 cache */ t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); - len += sprintf(buffer+len, "l2\t\t: %s %s (%s)\n", + len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n", gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3], gg2_cachemodes[t & 3]); return len; } + /* + * Fixes for the National Semiconductor PC78308VUL SuperI/O + * + * Some versions of Open Firmware incorrectly initialize the IRQ settings + * for keyboard and mouse + */ + +__initfunc(static inline void sio_write(u8 val, u8 index)) +{ + outb(index, 0x15c); + outb(val, 0x15d); +} + +__initfunc(static inline u8 sio_read(u8 index)) +{ + outb(index, 0x15c); + return inb(0x15d); +} + +__initfunc(static void sio_init(void)) +{ + u8 irq, type; + + /* select logical device 0 (KBC/Keyboard) */ + sio_write(0, 0x07); + irq = sio_read(0x70); + type = sio_read(0x71); + printk("sio: Keyboard irq %d, type %d: ", irq, type); + if (irq == 1 && type == 3) + printk("OK\n"); + else { + printk("remapping to irq 1, type 3\n"); + sio_write(1, 0x70); + sio_write(3, 0x71); + } + + /* select logical device 1 (KBC/Mouse) */ + sio_write(1, 0x07); + irq = sio_read(0x70); + type = sio_read(0x71); + printk("sio: Mouse irq %d, type %d: ", irq, type); + if (irq == 12 && type == 3) + printk("OK\n"); + else { + printk("remapping to irq 12, type 3\n"); + sio_write(12, 0x70); + sio_write(3, 0x71); + } +} + + __initfunc(void chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { @@ -191,7 +255,12 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) hydra_init(); /* Mac I/O */ w83c553f_init(); /* PCI-ISA bridge and IDE */ -#ifdef CONFIG_ABSTRACT_CONSOLE + /* + * Fix the Super I/O configuration + */ + sio_init(); + +#ifdef CONFIG_FB /* Frame buffer device based console */ conswitchp = &fb_con; #endif diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c index 73dcf9844..7109c0e5d 100644 --- a/arch/ppc/kernel/chrp_time.c +++ b/arch/ppc/kernel/chrp_time.c @@ -38,7 +38,7 @@ void chrp_time_init(void) rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); if (rtcs == NULL || rtcs->addrs == NULL) return; - base = ((int *)rtcs->addrs)[2]; + base = rtcs->addrs[0].address; nvram_as1 = 0; nvram_as0 = base; nvram_data = base + 1; @@ -69,7 +69,7 @@ int chrp_set_rtc_time(unsigned long nowtime) unsigned char save_control, save_freq_select; struct rtc_time tm; - to_tm(nowtime + 10*60*60, &tm); /* XXX for now */ + to_tm(nowtime, &tm); save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ @@ -146,7 +146,7 @@ unsigned long chrp_get_rtc_time(void) } if ((year += 1900) < 1970) year += 100; - return mktime(year, mon, day, hour, min, sec) - 10*60*60 /* XXX for now */; + return mktime(year, mon, day, hour, min, sec); } @@ -155,6 +155,9 @@ void chrp_calibrate_decr(void) struct device_node *cpu; int freq, *fp, divisor; + if (via_calibrate_decr()) + return; + /* * The cpu node should have a timebase-frequency property * to tell us the rate at which the decrementer counts. diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index 2924febc5..d8540e68d 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -9,6 +9,8 @@ * Low-level exception handlers and MMU support * rewritten by Paul Mackerras. * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * This file contains the low-level support and setup for the * PowerPC platform, including trap and interrupt dispatch. @@ -29,12 +31,14 @@ #include <linux/sys.h> #include <linux/errno.h> #include <linux/config.h> +#ifdef CONFIG_8xx +#include <asm/mmu.h> +#include <asm/pgtable.h> +#include <asm/cache.h> +#endif #ifdef CONFIG_APUS -/* At CYBERBASEp we'll find the following sum: - * -KERNELBASE+CyberStormMemoryBase - */ -#define CYBERBASEp (0xfff00000) +#include <asm/amigappc.h> #endif /* optimization for 603 to load the tlb directly from the linux table */ @@ -78,6 +82,8 @@ LG_CACHE_LINE_SIZE = 5 isync /* This instruction is not implemented on the PPC 603 or 601 */ +#ifndef CONFIG_8xx +/* This instruction is not implemented on the PPC 603 or 601 */ #define tlbia \ li r4,128; \ mtctr r4; \ @@ -85,6 +91,7 @@ LG_CACHE_LINE_SIZE = 5 0: tlbie r4; \ addi r4,r4,0x1000; \ bdnz 0b +#endif #define LOAD_BAT(n, offset, reg, RA, RB) \ lwz RA,offset+0(reg); \ @@ -96,6 +103,20 @@ LG_CACHE_LINE_SIZE = 5 mtspr DBAT##n##U,RA; \ mtspr DBAT##n##L,RB +#ifndef CONFIG_APUS +#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h +#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h +#else +#define tophys(rd,rs,rt) \ + lis rt,CYBERBASEp@h; \ + lwz rt,0(rt); \ + add rd,rs,rt +#define tovirt(rd,rs,rt) \ + lis rt,CYBERBASEp@h; \ + lwz rt,0(rt); \ + sub rd,rs,rt +#endif + .text .globl _stext _stext: @@ -130,12 +151,44 @@ _start: * * This just gets a minimal mmu environment setup so we can call * start_here() to do the real work. - * -- Cort + * -- Cort + * + * MPC8xx + * This port was done on an MBX board with an 860. Right now I only + * support an ELF compressed (zImage) boot from EPPC-Bug because the + * code there loads up some registers before calling us: + * r3: ptr to board info data + * r4: initrd_start or if no initrd then 0 + * r5: initrd_end - unused if r4 is 0 + * r6: Start of command line string + * r7: End of command line string + * + * I decided to use conditional compilation instead of checking PVR and + * adding more processor specific branches around code I don't need. + * Since this is an embedded processor, I also appreciate any memory + * savings I can get. + * + * The MPC8xx does not have any BATs, but it supports large page sizes. + * We first initialize the MMU to support 8M byte pages, then load one + * entry into each of the instruction and data TLBs to map the first + * 8M 1:1. I also mapped an additional I/O space 1:1 so we can get to + * the "internal" processor registers before MMU_init is called. + * + * The TLB code currently contains a major hack. Since I use the condition + * code register, I have to save and restore it. I am out of registers, so + * I just store it in memory location 0 (the TLB handlers are not reentrant). + * To avoid making any decisions, I need to use the "segment" valid bit + * in the first level table, but that would require many changes to the + * Linux page directory/table functions that I don't want to do right now. + * + * I used to use SPRG2 for a temporary register in the TLB handler, but it + * has since been put to other uses. I now use a hack to save a register + * and the CCR at memory location 0.....Someday I'll fix this..... + * -- Dan */ .globl __start __start: - /* * We have to do any OF calls before we map ourselves to KERNELBASE, * because OF may have I/O devices mapped in in that area @@ -145,12 +198,16 @@ __start: mr r30,r4 mr r29,r5 mr r28,r6 - mr r29,r7 + mr r27,r7 +#ifndef CONFIG_8xx +#ifndef CONFIG_APUS bl prom_init +#endif /* * Use the first pair of BAT registers to map the 1st 16MB - * of RAM to KERNELBASE. + * of RAM to KERNELBASE. From this point on we can't safely + * call OF any more. */ mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ @@ -173,17 +230,134 @@ __start: addis r8,r8,KERNELBASE@h addi r8,r8,2 #endif - mtspr DBAT0U,r11 +5: mtspr DBAT0U,r11 mtspr DBAT0L,r8 -5: mtspr IBAT0U,r11 + mtspr IBAT0U,r11 mtspr IBAT0L,r8 isync + +/* + * We need to run with _start at physical address 0. + * On CHRP, we are loaded at 0x10000 since OF on CHRP uses + * the exception vectors at 0 (and therefore this copy + * overwrites OF's exception vectors with our own). + * If the MMU is already turned on, we copy stuff to KERNELBASE, + * otherwise we copy it to 0. + */ + bl reloc_offset + mr r26,r3 + addis r4,r3,KERNELBASE@h /* current address of _start */ + cmpwi 0,r4,0 /* are we already running at 0? */ + beq 2f /* assume it's OK if so */ + li r3,0 + mfmsr r0 + andi. r0,r0,MSR_DR /* MMU enabled? */ + beq 7f + lis r3,KERNELBASE@h /* if so, are we */ + cmpw 0,r4,r3 /* already running at KERNELBASE? */ + beq 2f + rlwinm r4,r4,0,8,31 /* translate source address */ + add r4,r4,r3 /* to region mapped with BATs */ +7: addis r9,r26,klimit@ha /* fetch klimit */ + lwz r25,klimit@l(r9) + addis r25,r25,-KERNELBASE@h + li r6,0 /* Destination */ +#ifdef CONFIG_APUS + lis r9,0x6170 + ori r9,r9,0x7573 + cmpw 0,r9,r31 + bne 8f + lis r6,0xfff0 /* Copy to 0xfff00000 on APUS */ +8: +#endif + li r5,0x4000 /* # bytes of memory to copy */ + bl copy_and_flush /* copy the first 0x4000 bytes */ +#ifdef CONFIG_APUS + cmpw 0,r9,r31 /* That's all we need on APUS. */ + beq 2f +#endif + addi r0,r3,4f@l /* jump to the address of 4f */ + mtctr r0 /* in copy and do the rest. */ + bctr /* jump to the copy */ +4: mr r5,r25 + bl copy_and_flush /* copy the rest */ +2: /* * we now have the 1st 16M of ram mapped with the bats. * prep needs the mmu to be turned on here, but pmac already has it on. * this shouldn't bother the pmac since it just gets turned on again * as we jump to our code at KERNELBASE. -- Cort */ + +#else /* CONFIG_8xx */ + tlbia /* Invalidate all TLB entries */ + li r8, 0 + mtspr MI_CTR, r8 /* Set instruction control to zero */ + lis r8, MD_RESETVAL@h + mtspr MD_CTR, r8 /* Set data TLB control */ + + /* Now map the lower 8 Meg into the TLBs. For this quick hack, + * we can load the instruction and data TLB registers with the + * same values. + */ + lis r8, KERNELBASE@h /* Create vaddr for TLB */ + ori r8, r8, MI_EVALID /* Mark it valid */ + mtspr MI_EPN, r8 + mtspr MD_EPN, r8 + li r8, MI_PS8MEG /* Set 8M byte page */ + ori r8, r8, MI_SVALID /* Make it valid */ + mtspr MI_TWC, r8 + mtspr MD_TWC, r8 + li r8, MI_BOOTINIT /* Create RPN for address 0 */ + mtspr MI_RPN, r8 /* Store TLB entry */ + mtspr MD_RPN, r8 + lis r8, MI_Kp@h /* Set the protection mode */ + mtspr MI_AP, r8 + mtspr MD_AP, r8 +#ifdef CONFIG_MBX + /* Map another 8 MByte at 0xfa000000 to get the processor + * internal registers (among other things). + */ + lis r8, 0xfa000000@h /* Create vaddr for TLB */ + ori r8, r8, MD_EVALID /* Mark it valid */ + mtspr MD_EPN, r8 + li r8, MD_PS8MEG /* Set 8M byte page */ + ori r8, r8, MD_SVALID /* Make it valid */ + mtspr MD_TWC, r8 + lis r8, 0xfa000000@h /* Create paddr for TLB */ + ori r8, r8, MI_BOOTINIT + mtspr MD_RPN, r8 +#endif + + /* Since the cache is enabled according to the information we + * just loaded into the TLB, invalidate and enable the caches here. + * We should probably check/set other modes....later. + */ + lis r8, IDC_INVALL@h + mtspr IC_CST, r8 + mtspr DC_CST, r8 + lis r8, IDC_ENABLE@h + mtspr IC_CST, r8 +#ifdef notdef + mtspr DC_CST, r8 +#else + /* I still have a bug somewhere because the Ethernet driver + * does not want to work with copyback enabled. For now, + * at least enable write through. + */ +#if 0 + lis r8, DC_SFWT@h + mtspr DC_CST, r8 + lis r8, IDC_ENABLE@h + mtspr DC_CST, r8 +#endif +#endif + +/* We now have the lower 8 Meg mapped into TLB entries, and the caches + * ready to work. + */ +#endif /* CONFIG_8xx */ + mfmsr r0 ori r0,r0,MSR_DR|MSR_IR mtspr SRR1,r0 @@ -202,48 +376,6 @@ __start: #define STACK_UNDERHEAD 64 /* - * 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) - -#ifndef CONFIG_APUS -#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h -#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h -#else -#define tophys(rd,rs,rt) \ - lis rt,CYBERBASEp@h; \ - lwz rt,0(rt); \ - add rd,rs,rt -#define tovirt(rd,rs,rt) \ - lis rt,CYBERBASEp@h; \ - lwz rt,0(rt); \ - sub rd,rs,rt -#endif - -/* * 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 @@ -303,11 +435,15 @@ label: \ /* Machine check */ STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) -/* Data access exception */ +/* Data access exception. + * This is "never generated" by the MPC8xx. We jump to it for other + * translation errors. + */ . = 0x300 DataAccess: EXCEPTION_PROLOG mfspr r20,DSISR +#ifndef CONFIG_8xx andis. r0,r20,0xa470 /* weird error? */ bne 1f /* if not, try to put a PTE */ mfspr r3,DAR /* into the hash table */ @@ -315,6 +451,7 @@ DataAccess: rlwimi r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ mfspr r5,SPRG3 /* phys addr of TSS */ bl hash_page +#endif 1: stw r20,_DSISR(r21) mr r5,r20 mfspr r4,DAR @@ -326,10 +463,14 @@ DataAccess: .long do_page_fault .long int_return -/* Instruction access exception */ +/* Instruction access exception. + * This is "never generated" by the MPC8xx. We jump to it for other + * translation errors. + */ . = 0x400 InstructionAccess: EXCEPTION_PROLOG +#ifndef CONFIG_8xx andis. r0,r23,0x4000 /* no pte found? */ beq 1f /* if so, try to put a PTE */ mr r3,r22 /* into the hash table */ @@ -337,6 +478,7 @@ InstructionAccess: mr r20,r23 /* SRR1 has reason bits */ mfspr r5,SPRG3 /* phys addr of TSS */ bl hash_page +#endif 1: addi r3,r1,STACK_FRAME_OVERHEAD mr r4,r22 mr r5,r23 @@ -347,7 +489,38 @@ InstructionAccess: .long int_return /* External interrupt */ - STD_EXCEPTION(0x500, HardwareInterrupt, do_IRQ) + . = 0x500; +HardwareInterrupt: + EXCEPTION_PROLOG; +#ifdef CONFIG_APUS + mfmsr 20 + xori r20,r20,MSR_DR + sync + mtmsr r20 + sync + + lis r3,APUS_IPL_EMU@h + + li r20,(IPLEMU_SETRESET|IPLEMU_DISABLEINT) + stb r20,APUS_IPL_EMU@l(r3) + sync + + lbz r3,APUS_IPL_EMU@l(r3) + + mfmsr r20 + xori r20,r20,MSR_DR + sync + mtmsr r20 + sync + + stw r3,(_CCR+4)(r21); +#endif + addi r3,r1,STACK_FRAME_OVERHEAD; + li r20,MSR_KERNEL; + bl transfer_to_handler; + .long do_IRQ; + .long int_return + /* Alignment exception */ . = 0x600 @@ -375,6 +548,7 @@ ProgramCheck: .long ProgramCheckException .long int_return +#ifndef CONFIG_8xx /* Floating-point unavailable */ . = 0x800 FPUnavailable: @@ -384,6 +558,11 @@ FPUnavailable: bl transfer_to_handler /* if from kernel, take a trap */ .long KernelFP .long int_return +#else +/* No FPU on MPC8xx. This exception is not supposed to happen. +*/ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) +#endif STD_EXCEPTION(0x900, Decrementer, timer_interrupt) STD_EXCEPTION(0xa00, Trap_0a, UnknownException) @@ -406,6 +585,7 @@ SystemCall: STD_EXCEPTION(0xe00, Trap_0e, UnknownException) STD_EXCEPTION(0xf00, Trap_0f, UnknownException) +#ifndef CONFIG_8xx /* * Handle TLB miss for instruction on 603/603e. * Note: we get an alternate set of r0 - r3 to use automatically. @@ -502,7 +682,14 @@ InstructionAddressInvalid: sync /* Some chip revs have problems here... */ mtmsr r0 b InstructionAccess +#else +/* On the MPC8xx, this is a software emulation interrupt. It occurs + * for all unimplemented and illegal instructions. + */ + STD_EXCEPTION(0x1000, SoftEmu, SoftwareEmulation) +#endif +#ifndef CONFIG_8xx /* * Handle TLB miss for DATA Load operation on 603/603e */ @@ -598,12 +785,78 @@ DataAddressInvalid: sync /* Some chip revs have problems here... */ mtmsr r0 b DataAccess +#else +/* + * For the MPC8xx, this is a software tablewalk to load the instruction + * TLB. It is modelled after the example in the Motorola manual. The task + * switch loads the M_TWB register with the pointer to the first level table. + * If we discover there is no second level table (the value is zero), the + * plan was to load that into the TLB, which causes another fault into the + * TLB Error interrupt where we can handle such problems. However, that did + * not work, so if we discover there is no second level table, we restore + * registers and branch to the error exception. We have to use the MD_xxx + * registers for the tablewalk because the equivalent MI_xxx registers + * only perform the attribute functions. + */ +InstructionTLBMiss: + mtspr M_TW, r20 /* Save a couple of working registers */ + mfcr r20 + stw r20, 0(r0) + stw r21, 4(r0) + mfspr r20, SRR0 /* Get effective address of fault */ + mtspr MD_EPN, r20 /* Have to use MD_EPN for walk, MI_EPN can't */ + mfspr r20, M_TWB /* Get level 1 table entry address */ + lwz r21, 0(r20) /* Get the level 1 entry */ + rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ + beq 2f /* If zero, don't try to find a pte */ + + /* We have a pte table, so load the MI_TWC with the attributes + * for this page, which has only bit 31 set. + */ + tophys(r21,r21,0) + ori r21,r21,1 /* Set valid bit */ + mtspr MI_TWC, r21 /* Set page attributes */ + mtspr MD_TWC, r21 /* Load pte table base address */ + mfspr r21, MD_TWC /* ....and get the pte address */ + lwz r21, 0(r21) /* Get the pte */ + + /* Set four subpage valid bits (24, 25, 26, and 27). + * Since we currently run MI_CTR.PPCS = 0, the manual says, + * "If the page size is larger than 4k byte, then all the + * 4 bits should have the same value." + * I don't really know what to do if the page size is 4k Bytes, + * but I know setting them all to 0 does not work, and setting them + * all to 1 does, so that is the way it is right now. + * BTW, these four bits map to the software only bits in the + * linux page table. I used to turn them all of, but now just + * set them all for the hardware. + li r20, 0x00f0 + andc r20, r21, r20 + ori r20, r20, 0x0080 + */ + ori r20, r21, 0x00f0 + mtspr MI_RPN, r20 /* Update TLB entry */ + + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + rfi + +2: mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + b InstructionAccess +#endif /* CONFIG_8xx */ + /* * Handle TLB miss for DATA Store on 603/603e */ . = 0x1200 DataStoreTLBMiss: +#ifndef CONFIG_8xx #ifdef NO_RELOAD_HTAB /* * r0: stored ctr @@ -671,27 +924,164 @@ DataStoreTLBMiss: ori r3,r3,0x40 /* Set secondary hash */ b 00b /* Try lookup again */ #endif /* NO_RELOAD_HTAB */ - +#else /* CONFIG_8xx */ + mtspr M_TW, r20 /* Save a couple of working registers */ + mfcr r20 + stw r20, 0(r0) + stw r21, 4(r0) + mfspr r20, M_TWB /* Get level 1 table entry address */ + lwz r21, 0(r20) /* Get the level 1 entry */ + rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ + beq 2f /* If zero, don't try to find a pte */ + + /* We have a pte table, so load fetch the pte from the table. + */ + tophys(r21, r21, 0) + ori r21, r21, 1 /* Set valid bit in physical L2 page */ + mtspr MD_TWC, r21 /* Load pte table base address */ + mfspr r21, MD_TWC /* ....and get the pte address */ + lwz r21, 0(r21) /* Get the pte */ + + /* Set four subpage valid bits (24, 25, 26, and 27). + * Since we currently run MD_CTR.PPCS = 0, the manual says, + * "If the page size is larger than 4k byte, then all the + * 4 bits should have the same value." + * I don't really know what to do if the page size is 4k Bytes, + * but I know setting them all to 0 does not work, and setting them + * all to 1 does, so that is the way it is right now. + * BTW, these four bits map to the software only bits in the + * linux page table. I used to turn them all of, but now just + * set them all for the hardware. + li r20, 0x00f0 + andc r20, r21, r20 + ori r20, r20, 0x0080 + */ + ori r20, r21, 0x00f0 + mtspr MD_RPN, r20 /* Update TLB entry */ + + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + rfi + +2: mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + b DataAccess +#endif /* CONFIG_8xx */ + +#ifndef CONFIG_8xx /* Instruction address breakpoint exception (on 603/604) */ STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint) +#else + +/* This is an instruction TLB error on the MPC8xx. This could be due + * to many reasons, such as executing guarded memory or illegal instruction + * addresses. There is nothing to do but handle a big time error fault. + */ + . = 0x1300 +InstructionTLBError: + b InstructionAccess +#endif /* System management exception (603?) */ +#ifndef CONFIG_8xx STD_EXCEPTION(0x1400, Trap_14, UnknownException) +#else + +/* This is the data TLB error on the MPC8xx. This could be due to + * many reasons, including a dirty update to a pte. We can catch that + * one here, but anything else is an error. First, we track down the + * Linux pte. If it is valid, write access is allowed, but the + * page dirty bit is not set, we will set it and reload the TLB. For + * any other case, we bail out to a higher level function that can + * handle it. + */ + . = 0x1400 +DataTLBError: + mtspr M_TW, r20 /* Save a couple of working registers */ + mfcr r20 + stw r20, 0(r0) + stw r21, 4(r0) + + /* First, make sure this was a store operation. + */ + mfspr r20, DSISR + andis. r21, r20, 0x0200 /* If set, indicates store op */ + beq 2f + + mfspr r20, M_TWB /* Get level 1 table entry address */ + lwz r21, 0(r20) /* Get the level 1 entry */ + rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ + beq 2f /* If zero, bail */ + + /* We have a pte table, so fetch the pte from the table. + */ + tophys(r21, r21, 0) + ori r21, r21, 1 /* Set valid bit in physical L2 page */ + mtspr MD_TWC, r21 /* Load pte table base address */ + mfspr r21, MD_TWC /* ....and get the pte address */ + lwz r21, 0(r21) /* Get the pte */ + + andi. r20, r21, _PAGE_RW /* Is it writeable? */ + beq 2f /* Bail out if not */ + + ori r21, r21, _PAGE_DIRTY /* Update changed bit */ + mfspr r20, MD_TWC /* Get pte address again */ + stw r21, 0(r20) /* and update pte in table */ + + /* Set four subpage valid bits (24, 25, 26, and 27). + * Since we currently run MD_CTR.PPCS = 0, the manual says, + * "If the page size is larger than 4k byte, then all the + * 4 bits should have the same value." + * I don't really know what to do if the page size is 4k Bytes, + * but I know setting them all to 0 does not work, and setting them + * all to 1 does, so that is the way it is right now. + * BTW, these four bits map to the software only bits in the + * linux page table. I used to turn them all of, but now just + * set them all for the hardware. + li r20, 0x00f0 + andc r20, r21, r20 + ori r20, r20, 0x0080 + */ + ori r20, r21, 0x00f0 + + mtspr MD_RPN, r20 /* Update TLB entry */ + + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + rfi +2: + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + b DataAccess +#endif /* CONFIG_8xx */ STD_EXCEPTION(0x1500, Trap_15, UnknownException) STD_EXCEPTION(0x1600, Trap_16, UnknownException) - STD_EXCEPTION(0x1700, Trap_17, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, TAUException) STD_EXCEPTION(0x1800, Trap_18, UnknownException) STD_EXCEPTION(0x1900, Trap_19, UnknownException) STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) +/* On the MPC8xx, these next four traps are used for development + * support of breakpoints and such. Someday I will get around to + * using them. + */ 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 */ +#ifndef CONFIG_8xx + /* Run mode exception */ STD_EXCEPTION(0x2000, RunMode, RunModeException) STD_EXCEPTION(0x2100, Trap_21, UnknownException) @@ -711,6 +1101,9 @@ DataStoreTLBMiss: STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) . = 0x3000 +#else + . = 0x2000 +#endif /* * This code finishes saving the registers to the exception frame @@ -720,6 +1113,8 @@ DataStoreTLBMiss: .globl transfer_to_handler transfer_to_handler: stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 stw r23,_MSR(r21) SAVE_GPR(7, r21) SAVE_4GPRS(8, r21) @@ -768,6 +1163,7 @@ stack_ovf: SYNC rfi +#ifndef CONFIG_8xx /* * Continuation of the floating-point unavailable handler. */ @@ -790,9 +1186,18 @@ load_up_fpu: ori r5,r5,MSR_FP SYNC mtmsr r5 /* enable use of fpu now */ +#ifndef __SMP__ SYNC cmpi 0,r4,0 beq 1f +#else +/* + * All the saving of last_task_used_math is handled + * by a switch_to() call to smp_giveup_fpu() in SMP so + * last_task_used_math is not used. -- Cort + */ + b 1f +#endif add r4,r4,r6 addi r4,r4,TSS /* want TSS of last_task_used_math */ SAVE_32FPRS(0, r4) @@ -810,9 +1215,11 @@ load_up_fpu: lfd fr0,TSS_FPSCR-4(r5) mtfsf 0xff,fr0 REST_32FPRS(0, r5) +#ifndef __SMP__ subi r4,r5,TSS sub r4,r4,r6 stw r4,last_task_used_math@l(r3) +#endif /* __SMP__ */ /* restore registers and return */ lwz r3,_CCR(r21) lwz r4,_LINK(r21) @@ -859,16 +1266,6 @@ Hash_msk = (((1 << Hash_bits) - 1) * 64) .globl hash_page hash_page: -#ifdef __SMP__ - lis r6,hash_table_lock@h - ori r6,r6,hash_table_lock@l - tophys(r6,r6,r2) -1011: lwarx r0,0,r6 - stwcx. r6,0,r6 - bne- 1011b - cmpi 0,r0,0 - bne 1011b -#endif /* __SMP__ */ /* Get PTE (linux-style) and check access */ lwz r5,PG_TABLES(r5) tophys(r5,r5,r2) /* convert to phys addr */ @@ -1018,7 +1415,6 @@ found_slot: lwz r3,0(r2) addi r3,r3,1 stw r3,0(r2) - SYNC /* Return from the exception */ lwz r3,_CCR(r21) @@ -1027,19 +1423,14 @@ found_slot: mtcrf 0xff,r3 mtlr r4 mtctr r5 -#ifdef __SMP__ - lis r5,hash_table_lock@h - ori r5,r5,hash_table_lock@l - tophys(r5,r5,r6) - li r6,0 - stw r6,0(r5) -#endif /* __SMP__ */ REST_GPR(0, r21) REST_2GPRS(1, r21) REST_4GPRS(3, r21) /* we haven't used xer */ + SYNC mtspr SRR1,r23 mtspr SRR0,r22 + SYNC REST_GPR(20, r21) REST_2GPRS(22, r21) lwz r21,GPR21(r21) @@ -1047,16 +1438,37 @@ found_slot: rfi hash_page_out: -#ifdef __SMP__ - lis r5,hash_table_lock@h - ori r5,r5,hash_table_lock@l - tophys(r5,r5,r6) - li r6,0 - stw r6,0(r5) -#endif /* __SMP__ */ blr next_slot: .long 0 +#endif /* CONFIG_8xx */ + +#ifndef CONFIG_APUS +/* + * Copy routine used to copy the kernel to start at physical address 0 + * and flush and invalidate the caches as needed. + * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset + * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5. + */ +copy_and_flush: + addi r5,r5,-4 + addi r6,r6,-4 +4: li r0,8 + mtctr r0 +3: addi r6,r6,4 /* copy a cache line */ + lwzx r0,r6,r4 + stwx r0,r6,r3 + bdnz 3b + dcbst r6,r3 /* write it to memory */ + sync + icbi r6,r3 /* flush the icache line */ + cmplw 0,r6,r5 + blt 4b + isync + addi r5,r5,4 + addi r6,r6,4 + blr +#endif #ifdef CONFIG_APUS /* On APUS the first 0x4000 bytes of the kernel will be mapped @@ -1072,6 +1484,7 @@ next_slot: */ start_here: +#ifndef CONFIG_8xx /* * Enable caches and 604-specific features if necessary. */ @@ -1108,6 +1521,7 @@ start_here: ori r11,r11,HID0_BTCD 5: mtspr HID0,r11 /* superscalar exec & br history tbl */ 4: +#endif /* CONFIG_8xx */ /* ptr to current */ lis r2,init_task_union@h ori r2,r2,init_task_union@l @@ -1140,6 +1554,9 @@ start_here: mr r6,r28 mr r7,r27 bl identify_machine +#ifdef CONFIG_MBX + bl set_mbx_memory +#endif bl MMU_init /* @@ -1147,8 +1564,19 @@ start_here: * for SDR1 (hash table pointer) and the segment registers * and change to using our exception vectors. */ +#ifndef CONFIG_8xx lis r6,_SDR1@ha lwz r6,_SDR1@l(r6) +#else + /* The right way to do this would be to track it down through + * init's TSS like the context switch code does, but this is + * easier......until someone changes init's static structures. + */ + lis r6, swapper_pg_dir@h + tophys(r6,r6,0) + ori r6, r6, swapper_pg_dir@l + mtspr M_TWB, r6 +#endif lis r4,2f@h ori r4,r4,2f@l tophys(r4,r4,r3) @@ -1158,8 +1586,10 @@ start_here: rfi /* Load up the kernel context */ 2: + SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ +#ifndef CONFIG_8xx mtspr SDR1,r6 li r0,16 /* load up segment register values */ mtctr r0 /* for context 0 */ @@ -1179,7 +1609,8 @@ start_here: LOAD_BAT(1,16,r3,r4,r5) LOAD_BAT(2,32,r3,r4,r5) LOAD_BAT(3,48,r3,r4,r5) - +#endif /* CONFIG_8xx */ + /* Set up for using our exception vectors */ /* ptr to phys current tss */ tophys(r4,r2,r4) @@ -1188,36 +1619,6 @@ start_here: li r3,0 mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ -/* On CHRP copy exception vectors down to 0 */ - lis r5,_stext@ha - addi r5,r5,_stext@l - addis r5,r5,-KERNELBASE@h - cmpwi 0,r5,0 - beq 77f /* vectors are already at 0 */ - li r3,0x1000 - mtctr r3 - li r4,-4 - addi r5,r5,-4 -74: lwzu r0,4(r5) - stwu r0,4(r4) - bdnz 74b - /* need to flush/invalidate caches too */ - li r3,0x4000/CACHE_LINE_SIZE - li r4,0 - mtctr r3 -73: dcbst 0,r4 - addi r4,r4,CACHE_LINE_SIZE - bdnz 73b - sync - li r4,0 - mtctr r3 -72: icbi 0,r4 - addi r4,r4,CACHE_LINE_SIZE - bdnz 72b - sync - isync -77: - /* Now turn on the MMU for real! */ li r4,MSR_KERNEL lis r3,start_kernel@h @@ -1227,29 +1628,6 @@ start_here: rfi /* enable MMU and jump to start_kernel */ - .globl reset_SDR1 -reset_SDR1: - lis r6,_SDR1@ha - lwz r6,_SDR1@l(r6) - mfmsr r5 - li r4,0 - ori r4,r4,MSR_EE|MSR_IR|MSR_DR - andc r3,r5,r4 - lis r4,2f@h - ori r4,r4,2f@l - tophys(r4,r4,r5) - mtspr SRR0,r4 - mtspr SRR1,r3 - rfi -2: /* load new SDR1 */ - tlbia - mtspr SDR1,r6 - /* turn the mmu back on */ - mflr r3 - mtspr SRR0,r3 - mtspr SRR1,r5 - rfi - /* * FP unavailable trap from kernel - print a message, but let * the task use FP in the kernel until it returns to user mode. @@ -1272,10 +1650,19 @@ KernelFP: * and save its floating-point registers in its thread_struct. * Enables the FPU for use in the kernel on return. */ +/* smp_giveup_fpu() takes an arg to tell it where to save the fpu + * regs since last_task_used_math can't be trusted (many many race + * conditions). -- Cort + */ + .globl smp_giveup_fpu +smp_giveup_fpu: + mr r4,r3 + b 12f .globl giveup_fpu giveup_fpu: lis r3,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) +12: mfmsr r5 ori r5,r5,MSR_FP SYNC @@ -1284,8 +1671,10 @@ giveup_fpu: cmpi 0,r4,0 beqlr- /* if no previous owner, done */ addi r4,r4,TSS /* want TSS of last_task_used_math */ +#ifndef __SMP__ li r5,0 stw r5,last_task_used_math@l(r3) +#endif /* __SMP__ */ SAVE_32FPRS(0, r4) mffs fr0 stfd fr0,TSS_FPSCR-4(r4) @@ -1445,6 +1834,18 @@ syscall_ret_2: * * The code which creates the new task context is in 'copy_thread' * in arch/ppc/kernel/process.c + * + * The MPC8xx has something that currently happens "automagically." + * Unshared user space address translations are subject to ASID (context) + * match. During each task switch, the ASID is incremented. We can + * guarantee (I hope :-) that no entries currently match this ASID + * because every task will cause at least a TLB entry to be loaded for + * the first instruction and data access, plus the kernel running will + * have displaced several more TLBs. The MMU contains 32 entries for + * each TLB, and there are 16 contexts, so we just need to make sure + * two pages get replaced for every context switch, which currently + * happens. There are other TLB management techniques that I will + * eventually implement, but this is the easiest for now. -- Dan */ _GLOBAL(_switch) stwu r1,-INT_FRAME_SIZE-STACK_UNDERHEAD(r1) @@ -1476,6 +1877,7 @@ _GLOBAL(_switch) SYNC lwz r1,KSP(r4) /* Load new stack pointer */ addi r2,r4,-TSS /* Update current */ +#ifndef CONFIG_8xx /* Set up segment registers for new task */ rlwinm r5,r5,4,8,27 /* VSID = context << 4 */ addis r5,r5,0x6000 /* Set Ks, Ku bits */ @@ -1486,9 +1888,32 @@ _GLOBAL(_switch) addi r5,r5,1 /* next VSID */ addis r3,r3,0x1000 /* address of next segment */ bdnz 3b +#else +/* On the MPC8xx, we place the physical address of the new task + * page directory loaded into the MMU base register, and set the + * ASID compare register with the new "context". + */ + mtspr M_CASID, r5 /* Update context */ + lwz r5,MM-TSS(r4) /* Get virtual address of mm */ + lwz r5,PGD(r5) /* get new->mm->pgd */ + tophys(r5, r5, 0) /* convert to phys addr */ + mtspr M_TWB, r5 /* Update MMU base address */ +#endif SYNC /* FALL THROUGH into int_return */ +#ifdef __SMP__ + /* drop scheduler_lock since we weren't called by schedule() */ + lwz r5,TSS_SMP_FORK_RET(r4) + cmpi 0,r5,0 + beq+ int_return + li r3,0 + lis r5,scheduler_lock@ha + stw r3,TSS_SMP_FORK_RET(r4) + stw r3,scheduler_lock@l+4(r5) /* owner_pc */ + stw r3,scheduler_lock@l+8(r5) /* owner_cpu */ + stw r3,scheduler_lock@l(r5) /* lock */ +#endif /* __SMP__ */ /* * Trap exit. @@ -1566,6 +1991,18 @@ int_return: SYNC rfi +#if 0/*def __SMP__*/ + .globl ret_from_smpfork +ret_from_smpfork: + /* drop scheduler_lock since schedule() called us */ + lis r4,scheduler_lock@ha + li r5,0 + stw r5,scheduler_lock@l+4(r4) /* owner_pc */ + stw r5,scheduler_lock@l+8(r4) /* owner_cpu */ + stw r5,scheduler_lock@l(r4) /* lock */ + b int_return +#endif /* __SMP__ */ + /* * Fake an interrupt from kernel mode. * This is used when enable_irq loses an interrupt. @@ -1686,6 +2123,7 @@ _GLOBAL(flush_page_to_ram) * Flush entries from the hash table with VSIDs in the range * given. */ +#ifndef CONFIG_8xx _GLOBAL(flush_hash_segments) #ifdef NO_RELOAD_HTAB /* @@ -1700,15 +2138,6 @@ _GLOBAL(flush_hash_segments) rlwnm. r0,r9,r0,0,0 bne 99f #endif /* NO_RELOAD_HTAB */ -#ifdef __SMP__ - lis r6,hash_table_lock@h - ori r6,r6,hash_table_lock@l -1011: lwarx r0,0,r6 - stwcx. r6,0,r6 - bne- 1011b - cmpi 0,r0,0 - bne 1011b -#endif /* __SMP__ */ 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 */ @@ -1730,12 +2159,6 @@ _GLOBAL(flush_hash_segments) stw r0,0(r5) /* invalidate entry */ 2: bdnz 1b /* continue with loop */ sync -#ifdef __SMP__ - lis r5,hash_table_lock@h - ori r5,r5,hash_table_lock@l - li r6,0 - stw r6,0(r5) -#endif /* __SMP__ */ 99: tlbia isync blr @@ -1753,15 +2176,6 @@ _GLOBAL(flush_hash_page) rlwnm. r0,r9,r0,0,0 bne 99f #endif /* NO_RELOAD_HTAB */ -#ifdef __SMP__ - lis r6,hash_table_lock@h - ori r6,r6,hash_table_lock@l -1011: lwarx r0,0,r6 - stwcx. r6,0,r6 - bne- 1011b - cmpi 0,r0,0 - bne 1011b -#endif /* __SMP__ */ 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 */ @@ -1794,22 +2208,17 @@ _GLOBAL(flush_hash_page) 3: li r0,0 stw r0,0(r7) /* invalidate entry */ 4: sync -#ifdef __SMP__ - lis r5,hash_table_lock@h - ori r5,r5,hash_table_lock@l - li r6,0 - stw r6,0(r5) -#endif /* __SMP__ */ 99: tlbie r4 /* in hw tlb too */ isync blr - +#endif /* CONFIG_8xx */ /* * This routine is just here to keep GCC happy - sigh... */ _GLOBAL(__main) blr +#ifndef CONFIG_8xx /* * On CHRP, the Run-Time Abstraction Services (RTAS) have to be * called with the MMU off. @@ -1819,9 +2228,9 @@ enter_rtas: stwu r1,-16(r1) mflr r0 stw r0,20(r1) - addis r3,r3,-KERNELBASE@h lis r4,rtas_data@ha lwz r4,rtas_data@l(r4) + addis r4,r4,-KERNELBASE@h lis r6,1f@ha /* physical return address for rtas */ addi r6,r6,1f@l addis r6,r6,-KERNELBASE@h @@ -1829,14 +2238,15 @@ enter_rtas: addis r7,r7,-KERNELBASE@h lis r8,rtas_entry@ha lwz r8,rtas_entry@l(r8) + addis r5,r8,-KERNELBASE@h mfmsr r9 stw r9,8(r1) - li r0,0 ori r0,r0,MSR_EE|MSR_SE|MSR_BE andc r0,r9,r0 andi. r9,r9,MSR_ME|MSR_RI sync /* disable interrupts so SRR0/1 */ mtmsr r0 /* don't get trashed */ + li r6,0 mtlr r6 mtspr SPRG2,r7 mtspr SRR0,r8 @@ -1850,23 +2260,26 @@ enter_rtas: mtspr SRR0,r8 mtspr SRR1,r9 rfi /* return to caller */ +#endif /* CONFIG_8xx */ +#ifdef CONFIG_8xx +/* This is called during an exec when new page tables are created. + * It maps to the SET_PAGE_DIR macro. I guess I should make it an + * inline function. + */ +_GLOBAL(set_page_dir) + addis r3,r3,-KERNELBASE@h /* convert to phys addr */ + mtspr M_TWB, r3 /* Update MMU base address */ + blr +#endif - .globl amhere -amhere: .long 0 - + #ifdef __SMP__ /* * Secondary processor begins executing here. */ .globl secondary_entry secondary_entry: - lis r0,amhere@h - ori r0,r0,amhere@l - addis r0,r0,-KERNELBASE@h - stw r0,0(r0) - sync - isync /* just like __start() with a few changes -- Cort */ mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ @@ -1938,16 +2351,6 @@ secondary_entry: ori r11,r11,HID0_BTCD 5: mtspr HID0,r11 /* superscalar exec & br history tbl */ 4: - /* get ptr to current */ - lis r2,current_set@h - ori r2,r2,current_set@l - /* assume we're second processor for now */ - lwz r2,4(r2) - /* stack */ - addi r1,r2,TASK_UNION_SIZE - li r0,0 - stwu r0,-STACK_FRAME_OVERHEAD(r1) - /* * init_MMU on the first processor has setup the variables * for us - all we need to do is load them -- Cort @@ -1969,6 +2372,18 @@ secondary_entry: rfi /* Load up the kernel context */ 2: + /* get ptr to current */ + lis r2,current_set@h + ori r2,r2,current_set@l + /* assume we're second processor for now */ + tophys(r2,r2,r10) + lwz r2,4(r2) + /* stack */ + addi r1,r2,TASK_UNION_SIZE + li r0,0 + tophys(r3,r1,r10) + stwu r0,-STACK_FRAME_OVERHEAD(r3) + SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ mtspr SDR1,r6 @@ -2025,6 +2440,31 @@ secondary_entry: /* should never return */ .long 0 #endif /* __SMP__ */ + +#ifdef CONFIG_MBX +/* Jump into the system reset for the MBX rom. + * We first disable the MMU, and then jump to the ROM reset address. + * + * This does not work, don't bother trying. There is no place in + * the ROM we can jump to cause a reset. We will have to program + * a watchdog of some type that we don't service to cause a processor + * reset. + */ + .globl MBX_gorom +MBX_gorom: + 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 +2: + lis r4, 0xfe000000@h + addi r4, r4, 0xfe000000@l + mtlr r4 + blr +#endif /* * We put a few things here that have to be page-aligned. diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index f47d6f3d6..21c6966e0 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -1,5 +1,5 @@ /* - * $Id: idle.c,v 1.13 1998/01/06 06:44:55 cort Exp $ + * $Id: idle.c,v 1.35 1998/04/07 20:24:23 cort Exp $ * * Idle daemon for PowerPC. Idle daemon will handle any action * that needs to be taken when the system becomes idle. @@ -13,6 +13,7 @@ */ #define __KERNEL_SYSCALLS__ +#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -31,41 +32,44 @@ #include <asm/smp_lock.h> #include <asm/processor.h> #include <asm/mmu.h> +#include <asm/cache.h> +#ifdef CONFIG_PMAC +#include <asm/mediabay.h> +#endif -int zero_paged(void *unused); -void inline power_save(void); +void zero_paged(void); +void power_save(void); void inline htab_reclaim(void); +unsigned long htab_reclaim_on = 0; +unsigned long zero_paged_on = 0; + int idled(void *unused) { int ret = -EPERM; - /* - * 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); */ - -#ifdef __SMP__ -printk("SMP %d: in idle. current = %s/%d\n", - current->processor,current->comm,current->pid); -#endif /* __SMP__ */ for (;;) { + __sti(); + /* endless loop with no priority at all */ current->priority = -100; current->counter = -100; + + check_pgt_cache(); - /* endless idle loop with no priority at all */ - /* htab_reclaim(); */ - schedule(); + if ( !need_resched && zero_paged_on ) zero_paged(); + if ( !need_resched && htab_reclaim_on ) htab_reclaim(); + + /* + * Only processor 1 may sleep now since processor 2 would + * never wake up. Need to add timer code for processor 2 + * then it can sleep. -- Cort + */ #ifndef __SMP__ - /* can't do this on smp since second processor - will never wake up -- Cort */ - /* power_save(); */ -#endif /* __SMP__ */ + if ( !need_resched ) power_save(); +#endif /* __SMP__ */ + schedule(); } ret = 0; return ret; @@ -76,13 +80,16 @@ printk("SMP %d: in idle. current = %s/%d\n", * Mark 'zombie' pte's in the hash table as invalid. * This improves performance for the hash table reload code * a bit since we don't consider unused pages as valid. - * I haven't done any rigorous performance analysis yet - * so it's still experimental and turned off here. * -- Cort */ +PTE *reclaim_ptr = 0; void inline htab_reclaim(void) { +#ifndef CONFIG_8xx +#if 0 PTE *ptr, *start; + static int dir = 1; +#endif struct task_struct *p; unsigned long valid = 0; extern PTE *Hash, *Hash_end; @@ -91,28 +98,33 @@ void inline htab_reclaim(void) /* if we don't have a htab */ if ( Hash_size == 0 ) return; - /*lock_dcache();*/ - + lock_dcache(1); + +#if 0 /* find a random place in the htab to start each time */ - start = &Hash[jiffies%(Hash_size/sizeof(ptr))]; - for ( ptr = start; ptr < Hash_end ; ptr++) + start = &Hash[jiffies%(Hash_size/sizeof(PTE))]; + /* go a different direction each time */ + dir *= -1; + for ( ptr = start; + !need_resched && (ptr != Hash_end) && (ptr != Hash); + ptr += dir) { - if ( ptr == start ) - return; - if ( ptr == Hash_end ) - ptr = Hash; - valid = 0; - if (!ptr->v) +#else + if ( !reclaim_ptr ) reclaim_ptr = Hash; + while ( !need_resched ) + { + reclaim_ptr++; + if ( reclaim_ptr == Hash_end ) reclaim_ptr = Hash; +#endif + if (!reclaim_ptr->v) continue; + valid = 0; for_each_task(p) { if ( need_resched ) - { - /*unlock_dcache();*/ - return; - } + goto out; /* if this vsid/context is in use */ - if ( (ptr->vsid >> 4) == p->mm->context ) + if ( (reclaim_ptr->vsid >> 4) == p->mm->context ) { valid = 1; break; @@ -121,19 +133,28 @@ void inline htab_reclaim(void) if ( valid ) continue; /* this pte isn't used */ - ptr->v = 0; + reclaim_ptr->v = 0; } - /*unlock_dcache();*/ +out: + if ( need_resched ) printk("need_resched: %x\n", need_resched); + unlock_dcache(); +#endif /* CONFIG_8xx */ } - + /* * Syscall entry into the idle task. -- Cort */ asmlinkage int sys_idle(void) { + extern int media_bay_task(void *); if(current->pid != 0) return -EPERM; +#ifdef CONFIG_PMAC + if (media_bay_present) + kernel_thread(media_bay_task, NULL, 0); +#endif + idled(NULL); return 0; /* should never execute this but it makes gcc happy -- Cort */ } @@ -157,10 +178,8 @@ 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 */ unsigned long zeropage_calls = 0;/* # zero'd pages request that've been made */ -static struct wait_queue * page_zerod_wait = NULL; #define PAGE_THRESHOLD 96 /* how many pages to keep pre-zero'd */ /* @@ -189,7 +208,6 @@ unsigned long get_prezerod_page(void) */ atomic_inc((atomic_t *)&zeropage_hits); atomic_dec((atomic_t *)&zerocount); - wake_up(&page_zerod_wait); need_resched = 1; /* zero out the pointer to next in the page */ @@ -201,35 +219,18 @@ unsigned long get_prezerod_page(void) /* * 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. + * to speed up get_free_pages(). Zero's out pages until + * we've reached the limit of zero'd pages. We handle + * reschedule()'s in here so when we return we know we've + * zero'd all we need to for now. */ -int zero_paged(void *unused) +void zero_paged(void) { - extern pte_t *get_pte( struct mm_struct *mm, unsigned long address ); - pgd_t *dir; - pmd_t *pmd; + unsigned long pageptr = 0; /* current page being zero'd */ pte_t *pte; - - sprintf(current->comm, "zero_paged (idle)"); - /* current->blocked = ~0UL; */ -#ifdef __SMP__ - printk("Started zero_paged (cpu %d)\n", hard_smp_processor_id()); -#else - printk("Started zero_paged\n"); -#endif /* __SMP__ */ - - __sti(); - while ( 1 ) + while ( zerocount <= PAGE_THRESHOLD ) { - /* don't want to be pre-empted by swapper or power_save */ - current->priority = -98; - current->counter = -98; - /* we don't want to run until we have something to do */ - while ( zerocount >= PAGE_THRESHOLD ) - sleep_on(&page_zerod_wait); /* * Mark a page as reserved so we can mess with it * If we're interrupted we keep this page and our place in it @@ -237,7 +238,7 @@ int zero_paged(void *unused) */ pageptr = __get_free_pages(GFP_ATOMIC, 0); if ( !pageptr ) - goto retry; + return; if ( need_resched ) schedule(); @@ -245,20 +246,15 @@ int zero_paged(void *unused) /* * Make the page no cache so we don't blow our cache with 0's */ - dir = pgd_offset( init_task.mm, pageptr ); - if (dir) + pte = find_pte(init_task.mm, pageptr); + if ( !pte ) { - 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); - } - } + printk("pte NULL in zero_paged()\n"); + return; } + + pte_uncache(*pte); + flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); /* * Important here to not take time away from real processes. @@ -308,35 +304,34 @@ int zero_paged(void *unused) */ atomic_inc((atomic_t *)&zerocount); atomic_inc((atomic_t *)&zerototal); -retry: - schedule(); } } -void inline power_save(void) +int powersave_mode = HID0_DOZE; + +void power_save(void) { unsigned long msr, hid0; - /* no powersaving modes on the 601 */ - if( (_get_PVR()>>16) == 1 ) + /* only sleep on the 603-family/750 processors */ + switch (_get_PVR() >> 16) { + case 3: /* 603 */ + case 6: /* 603e */ + case 7: /* 603ev */ + case 8: /* 750 */ + break; + default: return; + } - __sti(); - 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)); + save_flags(msr); + cli(); + if (!need_resched) { + asm("mfspr %0,1008" : "=r" (hid0) :); + hid0 &= ~(HID0_NAP | HID0_SLEEP | HID0_DOZE); + hid0 |= powersave_mode | HID0_DPM; + asm("mtspr 1008,%0" : : "r" (hid0)); + msr |= MSR_POW; + } + restore_flags(msr); } diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index 4616d59a2..462805a65 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -14,6 +14,14 @@ * instead of just grabbing them. Thus setups with different IRQ numbers * shouldn't result in any weird surprises, and installing new handlers * should be easier. + * + * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the + * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit + * mask register (of which only 16 are defined), hence the weird shifting + * and compliment of the cached_irq_mask. I want to be able to stuff + * this right into the SIU SMASK register. + * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx + * to reduce code space and undefined function references. */ @@ -30,30 +38,41 @@ #include <linux/malloc.h> #include <linux/openpic.h> #include <linux/pci.h> -#include <linux/bios32.h> #include <linux/openpic.h> #include <asm/hydra.h> #include <asm/system.h> #include <asm/io.h> +#include <asm/pgtable.h> #include <asm/irq.h> #include <asm/bitops.h> #include <asm/gg2.h> +#include <asm/cache.h> +#ifdef CONFIG_8xx +#include <asm/8xx_immap.h> +#include <asm/mbx.h> +#endif #undef SHOW_IRQ unsigned lost_interrupts = 0; unsigned int local_irq_count[NR_CPUS]; -static struct irqaction irq_action[NR_IRQS]; +static struct irqaction *irq_action[NR_IRQS]; static int spurious_interrupts = 0; +#ifndef CONFIG_8xx +static unsigned int cached_irq_mask = 0xffffffff; +#else static unsigned int cached_irq_mask = 0xffffffff; +#endif static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } -spinlock_t irq_controller_lock; +/*spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;*/ #ifdef __SMP__ atomic_t __ppc_bh_counter = ATOMIC_INIT(0); #else int __ppc_bh_counter = 0; #endif +static volatile unsigned char *gg2_int_ack_special; +extern volatile unsigned long ipi_count; #define cached_21 (((char *)(&cached_irq_mask))[3]) #define cached_A1 (((char *)(&cached_irq_mask))[2]) @@ -61,9 +80,19 @@ int __ppc_bh_counter = 0; /* * These are set to the appropriate functions by init_IRQ() */ +#ifndef CONFIG_8xx void (*mask_and_ack_irq)(int irq_nr); void (*mask_irq)(unsigned int irq_nr); void (*unmask_irq)(unsigned int irq_nr); +#else /* CONFIG_8xx */ +/* init_IRQ() happens too late for the MBX because we initialize the + * CPM early and it calls request_irq() before we have these function + * pointers initialized. + */ +#define mask_and_ack_irq(irq) mbx_mask_irq(irq) +#define mask_irq(irq) mbx_mask_irq(irq) +#define unmask_irq(irq) mbx_unmask_irq(irq) +#endif /* CONFIG_8xx */ /* prep */ @@ -79,9 +108,46 @@ extern unsigned long route_pci_interrupts(void); #define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE)) + +/* nasty hack for shared irq's since we need to do kmalloc calls but + * can't very very early in the boot when we need to do a request irq. + * this needs to be removed. + * -- Cort + */ +static char cache_bitmask = 0; +static struct irqaction malloc_cache[4]; +extern int mem_init_done; + +void *irq_kmalloc(size_t size, int pri) +{ + unsigned int i; + if ( mem_init_done ) + return kmalloc(size,pri); + for ( i = 0; i <= 3 ; i++ ) + if ( ! ( cache_bitmask & (1<<i) ) ) + { + cache_bitmask |= (1<<i); + return (void *)(&malloc_cache[i]); + } + return 0; +} + +void irq_kfree(void *ptr) +{ + unsigned int i; + for ( i = 0 ; i <= 3 ; i++ ) + if ( ptr == &malloc_cache[i] ) + { + cache_bitmask &= ~(1<<i); + return; + } + kfree(ptr); +} + +#ifndef CONFIG_8xx void i8259_mask_and_ack_irq(int irq_nr) { - spin_lock(&irq_controller_lock); + /* spin_lock(&irq_controller_lock);*/ cached_irq_mask |= 1 << irq_nr; if (irq_nr > 7) { inb(0xA1); /* DUMMY */ @@ -96,20 +162,22 @@ void i8259_mask_and_ack_irq(int irq_nr) outb(0x60|irq_nr,0x20); /* specific eoi */ } - spin_unlock(&irq_controller_lock); + /* spin_unlock(&irq_controller_lock);*/ } void pmac_mask_and_ack_irq(int irq_nr) { unsigned long bit = 1UL << irq_nr; - spin_lock(&irq_controller_lock); + /* spin_lock(&irq_controller_lock);*/ cached_irq_mask |= bit; lost_interrupts &= ~bit; out_le32(IRQ_ACK, bit); out_le32(IRQ_ENABLE, ~cached_irq_mask); out_le32(IRQ_ACK, bit); - spin_unlock(&irq_controller_lock); + /* spin_unlock(&irq_controller_lock);*/ + /*if ( irq_controller_lock.lock ) + panic("irq controller lock still held in mask and ack\n");*/ } void chrp_mask_and_ack_irq(int irq_nr) @@ -188,44 +256,96 @@ static void chrp_unmask_irq(unsigned int irq_nr) else openpic_enable_irq(irq_to_openpic(irq_nr)); } +#else /* CONFIG_8xx */ +static void mbx_mask_irq(unsigned int irq_nr) +{ + cached_irq_mask &= ~(1 << (31-irq_nr)); + ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask = + cached_irq_mask; +} + +static void mbx_unmask_irq(unsigned int irq_nr) +{ + cached_irq_mask |= (1 << (31-irq_nr)); + ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask = + cached_irq_mask; +} +#endif /* CONFIG_8xx */ void disable_irq(unsigned int irq_nr) { - unsigned long flags; + /*unsigned long flags;*/ - spin_lock_irqsave(&irq_controller_lock, flags); + /* spin_lock_irqsave(&irq_controller_lock, flags);*/ mask_irq(irq_nr); - spin_unlock_irqrestore(&irq_controller_lock, flags); + /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ synchronize_irq(); } void enable_irq(unsigned int irq_nr) { - unsigned long flags; + /*unsigned long flags;*/ - spin_lock_irqsave(&irq_controller_lock, flags); + /* spin_lock_irqsave(&irq_controller_lock, flags);*/ unmask_irq(irq_nr); - spin_unlock_irqrestore(&irq_controller_lock, flags); + /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ } int get_irq_list(char *buf) { - int i, len = 0; + int i, len = 0, j; struct irqaction * action; + len += sprintf(buf+len, " "); + for (j=0; j<smp_num_cpus; j++) + len += sprintf(buf+len, "CPU%d ",j); + *(char *)(buf+len++) = '\n'; + for (i = 0 ; i < NR_IRQS ; i++) { - action = irq_action + i; + action = irq_action[i]; if (!action || !action->handler) continue; - len += sprintf(buf+len, "%2d: %10u %s", - i, kstat.interrupts[i], action->name); + len += sprintf(buf+len, "%3d: ", i); +#ifdef __SMP__ + for (j = 0; j < smp_num_cpus; j++) + len += sprintf(buf+len, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#else + len += sprintf(buf+len, "%10u ", kstat_irqs(i)); +#endif /* __SMP__ */ + switch( _machine ) + { + case _MACH_prep: + len += sprintf(buf+len, " 82c59 "); + break; + case _MACH_Pmac: + len += sprintf(buf+len, " PMAC-PIC "); + break; + case _MACH_chrp: + if ( is_8259_irq(i) ) + len += sprintf(buf+len, " 82c59 "); + else + len += sprintf(buf+len, " OpenPIC "); + break; + } + + len += sprintf(buf+len, " %s",action->name); for (action=action->next; action; action = action->next) { len += sprintf(buf+len, ", %s", action->name); } len += sprintf(buf+len, "\n"); } - len += sprintf(buf+len, "99: %10u spurious or short\n", - spurious_interrupts); +#ifdef __SMP__ + /* should this be per processor send/receive? */ + len += sprintf(buf+len, "IPI: %10lu\n", ipi_count); + for ( i = 0 ; i <= smp_num_cpus-1; i++ ) + len += sprintf(buf+len," "); + len += sprintf(buf+len, " interprocessor messages received\n"); +#endif + len += sprintf(buf+len, "BAD: %10u",spurious_interrupts); + for ( i = 0 ; i <= smp_num_cpus-1; i++ ) + len += sprintf(buf+len," "); + len += sprintf(buf+len, " spurious or short\n"); return len; } @@ -394,20 +514,31 @@ asmlinkage void do_IRQ(struct pt_regs *regs) int status; int openpic_eoi_done = 0; + /* save the HID0 in case dcache was off - see idle.c + * this hack should leave for a better solution -- Cort */ + unsigned dcache_locked; + + dcache_locked = unlock_dcache(); + hardirq_enter(cpu); +#ifndef CONFIG_8xx #ifdef __SMP__ if ( cpu != 0 ) - panic("cpu %d received interrupt", cpu); -#endif /* __SMP__ */ - - hardirq_enter(cpu); + { + if ( !lost_interrupts ) + { + extern smp_message_recv(void); + goto out; + + ipi_count++; + smp_message_recv(); + goto out; + } + /* could be here due to a do_fake_interrupt call but we don't + mess with the controller from the second cpu -- Cort */ + goto out; + } +#endif /* __SMP__ */ - /* - * I'll put this ugly mess of code into a function - * such as get_pending_irq() or some such clear thing - * so we don't have a switch in the irq code and - * the chrp code is merged a bit with the prep. - * -- Cort - */ switch ( _machine ) { case _MACH_Pmac: @@ -425,7 +556,7 @@ asmlinkage void do_IRQ(struct pt_regs *regs) * * This should go in the above mask/ack code soon. -- Cort */ - irq = *(volatile unsigned char *)GG2_INT_ACK_SPECIAL; + irq = *gg2_int_ack_special; /* * Acknowledge as soon as possible to allow i8259 * interrupt nesting @@ -456,7 +587,6 @@ asmlinkage void do_IRQ(struct pt_regs *regs) } break; case _MACH_prep: -#if 1 outb(0x0C, 0x20); irq = inb(0x20) & 7; if (irq == 2) @@ -470,23 +600,6 @@ retry_cascade: irq = (irq&7) + 8; } bits = 1UL << irq; -#else - /* - * get the isr from the intr controller since - * the bit in the irr has been cleared - */ - outb(0x0a, 0x20); - bits = inb(0x20)&0xff; - /* handle cascade */ - if ( bits & 4 ) - { - bits &= ~4UL; - outb(0x0a, 0xA0); - bits |= inb(0xA0)<<8; - } - /* ignore masked irqs */ - bits &= ~cached_irq_mask; -#endif break; } @@ -494,43 +607,62 @@ retry_cascade: printk("Bogus interrupt from PC = %lx\n", regs->nip); goto out; } + +#else /* CONFIG_8xx */ + /* For MPC8xx, read the SIVEC register and shift the bits down + * to get the irq number. + */ + bits = ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_sivec; + irq = bits >> 26; +#endif /* CONFIG_8xx */ mask_and_ack_irq(irq); status = 0; - action = irq_action + irq; - kstat.interrupts[irq]++; - if (action->handler) { + action = irq_action[irq]; + kstat.irqs[cpu][irq]++; + if ( action && action->handler) { if (!(action->flags & SA_INTERRUPT)) __sti(); - status |= action->flags; - action->handler(irq, action->dev_id, regs); - /*if (status & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq);*/ - __cli(); /* in case the handler turned them on */ - spin_lock(&irq_controller_lock); + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + /*if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq);*/ + action = action->next; + } while ( action ); + __cli(); + /* spin_lock(&irq_controller_lock);*/ unmask_irq(irq); - spin_unlock(&irq_controller_lock); + /* spin_unlock(&irq_controller_lock);*/ } else { +#ifndef CONFIG_8xx if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */ +#endif spurious_interrupts++; disable_irq( irq ); } /* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */ +#ifndef CONFIG_8xx if ( is_prep && (irq > 7) ) goto retry_cascade; /* do_bottom_half is called if necessary from int_return in head.S */ out: if (_machine == _MACH_chrp && !openpic_eoi_done) openpic_eoi(0); +#endif /* CONFIG_8xx */ hardirq_exit(cpu); + + /* restore the HID0 in case dcache was off - see idle.c + * this hack should leave for a better solution -- Cort */ + lock_dcache(dcache_locked); } int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) { - struct irqaction * action; + struct irqaction *old, **p, *action; unsigned long flags; #ifdef SHOW_IRQ @@ -540,49 +672,58 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) if (irq >= NR_IRQS) return -EINVAL; - action = irq + irq_action; - if (action->handler) - return -EBUSY; + if (!handler) - return -EINVAL; + { + /* Free */ + for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) + { + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + restore_flags(flags); + irq_kfree(action); + return 0; + } + return -ENOENT; + } + + action = (struct irqaction *) + irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; save_flags(flags); cli(); + action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; action->dev_id = dev_id; + action->next = NULL; enable_irq(irq); - restore_flags(flags); + p = irq_action + irq; + + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & action->flags & SA_SHIRQ)) + return -EBUSY; + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + } + *p = action; + + restore_flags(flags); return 0; } void free_irq(unsigned int irq, void *dev_id) { - struct irqaction * action = irq + irq_action; - unsigned long flags; - -#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; - } - if (!action->handler) { - printk("Trying to free free IRQ%d\n",irq); - return; - } - disable_irq(irq); - save_flags(flags); - cli(); - action->handler = NULL; - action->flags = 0; - action->mask = 0; - action->name = NULL; - action->dev_id = NULL; - restore_flags(flags); + request_irq(irq, NULL, 0, NULL, dev_id); } unsigned long probe_irq_on (void) @@ -595,6 +736,7 @@ int probe_irq_off (unsigned long irqs) return 0; } +#ifndef CONFIG_8xx __initfunc(static void i8259_init(void)) { /* init master interrupt controller */ @@ -616,11 +758,17 @@ __initfunc(static void i8259_init(void)) panic("Could not allocate cascade IRQ!"); enable_irq(2); /* Enable cascade interrupt */ } +#endif /* CONFIG_8xx */ +/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug. External + * interrupts can be either edge or level triggered, but there is no + * reason for us to change the EPPC-bug values (it would not work if we did). + */ __initfunc(void init_IRQ(void)) { extern void xmon_irq(int, void *, struct pt_regs *); +#ifndef CONFIG_8xx switch (_machine) { case _MACH_Pmac: @@ -637,7 +785,8 @@ __initfunc(void init_IRQ(void)) mask_and_ack_irq = chrp_mask_and_ack_irq; mask_irq = chrp_mask_irq; unmask_irq = chrp_unmask_irq; - ioremap(GG2_INT_ACK_SPECIAL, 1); + gg2_int_ack_special = (volatile unsigned char *) + ioremap(GG2_INT_ACK_SPECIAL, 1); openpic_init(); i8259_init(); #ifdef CONFIG_XMON @@ -653,7 +802,7 @@ __initfunc(void init_IRQ(void)) i8259_init(); route_pci_interrupts(); /* - * According to the Carolina spec from ibm irq's 0,1,2, and 8 + * According to the Carolina spec from ibm irqs 0,1,2, and 8 * must be edge triggered. Also, the pci intrs must be level * triggered and _only_ isa intrs can be level sensitive * which are 3-7,9-12,14-15. 13 is special - it can be level. @@ -686,5 +835,6 @@ __initfunc(void init_IRQ(void)) } break; - } + } +#endif /* CONFIG_8xx */ } diff --git a/arch/ppc/kernel/mbx_pci.c b/arch/ppc/kernel/mbx_pci.c new file mode 100644 index 000000000..30b7b1184 --- /dev/null +++ b/arch/ppc/kernel/mbx_pci.c @@ -0,0 +1,254 @@ +/* + * MBX pci routines. + * The MBX uses the QSpan PCI bridge. The config address register + * is located 0x500 from the base of the bridge control/status registers. + * The data register is located at 0x504. + * This is a two step operation. First, the address register is written, + * then the data register is read/written as required. + * I don't know what to do about interrupts (yet). + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/mbx.h> + + +/* + * This blows......The MBX uses the Tundra QSpan PCI bridge. When + * reading the configuration space, if something does not respond + * the bus times out and we get a machine check interrupt. So, the + * good ol' exception tables come to mind to trap it and return some + * value. + * + * On an error we just return a -1, since that is what the caller wants + * returned if nothing is present. I copied this from __get_user_asm, + * with the only difference of returning -1 instead of EFAULT. + * There is an associated hack in the machine check trap code. + * + * The QSPAN is also a big endian device, that is it makes the PCI + * look big endian to us. This presents a problem for the Linux PCI + * functions, which assume little endian. For example, we see the + * first 32-bit word like this: + * ------------------------ + * | Device ID | Vendor ID | + * ------------------------ + * If we read/write as a double word, that's OK. But in our world, + * when read as a word, device ID is at location 0, not location 2 as + * the little endian PCI would believe. We have to switch bits in + * the PCI addresses given to us to get the data to/from the correct + * byte lanes. + * + * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5. + * It always forces the MS bit to zero. Therefore, dev_fn values + * greater than 128 are returned as "no device found" errors. + * + * The QSPAN can only perform long word (32-bit) configuration cycles. + * The "offset" must have the two LS bits set to zero. Read operations + * require we read the entire word and then sort out what should be + * returned. Write operations other than long word require that we + * read the long word, update the proper word or byte, then write the + * entire long word back. + * + * PCI Bridge hack. We assume (correctly) that bus 0 is the primary + * PCI bus from the QSPAN. If we are called with a bus number other + * than zero, we create a Type 1 configuration access that a downstream + * PCI bridge will interpret. + */ + +#define __get_mbx_pci_config(x, addr, op) \ + __asm__ __volatile__( \ + "1: "op" %0,0(%1)\n" \ + " eieio\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r"(x) : "r"(addr)) + +#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500)) +#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504)) + +#define mk_config_addr(bus, dev, offset) \ + (((bus)<<16) | ((dev)<<8) | (offset & 0xfc)) + +#define mk_config_type1(bus, dev, offset) \ + mk_config_addr(bus, dev, offset) | 1; + +int mbx_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + uint temp; + u_char *cp; + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz"); + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *val = *cp; + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + uint temp; + ushort *sp; + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz"); + offset ^= 0x02; + + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *val = *sp; + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_mbx_pci_config(*val, QS_CONFIG_DATA, "lwz"); + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + uint temp; + u_char *cp; + + if ((bus > 7) || (dev_fn > 127)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *cp = val; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + uint temp; + ushort *sp; + + if ((bus > 7) || (dev_fn > 127)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x02; + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *sp = val; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + if ((bus > 7) || (dev_fn > 127)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *(unsigned int *)QS_CONFIG_DATA = val; + + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) +{ + int num, devfn; + unsigned int x, vendev; + + if (vendor == 0xffff) + return PCIBIOS_BAD_VENDOR_ID; + vendev = (dev_id << 16) + vendor; + num = 0; + for (devfn = 0; devfn < 32; devfn++) { + mbx_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x); + if (x == vendev) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devfn<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int mbx_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +{ + int devnr, x, num; + + num = 0; + for (devnr = 0; devnr < 32; devnr++) { + mbx_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x); + if ((x>>8) == class_code) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devnr<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c new file mode 100644 index 000000000..c028bb12d --- /dev/null +++ b/arch/ppc/kernel/mbx_setup.c @@ -0,0 +1,169 @@ +/* + * 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) + * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net) + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/blk.h> +#include <linux/ioport.h> + +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/ide.h> +#include <asm/mbx.h> + +extern unsigned long loops_per_sec; + +unsigned long empty_zero_page[1024]; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + +extern char saved_command_line[256]; + +extern unsigned long find_available_memory(void); +extern void mbx_cpm_reset(uint); + + +void mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + + *p = 0; + *irq = 0; + + if (base != 0) /* Only map the first ATA flash drive */ + return; +#ifdef ATA_FLASH + base = (unsigned long) ioremap(PCMCIA_MEM_ADDR, 0x200); + for (i = 0; i < 8; ++i) + *p++ = base++; + *p = ++base; /* Does not matter */ + if (irq) + *irq = 13; +#endif +} + +int +mbx_get_cpuinfo(char *buffer) +{ + int pvr = _get_PVR(); + int len; + char *model; + bd_t *bp; + extern RESIDUAL res; + + /* I know the MPC860 is 0x50. I don't have the book handy + * to check the others. + */ + if ((pvr>>16) == 0x50) + model = "MPC860"; + else + model = "unknown"; + +#ifdef __SMP__ +#define CD(X) (cpu_data[n].X) +#else +#define CD(X) (X) +#define CPUN 0 +#endif + bp = (bd_t *)&res; + + len = sprintf(buffer,"processor\t: %d\n" + "cpu\t\t: %s\n" + "revision\t: %d.%d\n" + "clock\t\t: %d MHz\n" + "bus clock\t: %d MHz\n", + CPUN, + model, + MAJOR(pvr), MINOR(pvr), + bp->bi_intfreq / 1000000, + bp->bi_busfreq / 1000000 + ); + + return len; +} + +__initfunc(void +mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) +{ + int cpm_page; + + cpm_page = *memory_start_p; + *memory_start_p += PAGE_SIZE; + + /* Reset the Communication Processor Module. + */ + mbx_cpm_reset(cpm_page); + +#ifdef notdef + ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ +#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 + /* initrd_start and size are setup by boot/head.S and kernel/head.S */ + if ( initrd_start ) + { + if (initrd_end > *memory_end_p) + { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end,*memory_end_p); + initrd_start = 0; + } + } +#endif + +#ifdef notdef + 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"); +#endif +} + +void +abort(void) +{ +#ifdef CONFIG_XMON + extern void xmon(void *); + xmon(0); +#endif + machine_restart(NULL); +} diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 2c866eed8..6607e00bd 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -12,6 +12,7 @@ * */ +#include <linux/config.h> #include <linux/sys.h> #include <asm/unistd.h> #include <asm/errno.h> @@ -19,6 +20,7 @@ #include "ppc_asm.tmpl" #include "ppc_defs.h" +#ifndef CONFIG_8xx /* This instruction is not implemented on the PPC 601 or 603 */ #define tlbia \ li r4,128; \ @@ -27,7 +29,7 @@ 0: tlbie r4; \ addi r4,r4,0x1000; \ bdnz 0b - +#endif .text /* @@ -323,6 +325,18 @@ _GLOBAL(_get_SP) mr r3,r1 /* Close enough */ blr +_GLOBAL(_get_THRM1) + mfspr r3,THRM1 + blr + +_GLOBAL(_set_THRM1) + mtspr THRM1,r3 + blr + +_GLOBAL(_get_L2CR) + mfspr r3,L2CR + blr + _GLOBAL(_get_PVR) mfspr r3,PVR blr @@ -348,33 +362,6 @@ cvt_df: stfs 0,0(r4) blr - -_GLOBAL(lock_dcache) - mfspr r3,PVR /* nop on 601 */ - rlwinm r3,r3,16,16,31 - cmpwi 0,r3,1 - beqlr- - mfspr r3,HID0 - ori r3,r3,HID0_DLOCK - mtspr HID0,r3 - sync - isync - blr - -_GLOBAL(unlock_dcache) - mfspr r3,PVR /* nop on 601 */ - rlwinm r3,r3,16,16,31 - cmpwi 0,r3,1 - beqlr- - mfspr r3,HID0 - li r4,HID0_DLOCK - andc r3,r3,r4 - mtspr HID0,r3 - sync - isync - blr - - /* * Create a kernel thread * __kernel_thread(flags, fn, arg) @@ -386,6 +373,16 @@ _GLOBAL(__kernel_thread) bnelr /* return if parent */ mtlr r4 /* fn addr in lr */ mr r3,r5 /* load arg and call fn */ +#if 0/*def __SMP__*/ + /* drop scheduler_lock since schedule() called us */ + lis r4,scheduler_lock@ha + li r5,0 + stw r5,scheduler_lock@l+4(r4) /* owner_pc */ + stw r5,scheduler_lock@l+8(r4) /* owner_cpu */ + stw r5,scheduler_lock@l(r4) + sync + isync +#endif /* __SMP__ */ blrl li r0,__NR_exit /* exit after child exits */ li r3,0 @@ -413,7 +410,9 @@ SYSCALL(execve) SYSCALL(open) SYSCALL(close) SYSCALL(waitpid) +SYSCALL(fork) SYSCALL(delete_module) +SYSCALL(_exit) /* Why isn't this a) automatic, b) written in 'C'? */ @@ -593,5 +592,7 @@ sys_call_table: .long sys_setresgid .long sys_getresgid /* 170 */ .long sys_prctl - .space (NR_syscalls-171)*4 + .long sys_xstat + .long sys_xmknod + .space (NR_syscalls-173)*4 diff --git a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c index 8db1763db..9de056504 100644 --- a/arch/ppc/kernel/mk_defs.c +++ b/arch/ppc/kernel/mk_defs.c @@ -19,6 +19,7 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> +#include <asm/io.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/processor.h> @@ -29,9 +30,11 @@ void main(void) { + 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(PROCESSOR, offsetof(struct task_struct, processor)); DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending)); DEFINE(TSS, offsetof(struct task_struct, tss)); DEFINE(MM, offsetof(struct task_struct, mm)); @@ -45,6 +48,7 @@ main(void) DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0])); DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr)); + DEFINE(TSS_SMP_FORK_RET, offsetof(struct thread_struct, smp_fork_ret)); /* Interrupt register frame */ DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 7e39487d8..186165a46 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1,24 +1,27 @@ /* - * $Id: pci.c,v 1.18 1997/10/29 03:35:07 cort Exp $ + * $Id: pci.c,v 1.24 1998/02/19 21:29:49 cort Exp $ * Common pmac/prep/chrp pci routines. -- Cort */ #include <linux/kernel.h> #include <linux/pci.h> -#include <linux/bios32.h> #include <linux/delay.h> #include <linux/string.h> #include <linux/init.h> #include <linux/config.h> #include <linux/pci.h> +#include <linux/openpic.h> #include <asm/processor.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/pci-bridge.h> +#include <asm/irq.h> -#if !defined(CONFIG_MACH_SPECIFIC) +#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC) unsigned long isa_io_base; +#endif /* CONFIG_MACH_SPECIFIC || CONFIG_PMAC */ +#if !defined(CONFIG_MACH_SPECIFIC) unsigned long isa_mem_base; unsigned long pci_dram_offset; #endif /* CONFIG_MACH_SPECIFIC */ @@ -121,49 +124,6 @@ int pcibios_present(void) return 1; } -int pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - -/* - * Given the class, find the n'th instance of that device - * in the system. - */ -int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - - __initfunc(unsigned long pcibios_init(unsigned long mem_start,unsigned long mem_end)) { @@ -203,6 +163,70 @@ __initfunc(void __initfunc(unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) + { + extern route_pci_interrupts(void); + struct pci_dev *dev; + extern struct bridge_data **bridges; + extern unsigned char *Motherboard_map; + extern unsigned char *Motherboard_routes; + + /* + * FIXME: This is broken: We should not assign IRQ's to IRQless + * devices (look at PCI_INTERRUPT_PIN) and we also should + * honor the existence of multi-function devices where + * different functions have different interrupt pins. [mj] + */ + switch (_machine ) + { + case _MACH_prep: + route_pci_interrupts(); + for(dev=pci_devices; dev; dev=dev->next) + { + unsigned char d = PCI_SLOT(dev->devfn); + dev->irq = Motherboard_routes[Motherboard_map[d]]; + } + break; + case _MACH_chrp: + /* PCI interrupts are controlled by the OpenPIC */ + for(dev=pci_devices; dev; dev=dev->next) + if (dev->irq) + dev->irq = openpic_to_irq(dev->irq); + break; + case _MACH_Pmac: + for(dev=pci_devices; dev; dev=dev->next) + { + /* + * Open Firmware often doesn't initialize the, + * PCI_INTERRUPT_LINE config register properly, so we + * should find the device node and se if it has an + * AAPL,interrupts property. + */ + struct bridge_data *bp = bridges[dev->bus->number]; + struct device_node *node; + unsigned int *reg; + unsigned char pin; + + if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || + !pin) + continue; /* No interrupt generated -> no fixup */ + for (node = bp->node->child; node != 0; + node = node->sibling) { + reg = (unsigned int *) get_property(node, "reg", 0); + if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn) + continue; + /* this is the node, see if it has interrupts */ + if (node->n_intrs > 0) + dev->irq = node->intrs[0].line; + break; + } + } + break; + } return mem_start; } + +__initfunc(char *pcibios_setup(char *str)) +{ + return str; +} diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c index d0144a567..a71aede12 100644 --- a/arch/ppc/kernel/pmac_pci.c +++ b/arch/ppc/kernel/pmac_pci.c @@ -12,27 +12,18 @@ * 2 of the License, or (at your option) any later version. */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/pci.h> -#include <linux/bios32.h> #include <linux/delay.h> #include <linux/string.h> #include <linux/init.h> #include <asm/io.h> +#include <asm/pgtable.h> #include <asm/prom.h> #include <asm/pci-bridge.h> -struct bridge_data { - volatile unsigned int *cfg_addr; - volatile unsigned char *cfg_data; - void *io_base; - int bus_number; - int max_bus; - struct bridge_data *next; - struct device_node *node; -}; - -static struct bridge_data **bridges, *bridge_list; +struct bridge_data **bridges, *bridge_list; static int max_bus; static void add_bridges(struct device_node *dev, unsigned long *mem_ptr); @@ -112,9 +103,11 @@ static void add_bridges(struct device_node *dev, unsigned long *mem_ptr) int *bus_range; int len; struct bridge_data *bp; + struct reg_property *addr; for (; dev != NULL; dev = dev->next) { - if (dev->n_addrs < 1) { + addr = (struct reg_property *) get_property(dev, "reg", &len); + if (addr == NULL || len < sizeof(*addr)) { printk(KERN_WARNING "Can't use %s: no address\n", dev->full_name); continue; @@ -130,15 +123,18 @@ static void add_bridges(struct device_node *dev, unsigned long *mem_ptr) else printk(KERN_INFO "PCI buses %d..%d", bus_range[0], bus_range[1]); - printk(" controlled by %s at %x\n", - dev->name, dev->addrs[0].address); + printk(" controlled by %s at %x\n", dev->name, addr->address); bp = (struct bridge_data *) *mem_ptr; *mem_ptr += sizeof(struct bridge_data); bp->cfg_addr = (volatile unsigned int *) - ioremap(dev->addrs[0].address + 0x800000, 0x1000); + ioremap(addr->address + 0x800000, 0x1000); bp->cfg_data = (volatile unsigned char *) - ioremap(dev->addrs[0].address + 0xc00000, 0x1000); - bp->io_base = (void *) ioremap(dev->addrs[0].address, 0x10000); + ioremap(addr->address + 0xc00000, 0x1000); + bp->io_base = (void *) ioremap(addr->address, 0x10000); +#ifdef CONFIG_PMAC + if (isa_io_base == 0) + isa_io_base = (unsigned long) bp->io_base; +#endif bp->bus_number = bus_range[0]; bp->max_bus = bus_range[1]; bp->next = bridge_list; @@ -180,7 +176,7 @@ int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, } int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val) + unsigned char offset, unsigned char *val) { struct bridge_data *bp; @@ -198,32 +194,11 @@ int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, } udelay(2); *val = in_8(bp->cfg_data + (offset & 3)); - - if (offset == PCI_INTERRUPT_LINE) { - /* - * Open Firmware often doesn't initialize this - * register properly, so we find the node and see - * if it has an AAPL,interrupts property. - */ - struct device_node *node; - unsigned int *reg; - - for (node = bp->node->child; node != 0; node = node->sibling) { - reg = (unsigned int *) get_property(node, "reg", 0); - if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn) - continue; - /* this is the node, see if it has interrupts */ - if (node->n_intrs > 0) - *val = node->intrs[0]; - break; - } - } - return PCIBIOS_SUCCESSFUL; } int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val) + unsigned char offset, unsigned short *val) { struct bridge_data *bp; @@ -245,7 +220,7 @@ int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, } int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val) + unsigned char offset, unsigned int *val) { struct bridge_data *bp; @@ -267,7 +242,7 @@ int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, } int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val) + unsigned char offset, unsigned char val) { struct bridge_data *bp; @@ -288,7 +263,7 @@ int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, } int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val) + unsigned char offset, unsigned short val) { struct bridge_data *bp; @@ -309,7 +284,7 @@ int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, } int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val) + unsigned char offset, unsigned int val) { struct bridge_data *bp; diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index 4e37becd5..afcd4fd8b 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -47,23 +47,18 @@ #include <asm/ide.h> #include <asm/pci-bridge.h> #include <asm/adb.h> +#include <asm/mediabay.h> +#include <asm/ohare.h> +#include <asm/mediabay.h> #include "time.h" -/* - * A magic address and value to put into it on machines with the - * "ohare" I/O controller. This makes the IDE CD work on Starmaxes. - * Contributed by Harry Eaton. - */ -#define OMAGICPLACE ((volatile unsigned *) 0xf3000038) -#define OMAGICCONT 0xbeff7a - extern int root_mountflags; unsigned char drive_info; #define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ -static void gc_init(const char *, int); +static void ohare_init(void); void pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p) @@ -91,27 +86,44 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p) loops_per_sec = 50000000; } + /* this area has the CPU identification register + and some registers used by smp boards */ + ioremap(0xf8000000, 0x1000); + *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); - gc_init("gc", 0); - gc_init("ohare", 1); -#ifdef CONFIG_ABSTRACT_CONSOLE + ohare_init(); + +#ifdef CONFIG_FB /* Frame buffer device based console */ conswitchp = &fb_con; #endif } -static void gc_init(const char *name, int isohare) +static volatile u32 *feature_addr; + +static void ohare_init(void) { struct device_node *np; - for (np = find_devices(name); np != NULL; np = np->next) { - if (np->n_addrs > 0) - ioremap(np->addrs[0].address, np->addrs[0].size); - if (isohare) { - printk(KERN_INFO "Twiddling the magic ohare bits\n"); - out_le32(OMAGICPLACE, OMAGICCONT); - } + np = find_devices("ohare"); + if (np == 0) + return; + if (np->next != 0) + printk(KERN_WARNING "only using the first ohare\n"); + if (np->n_addrs == 0) { + printk(KERN_ERR "No addresses for %s\n", np->full_name); + return; + } + feature_addr = (volatile u32 *) + ioremap(np->addrs[0].address + OHARE_FEATURE_REG, 4); + + if (find_devices("via-pmu") == 0) { + printk(KERN_INFO "Twiddling the magic ohare bits\n"); + out_le32(feature_addr, STARMAX_FEATURES); + } else { + out_le32(feature_addr, in_le32(feature_addr) | PBOOK_FEATURES); + printk(KERN_DEBUG "feature reg = %x\n", in_le32(feature_addr)); } } @@ -125,10 +137,15 @@ kdev_t boot_dev; unsigned long powermac_init(unsigned long mem_start, unsigned long mem_end) { - pmac_nvram_init(); +#ifdef CONFIG_KGDB + extern void zs_kgdb_hook(int tty_num); + zs_kgdb_hook(0); +#endif adb_init(); + pmac_nvram_init(); if (_machine == _MACH_Pmac) { pmac_read_rtc_time(); + media_bay_init(); } #ifdef CONFIG_PMAC_CONSOLE pmac_find_display(); @@ -175,7 +192,7 @@ note_scsi_host(struct device_node *node, void *host) #include "../../../drivers/scsi/sd.h" #include "../../../drivers/scsi/hosts.h" -int sd_find_target(void *host, int tgt) +kdev_t sd_find_target(void *host, int tgt) { Scsi_Disk *dp; int i; @@ -190,7 +207,7 @@ int sd_find_target(void *host, int tgt) void find_boot_device(void) { - int dev; + kdev_t dev; if (kdev_t_to_nr(ROOT_DEV) != 0) return; @@ -201,7 +218,7 @@ void find_boot_device(void) dev = sd_find_target(boot_host, boot_target); if (dev == 0) return; - boot_dev = to_kdev_t(dev + boot_part); + boot_dev = MKDEV(MAJOR(dev), MINOR(dev) + boot_part); #endif /* XXX should cope with booting from IDE also */ } @@ -221,39 +238,92 @@ void note_bootable_part(kdev_t dev, int part) } } +#ifdef CONFIG_BLK_DEV_IDE +int pmac_ide_ports_known; +ide_ioreg_t pmac_ide_regbase[MAX_HWIFS]; +int pmac_ide_irq[MAX_HWIFS]; + void pmac_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) + if (base == 0) return; - if (np->n_addrs == 0) { - printk("ide: no addresses for device %s\n", np->full_name); + if (base == mb_cd_base && !check_media_bay(MB_CD)) { + mb_cd_index = -1; return; } - if (np->n_intrs == 0) { - printk("ide: no intrs for device %s, using 13\n", - np->full_name); - *irq = 13; - } else { - *irq = np->intrs[0]; - } - base = (unsigned long) ioremap(np->addrs[0].address, 0x200); for (i = 0; i < 8; ++i) *p++ = base + i * 0x10; *p = base + 0x160; + if (irq != NULL) { + *irq = 0; + for (i = 0; i < MAX_HWIFS; ++i) { + if (base == pmac_ide_regbase[i]) { + *irq = pmac_ide_irq[i]; + break; + } + } + } +} + +void pmac_ide_probe(void) +{ + struct device_node *np; + int i; + struct device_node *atas; + struct device_node *p, **pp, *removables, **rp; + + pp = &atas; + rp = &removables; + p = find_devices("ATA"); + if (p == NULL) + p = find_devices("IDE"); + /* Move removable devices such as the media-bay CDROM + on the PB3400 to the end of the list. */ + for (; p != NULL; p = p->next) { + if (p->parent && p->parent->name + && strcasecmp(p->parent->name, "media-bay") == 0) { + *rp = p; + rp = &p->next; + } else { + *pp = p; + pp = &p->next; + } + } + *rp = NULL; + *pp = removables; + + for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) { + if (np->n_addrs == 0) { + printk(KERN_WARNING "ide: no address for device %s\n", + np->full_name); + continue; + } + pmac_ide_regbase[i] = (unsigned long) + ioremap(np->addrs[0].address, 0x200); + if (np->n_intrs == 0) { + printk("ide: no intrs for device %s, using 13\n", + np->full_name); + pmac_ide_irq[i] = 13; + } else { + pmac_ide_irq[i] = np->intrs[0].line; + } + + if (np->parent && np->parent->name + && strcasecmp(np->parent->name, "media-bay") == 0) { + mb_cd_index = i; + mb_cd_base = pmac_ide_regbase[i]; + mb_cd_irq = pmac_ide_irq[i]; + } + + ++i; + } + + pmac_ide_ports_known = 1; } +#endif /* CONFIG_BLK_DEV_IDE */ int pmac_get_cpuinfo(char *buffer) diff --git a/arch/ppc/kernel/pmac_support.c b/arch/ppc/kernel/pmac_support.c index 17226f8ff..4ceaa0dc9 100644 --- a/arch/ppc/kernel/pmac_support.c +++ b/arch/ppc/kernel/pmac_support.c @@ -7,8 +7,11 @@ #include <linux/nvram.h> #include <asm/ptrace.h> #include <asm/io.h> +#include <asm/pgtable.h> #include <asm/system.h> #include <asm/prom.h> +#include <asm/adb.h> +#include <asm/pmu.h> /* * Read and write the non-volatile RAM on PowerMacs and CHRP machines. @@ -32,8 +35,7 @@ void pmac_nvram_init(void) } nvram_naddrs = dp->n_addrs; if (_machine == _MACH_chrp && nvram_naddrs == 1) { - /* XXX for now */ - nvram_data = ioremap(0xf70e0000, NVRAM_SIZE); + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_mult = 1; } else if (nvram_naddrs == 1) { nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); @@ -41,6 +43,8 @@ void pmac_nvram_init(void) } 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 if (nvram_naddrs == 0 && adb_hardware == ADB_VIAPMU) { + nvram_naddrs = -1; } else { printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", nvram_naddrs); @@ -49,7 +53,16 @@ void pmac_nvram_init(void) unsigned char nvram_read_byte(int addr) { + struct adb_request req; + switch (nvram_naddrs) { + case -1: + if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM, + (addr >> 8) & 0xff, addr & 0xff)) + break; + while (!req.complete) + pmu_poll(); + return req.reply[1]; case 1: return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; case 2: @@ -62,7 +75,16 @@ unsigned char nvram_read_byte(int addr) void nvram_write_byte(unsigned char val, int addr) { + struct adb_request req; + switch (nvram_naddrs) { + case -1: + if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM, + (addr >> 8) & 0xff, addr & 0xff, val)) + break; + while (!req.complete) + pmu_poll(); + break; case 1: nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; break; diff --git a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c index 1a31ffa9a..3c976506e 100644 --- a/arch/ppc/kernel/pmac_time.c +++ b/arch/ppc/kernel/pmac_time.c @@ -15,8 +15,11 @@ #include <linux/mm.h> #include <asm/adb.h> #include <asm/cuda.h> +#include <asm/pmu.h> #include <asm/prom.h> #include <asm/system.h> +#include <asm/io.h> +#include <asm/pgtable.h> #include "time.h" @@ -44,7 +47,11 @@ /* Bits in IFR and IER */ #define T1_INT 0x40 /* Timer 1 interrupt */ -static int via_calibrate_decr(void) +/* + * Calibrate the decrementer register using VIA timer 1. + * This is used both on powermacs and CHRP machines. + */ +int via_calibrate_decr(void) { struct device_node *vias; volatile unsigned char *via; @@ -54,9 +61,12 @@ static int via_calibrate_decr(void) vias = find_devices("via-cuda"); if (vias == 0) vias = find_devices("via-pmu"); + if (vias == 0) + vias = find_devices("via"); if (vias == 0 || vias->n_addrs == 0) return 0; - via = (volatile unsigned char *) vias->addrs[0].address; + via = (volatile unsigned char *) + ioremap(vias->addrs[0].address, vias->addrs[0].size); /* set timer 1 for continuous interrupts */ out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT); @@ -123,14 +133,30 @@ pmac_get_rtc_time(void) struct adb_request req; /* Get the time from the RTC */ - cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME); - while (!req.complete) - cuda_poll(); - if (req.reply_len != 7) - printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", - req.reply_len); - return (req.reply[3] << 24) + (req.reply[4] << 16) - + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET; + switch (adb_hardware) { + case ADB_VIACUDA: + if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0) + return 0; + while (!req.complete) + cuda_poll(); + if (req.reply_len != 7) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + return (req.reply[3] << 24) + (req.reply[4] << 16) + + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET; + case ADB_VIAPMU: + if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) + return 0; + while (!req.complete) + pmu_poll(); + if (req.reply_len != 5) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + return (req.reply[1] << 24) + (req.reply[2] << 16) + + (req.reply[3] << 8) + req.reply[4] - RTC_OFFSET; + default: + return 0; + } } int pmac_set_rtc_time(unsigned long nowtime) diff --git a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c new file mode 100644 index 000000000..b7eab0fa1 --- /dev/null +++ b/arch/ppc/kernel/ppc-stub.c @@ -0,0 +1,705 @@ +/* $Id: ppc-stub.c,v 1.2 1998/04/11 17:29:03 geert Exp $ + * ppc-stub.c: KGDB support for the Linux kernel. + * + * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC + * some stuff borrowed from Paul Mackerras' xmon + * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu) + * + * Modifications to run under Linux + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * This file originally came from the gdb sources, and the + * copyright notices have been retained below. + */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or its performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: <two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> + +#include <asm/system.h> +#include <asm/signal.h> +#include <asm/system.h> +#include <asm/kgdb.h> +#include <asm/pgtable.h> +#include <asm/ptrace.h> + +void breakinst(void); + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +static int initialized = 0; +static int kgdb_active = 0; +static u_int fault_jmp_buf[100]; +static int kdebug; + +static const char hexchars[]="0123456789abcdef"; + +/* Place where we save old trap entries for restoration - sparc*/ +/* struct tt_entry kgdb_savettable[256]; */ +/* typedef void (*trapfunc_t)(void); */ + +#if 0 +/* Install an exception handler for kgdb */ +static void exceptionHandler(int tnum, unsigned int *tfunc) +{ + /* We are dorking with a live trap table, all irqs off */ +} +#endif + +int +kgdb_setjmp(long *buf) +{ + asm ("mflr 0; stw 0,0(%0);" + "stw 1,4(%0); stw 2,8(%0);" + "mfcr 0; stw 0,12(%0);" + "stmw 13,16(%0)" + : : "r" (buf)); + /* XXX should save fp regs as well */ + return 0; +} +void +kgdb_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm ("lmw 13,16(%0);" + "lwz 0,12(%0); mtcrf 0x38,0;" + "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" + "mtlr 0; mr 3,%1" + : : "r" (buf), "r" (val)); +} +/* Convert ch from a hex digit to an int */ +static int +hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + */ +static unsigned char * +mem2hex(char *mem, char *buf, int count) +{ + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + while (count-- > 0) { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + } else { + /* error condition */ + } + debugger_fault_handler = 0; + *buf = 0; + return buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written. +*/ +static char * +hex2mem(char *buf, char *mem, int count) +{ + int i; + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + for (i=0; i<count; i++) { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + *mem++ = ch; + } + flush_icache_range((int)mem, (int)mem+count); + } else { + /* error condition */ + } + debugger_fault_handler = 0; + return mem; +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + } else { + /* error condition */ + } + debugger_fault_handler = 0; + + return (numChars); +} + +/* scan for the sequence $<data>#<checksum> */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other + * characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ +static void putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch, recv; + + /* $<packet info>#<checksum>. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + recv = getDebugChar(); + } while ((recv & 0x7f) != '+'); +} + +static void kgdb_flush_cache_all(void) +{ + flush_instruction_cache(); +} + +static inline int get_msr() +{ + int msr; + asm volatile("mfmsr %0" : "=r" (msr):); + return msr; +} + +static inline void set_msr(int msr) +{ + asm volatile("mfmsr %0" : : "r" (msr)); +} + +/* Set up exception handlers for tracing and breakpoints + * [could be called kgdb_init()] + */ +void set_debug_traps(void) +{ +#if 0 + unsigned char c; + + save_and_cli(flags); + + /* In case GDB is started before us, ack any packets (presumably + * "$?#xx") sitting there. + * + * I've found this code causes more problems than it solves, + * so that's why it's commented out. GDB seems to work fine + * now starting either before or after the kernel -bwb + */ + + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ +#endif + debugger = kgdb; + debugger_bpt = kgdb_bpt; + debugger_sstep = kgdb_sstep; + debugger_iabr_match = kgdb_iabr_match; + debugger_dabr_match = kgdb_dabr_match; + + kgdb_interruptible(1); + initialized = 1; +} + +static void kgdb_fault_handler(struct pt_regs *regs) +{ + kgdb_longjmp((long*)fault_jmp_buf, 1); +} + +int kgdb_bpt(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +int kgdb_sstep(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +void kgdb(struct pt_regs *regs) +{ + handle_exception(regs); +} + +int kgdb_iabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support iabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +int kgdb_dabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support dabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +/* Convert the SPARC hardware trap type code to a unix signal number. */ +/* + * This table contains the mapping between PowerPC hardware trap types, and + * signals, which are primarily what GDB understands. + */ +static struct hard_trap_info +{ + unsigned int tt; /* Trap type code for powerpc */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* address error (store) */ + { 0x400, SIGBUS }, /* instruction bus error */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alingment */ + { 0x700, SIGILL }, /* reserved instruction or sumpin' */ + { 0x800, SIGFPE }, /* fpu unavail */ + { 0x900, SIGALRM }, /* decrementer */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGINT }, /* watch */ + { 0xe00, SIGFPE }, /* fp assist */ + { 0, 0} /* Must be last */ +}; + +static int computeSignal(unsigned int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * This function does all command processing for interfacing to gdb. + */ +static void +handle_exception (struct pt_regs *regs) +{ + int sigval; + int addr; + int length; + char *ptr; + unsigned int msr; + + if (debugger_fault_handler) { + debugger_fault_handler(regs); + panic("kgdb longjump failed!\n"); + } + if (kgdb_active) { + printk("interrupt while in kgdb, returning\n"); + return; + } + kgdb_active = 1; + + printk("kgdb: entering handle_exception; trap [0x%x]\n", + (unsigned int)regs->trap); + + kgdb_interruptible(0); + lock_kernel(); + msr = get_msr(); + set_msr(msr & ~MSR_EE); /* disable interrupts */ + + if (regs->nip == (unsigned long)breakinst) { + /* Skip over breakpoint trap insn */ + regs->nip += 4; + } + + /* reply to host that an exception has occurred */ + sigval = computeSignal(regs->trap); + ptr = remcomOutBuffer; + + *ptr++ = 'S'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + *ptr++ = 0; + + putpacket(remcomOutBuffer); + + /* XXX We may want to add some features dealing with poking the + * XXX page tables, ... (look at sparc-stub.c for more info) + * XXX also required hacking to the gdb sources directly... + */ + + while (1) { + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': /* report most recent signal */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; +#if 0 + case 'q': /* this screws up gdb for some reason...*/ + { + extern long _start, sdata, __bss_start; + + ptr = &remcomInBuffer[1]; + if (strncmp(ptr, "Offsets", 7) != 0) + break; + + ptr = remcomOutBuffer; + sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x", + &_start, &sdata, &__bss_start); + break; + } +#endif + case 'd': + /* toggle debug flag */ + kdebug ^= 1; + break; + + case 'g': /* return the value of the CPU registers. + * some of them are non-PowerPC names :( + * they are stored in gdb like: + * struct { + * u32 gpr[32]; + * f64 fpr[32]; + * u32 pc, ps, cnd, lr; (ps=msr) + * u32 cnt, xer, mq; + * } + */ + { + int i; + ptr = remcomOutBuffer; + /* General Purpose Regs */ + ptr = mem2hex((char *)regs, ptr, 32 * 4); + /* Floating Point Regs - FIXME */ + /*ptr = mem2hex((char *), ptr, 32 * 8);*/ + for(i=0; i<(32*8*2); i++) { /* 2chars/byte */ + ptr[i] = '0'; + } + ptr += 32*8*2; + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = mem2hex((char *)®s->nip, ptr, 4); + ptr = mem2hex((char *)®s->msr, ptr, 4); + ptr = mem2hex((char *)®s->ccr, ptr, 4); + ptr = mem2hex((char *)®s->link, ptr, 4); + ptr = mem2hex((char *)®s->ctr, ptr, 4); + ptr = mem2hex((char *)®s->xer, ptr, 4); + } + break; + + case 'G': /* set the value of the CPU registers */ + { + ptr = &remcomInBuffer[1]; + + /* + * If the stack pointer has moved, you should pray. + * (cause only god can help you). + */ + + /* General Purpose Regs */ + hex2mem(ptr, (char *)regs, 32 * 4); + + /* Floating Point Regs - FIXME?? */ + /*ptr = hex2mem(ptr, ??, 32 * 8);*/ + ptr += 32*8*2; + + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = hex2mem(ptr, (char *)®s->nip, 4); + ptr = hex2mem(ptr, (char *)®s->msr, 4); + ptr = hex2mem(ptr, (char *)®s->ccr, 4); + ptr = hex2mem(ptr, (char *)®s->link, 4); + ptr = hex2mem(ptr, (char *)®s->ctr, 4); + ptr = hex2mem(ptr, (char *)®s->xer, 4); + + strcpy(remcomOutBuffer,"OK"); + } + break; + case 'H': + /* dont do anything, yet, just acknowledge */ + hexToInt(&ptr, &addr); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, remcomOutBuffer,length)) + break; + strcpy (remcomOutBuffer, "E03"); + } else { + strcpy(remcomOutBuffer,"E01"); + } + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length)) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E03"); + } + } else { + strcpy(remcomOutBuffer, "E02"); + } + break; + + + case 'k': /* kill the program, actually just continue */ + case 'c': /* cAA..AA Continue; address AA..AA optional */ + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) { + regs->nip = addr; + } + +/* Need to flush the instruction cache here, as we may have deposited a + * breakpoint, and the icache probably has no way of knowing that a data ref to + * some location may have changed something that is in the instruction cache. + */ + kgdb_flush_cache_all(); + set_msr(msr); + kgdb_interruptible(1); + unlock_kernel(); + kgdb_active = 0; + return; + + case 's': + kgdb_flush_cache_all(); + regs->msr |= MSR_SE; + set_msr(msr | MSR_SE); + unlock_kernel(); + kgdb_active = 0; + return; + + case 'r': /* Reset (if user process..exit ???)*/ + panic("kgdb reset."); + break; + } /* switch */ + if (remcomOutBuffer[0] && kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1) */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +breakpoint(void) +{ + if (!initialized) { + printk("breakpoint() called b4 kgdb init\n"); + return; + } + + asm(" .globl breakinst + breakinst: trap + "); +} diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c index 386bfe3ac..e1803a269 100644 --- a/arch/ppc/kernel/ppc_htab.c +++ b/arch/ppc/kernel/ppc_htab.c @@ -1,5 +1,5 @@ /* - * $Id: ppc_htab.c,v 1.16 1997/11/17 18:25:04 cort Exp $ + * $Id: ppc_htab.c,v 1.17 1998/03/14 07:52:49 cort Exp $ * * PowerPC hash table management proc entry. Will show information * about the current hash table and will allow changes to it. @@ -12,6 +12,7 @@ * 2 of the License, or (at your option) any later version. */ +#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/proc_fs.h> @@ -88,6 +89,7 @@ struct inode_operations proc_ppc_htab_inode_operations = { #define PMC1 953 #define PMC2 954 +#ifndef CONFIG_8xx char *pmc1_lookup(unsigned long mmcr0) { switch ( mmcr0 & (0x7f<<7) ) @@ -123,7 +125,7 @@ char *pmc2_lookup(unsigned long mmcr0) return "unknown"; } } - +#endif /* CONFIG_8xx */ /* * print some useful info about the hash table. This function @@ -133,6 +135,7 @@ char *pmc2_lookup(unsigned long mmcr0) static ssize_t ppc_htab_read(struct file * file, char * buf, size_t count, loff_t *ppos) { +#ifndef CONFIG_8xx unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0; int n = 0, valid; unsigned int kptes = 0, overflow = 0, uptes = 0, zombie_ptes = 0; @@ -249,6 +252,9 @@ return_string: copy_to_user(buf, buffer + *ppos, n); *ppos += n; return n; +#else /* CONFIG_8xx */ + return 0; +#endif /* CONFIG_8xx */ } /* @@ -257,6 +263,7 @@ return_string: static ssize_t ppc_htab_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) { +#ifndef CONFIG_8xx unsigned long tmp; if ( current->uid != 0 ) return -EACCES; @@ -493,6 +500,9 @@ static ssize_t ppc_htab_write(struct file * file, const char * buffer, reset_SDR1(); #endif return count; +#else /* CONFIG_8xx */ + return 0; +#endif /* CONFIG_8xx */ } @@ -512,4 +522,3 @@ ppc_htab_lseek(struct file * file, loff_t offset, int orig) return(-EINVAL); } } - diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 6ec6258ab..29df507d3 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -4,9 +4,7 @@ #include <linux/elfcore.h> #include <linux/sched.h> #include <linux/string.h> -#include <linux/bios32.h> #include <linux/interrupt.h> -#include <linux/pci.h> #include <asm/semaphore.h> #include <asm/processor.h> @@ -18,9 +16,11 @@ #include <asm/pgtable.h> #include <asm/adb.h> #include <asm/cuda.h> +#include <asm/pmu.h> #include <asm/prom.h> #include <asm/system.h> #include <asm/pci-bridge.h> +#include <asm/irq.h> extern void transfer_to_handler(void); extern void int_return(void); @@ -49,9 +49,13 @@ EXPORT_SYMBOL(sys_sigreturn); EXPORT_SYMBOL(lost_interrupts); EXPORT_SYMBOL(do_lost_interrupts); EXPORT_SYMBOL(__ppc_bh_counter); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); -#if !defined(CONFIG_MACH_SPECIFIC) +#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC) EXPORT_SYMBOL(isa_io_base); +#endif +#if !defined(CONFIG_MACH_SPECIFIC) EXPORT_SYMBOL(pci_dram_offset); #endif @@ -114,11 +118,15 @@ EXPORT_SYMBOL(outw); EXPORT_SYMBOL(outl); EXPORT_SYMBOL(outsl);*/ +EXPORT_SYMBOL(_insb); +EXPORT_SYMBOL(_outsb); EXPORT_SYMBOL(_insw); EXPORT_SYMBOL(_outsw); EXPORT_SYMBOL(_insl); EXPORT_SYMBOL(_outsl); EXPORT_SYMBOL(ioremap); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(start_thread); @@ -140,6 +148,10 @@ EXPORT_SYMBOL(adb_autopoll); EXPORT_SYMBOL(adb_register); EXPORT_SYMBOL(cuda_request); EXPORT_SYMBOL(cuda_send_request); +EXPORT_SYMBOL(cuda_poll); +EXPORT_SYMBOL(pmu_request); +EXPORT_SYMBOL(pmu_send_request); +EXPORT_SYMBOL(pmu_poll); EXPORT_SYMBOL(abort); EXPORT_SYMBOL(find_devices); EXPORT_SYMBOL(find_type_devices); @@ -148,7 +160,3 @@ EXPORT_SYMBOL(get_property); EXPORT_SYMBOL(pci_io_base); EXPORT_SYMBOL(pci_device_loc); EXPORT_SYMBOL(note_scsi_host); - -#if CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c index 193ded4df..999406200 100644 --- a/arch/ppc/kernel/prep_pci.c +++ b/arch/ppc/kernel/prep_pci.c @@ -1,5 +1,5 @@ /* - * $Id: prep_pci.c,v 1.12 1997/10/29 03:35:08 cort Exp $ + * $Id: prep_pci.c,v 1.16 1998/02/23 02:47:32 davem Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -8,7 +8,6 @@ */ #include <linux/types.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/kernel.h> #include <linux/init.h> @@ -271,6 +270,12 @@ static char Nobis_pci_IRQ_routes[] = { #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] */ +/* + * FIXME: This code incorrectly assumes there's only bus #0, breaking all + * PCI-to-PCI bridges. Also multi-function devices are not supported + * at all. [mj] + */ + int prep_pcibios_read_config_dword (unsigned char bus, unsigned char dev, unsigned char offset, unsigned int *val) @@ -319,16 +324,6 @@ prep_pcibios_read_config_byte (unsigned char bus, unsigned char _val; volatile unsigned char *ptr; dev >>= 3; - /* Note: the configuration registers don't always have this right! */ - if (offset == PCI_INTERRUPT_LINE) - { - *val = Motherboard_routes[Motherboard_map[dev]]; -/*printk("dev %d map %d route %d on board %d\n", - dev,Motherboard_map[dev], - Motherboard_routes[Motherboard_map[dev]], - *(unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)));*/ - return PCIBIOS_SUCCESSFUL; - } if ((bus != 0) || (dev > MAX_DEVNR)) { *(unsigned long *)val = (unsigned long) 0xFFFFFFFF; @@ -406,7 +401,7 @@ __initfunc(unsigned long route_pci_interrupts(void)) int i; if ( _prep_type == _PREP_Motorola) - { + { switch (inb(0x800) & 0xF0) { case 0x10: /* MVME16xx */ @@ -430,7 +425,6 @@ __initfunc(unsigned long route_pci_interrupts(void)) 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; @@ -474,3 +468,4 @@ __initfunc(unsigned long route_pci_interrupts(void)) *ibc_pcicon |= 0x20; return 0; } + diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index 0aee7cff4..5234435ca 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -80,8 +80,7 @@ prep_get_cpuinfo(char *buffer) { extern char *Motherboard_map_name; extern RESIDUAL res; - int i; - int len; + int len, i; #ifdef __SMP__ #define CD(X) (cpu_data[n].X) @@ -93,7 +92,7 @@ prep_get_cpuinfo(char *buffer) if ( res.ResidualLength == 0 ) return len; - + /* print info about SIMMs */ len += sprintf(buffer+len,"simms\t\t: "); for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ ) @@ -106,6 +105,7 @@ prep_get_cpuinfo(char *buffer) } len += sprintf(buffer+len,"\n"); +#if 0 /* TLB */ len += sprintf(buffer+len,"tlb\t\t:"); switch(res.VitalProductData.TLBAttrib) @@ -123,7 +123,6 @@ prep_get_cpuinfo(char *buffer) len += sprintf(buffer+len," not present\n"); break; } - /* L1 */ len += sprintf(buffer+len,"l1\t\t: "); switch(res.VitalProductData.CacheAttrib) @@ -144,6 +143,7 @@ prep_get_cpuinfo(char *buffer) len += sprintf(buffer+len,"not present\n"); break; } +#endif /* L2 */ if ( (inb(IBM_EQUIP_PRESENT) & 1) == 0) /* l2 present */ @@ -201,7 +201,11 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) } } #endif - + /* make the serial port the console */ + /* strcat(cmd_line,"console=ttyS0,9600n8"); */ + /* use the normal console but send output to the serial port, too */ + /*strcat(cmd_line,"console=tty0 console=ttyS0,9600n8");*/ + sprintf(cmd_line,"%s console=tty0 console=ttyS0,9600n8", cmd_line); printk("Boot arguments: %s\n", cmd_line); #ifdef CONFIG_CS4232 @@ -256,9 +260,5 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) #ifdef CONFIG_VGA_CONSOLE conswitchp = &vga_con; #endif -#ifdef CONFIG_FB - /* Frame buffer device based console */ - conswitchp = &fb_con; -#endif #endif } diff --git a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c index 4c3a91f91..3536eab43 100644 --- a/arch/ppc/kernel/prep_time.c +++ b/arch/ppc/kernel/prep_time.c @@ -220,7 +220,7 @@ static inline void timer_interrupt(int irq, void *dev, struct pt_regs * regs) #ifdef CONFIG_HEARTBEAT /* use hard disk LED as a heartbeat instead -- much more useful for debugging -- Cort */ - switch(kstat.interrupts[0] % 101) + switch(kstat_irqs(0) % 101) { /* act like an actual heart beat -- ie thump-thump-pause... */ case 0: diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index 7ffaf58c0..1c993bdc1 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -1,4 +1,3 @@ - /* * linux/arch/ppc/kernel/process.c * @@ -77,8 +76,13 @@ struct task_struct *current_set[NR_CPUS] = {&init_task, }; int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) { +#ifdef __SMP__ + if ( regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else if (last_task_used_math == current) giveup_fpu(); +#endif memcpy(fpregs, ¤t->tss.fpr[0], sizeof(*fpregs)); return 1; } @@ -98,7 +102,7 @@ int check_stack(struct task_struct *tsk) printk("tss.magic bad: %08x\n", tsk->tss.magic); } #endif - + if ( !tsk ) printk("check_stack(): tsk bad tsk %p\n",tsk); @@ -157,17 +161,21 @@ switch_to(struct task_struct *prev, struct task_struct *new) #endif #ifdef SHOW_TASK_SWITCHES - printk("%s/%d -> %s/%d cpu %d\n", + printk("%s/%d -> %s/%d NIP %08lx cpu %d sfr %d lock %x\n", prev->comm,prev->pid, - new->comm,new->pid,new->processor); + new->comm,new->pid,new->tss.regs->nip,new->processor, + new->tss.smp_fork_ret,scheduler_lock.lock); #endif #ifdef __SMP__ - /* bad news if last_task_used_math changes processors right now -- Cort */ - if ( (last_task_used_math == new) && - (new->processor != new->last_processor) ) - panic("last_task_used_math switched processors"); + /* avoid complexity of lazy save/restore of fpu + * by just saving it every time we switch out -- Cort + */ + if ( prev->tss.regs->msr & MSR_FP ) + smp_giveup_fpu(prev); + /* be noisy about processor changes for debugging -- Cort */ - if ( new->last_processor != new->processor ) + if ( (new->last_processor != NO_PROC_ID) && + (new->last_processor != new->processor) ) printk("switch_to(): changing cpu's %d -> %d %s/%d\n", new->last_processor,new->processor, new->comm,new->pid); @@ -181,11 +189,6 @@ switch_to(struct task_struct *prev, struct task_struct *new) _enable_interrupts(s); } -asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs) -{ - return 0; -} - void show_regs(struct pt_regs * regs) { int i; @@ -257,12 +260,11 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, ((unsigned long)p + sizeof(union task_union) - STACK_FRAME_OVERHEAD)) - 2; *childregs = *regs; - 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 - STACK_FRAME_OVERHEAD; - p->tss.regs = childregs; + p->tss.regs = childregs; if (usp >= (unsigned long) regs) { /* Stack is in kernel space - must adjust */ childregs->gpr[1] = (unsigned long)(childregs + 1); @@ -271,18 +273,28 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, childregs->gpr[1] = usp; } p->tss.last_syscall = -1; - + /* * copy fpu info - assume lazy fpu switch now always * -- Cort */ +#ifdef __SMP__ + if ( regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else if ( last_task_used_math == current ) giveup_fpu(); +#endif memcpy(&p->tss.fpr, ¤t->tss.fpr, sizeof(p->tss.fpr)); p->tss.fpscr = current->tss.fpscr; childregs->msr &= ~MSR_FP; +#ifdef __SMP__ + if ( (p->pid != 0) || !(clone_flags & CLONE_PID) ) + p->tss.smp_fork_ret = 1; + p->last_processor = NO_PROC_ID; +#endif /* __SMP__ */ return 0; } @@ -337,20 +349,48 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) shove_aux_table(sp); } +asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + unsigned long clone_flags = p1; + int res; + lock_kernel(); + res = do_fork(clone_flags, regs->gpr[1], regs); + /* + * only parent returns here, child returns to either + * syscall_ret_1() or kernel_thread() + * -- Cort + */ +#ifdef __SMP__ + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; +#endif /* __SMP__ */ + unlock_kernel(); + return res; +} asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) { - int ret; + int res; lock_kernel(); - ret = do_fork(SIGCHLD, regs->gpr[1], regs); -#if 0/*def __SMP__*/ - if ( ret ) /* drop scheduler lock in child */ - scheduler_lock.lock = 0L; -#endif /* __SMP__ */ + res = do_fork(SIGCHLD, regs->gpr[1], regs); + /* only parent returns here */ +#ifdef __SMP__ + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; +#endif /* __SMP__ */ unlock_kernel(); - return ret; + return res; } asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, @@ -374,28 +414,6 @@ out: return error; } -asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, - struct pt_regs *regs) -{ - unsigned long clone_flags = p1; - int res; - - lock_kernel(); - res = do_fork(clone_flags, regs->gpr[1], regs); -#ifdef __SMP__ - /* When we clone the idle task we keep the same pid but - * the return value of 0 for both causes problems. - * -- Cort - */ - if ((current->pid == 0) && (current == &init_task)) - res = 1; - if ( 0 /*res*/ ) /* drop scheduler lock in child */ - scheduler_lock.lock = 0L; -#endif /* __SMP__ */ - unlock_kernel(); - return res; -} - void print_backtrace(unsigned long *sp) { diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c index 121ffea73..0e656caa1 100644 --- a/arch/ppc/kernel/prom.c +++ b/arch/ppc/kernel/prom.c @@ -16,6 +16,8 @@ #include <asm/prom.h> #include <asm/page.h> #include <asm/processor.h> +#include <asm/irq.h> +#include <asm/io.h> /* * Properties whose value is longer than this get excluded from our @@ -50,6 +52,19 @@ struct pci_range { unsigned size_lo; }; +struct isa_reg_property { + unsigned space; + unsigned address; + unsigned size; +}; + +typedef unsigned long interpret_func(struct device_node *, unsigned long); +static interpret_func interpret_pci_props; +static interpret_func interpret_dbdma_props; +static interpret_func interpret_isa_props; +static interpret_func interpret_macio_props; +static interpret_func interpret_root_props; + char *prom_display_paths[FB_MAX] __initdata = { 0, }; unsigned int prom_num_displays = 0; @@ -60,19 +75,21 @@ extern char *klimit; char *bootpath = 0; char *bootdevice = 0; -unsigned int rtas_data = 0; -unsigned int rtas_entry = 0; +unsigned int rtas_data = 0; /* virtual pointer */ +unsigned int rtas_entry = 0; /* physical pointer */ +unsigned int rtas_size = 0; +char chunk[PAGE_SIZE*64]; static struct device_node *allnodes = 0; static void *call_prom(const char *service, int nargs, int nret, ...); -static void prom_print(const char *msg); + void prom_print(const char *msg); static void prom_exit(void); static unsigned long copy_device_tree(unsigned long, unsigned long); static unsigned long inspect_node(phandle, struct device_node *, unsigned long, unsigned long, struct device_node ***); static unsigned long finish_node(struct device_node *, unsigned long, - unsigned long); + interpret_func *); static unsigned long check_display(unsigned long); static int prom_next_node(phandle *); @@ -119,6 +136,18 @@ prom_exit() ; } +void +prom_enter(void) +{ + struct prom_args args; + unsigned long offset = reloc_offset(); + + args.service = RELOC("enter"); + args.nargs = 0; + args.nret = 0; + RELOC(prom)(&args); +} + static void * call_prom(const char *service, int nargs, int nret, ...) { @@ -140,7 +169,7 @@ call_prom(const char *service, int nargs, int nret, ...) return prom_args.args[nargs]; } -static void +void prom_print(const char *msg) { const char *p, *q; @@ -160,6 +189,11 @@ prom_print(const char *msg) } } + +#ifdef CONFIG_ALL_PPC +unsigned char OF_type[16], OF_model[16]; +#endif + /* * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. @@ -169,11 +203,14 @@ prom_init(int r3, int r4, prom_entry pp) { unsigned long mem; ihandle prom_rtas; - unsigned int rtas_size; unsigned long offset = reloc_offset(); int l; char *p, *d; + /* check if we're prep, return if we are */ + if ( *(unsigned long *)(0) == 0xdeadc0de ) + return; + /* First get a handle for the stdout device */ RELOC(prom) = pp; RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1, @@ -209,27 +246,57 @@ prom_init(int r3, int r4, prom_entry pp) prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas")); if (prom_rtas != (void *) -1) { - rtas_size = 0; + RELOC(rtas_size) = 0; call_prom(RELOC("getprop"), 4, 1, prom_rtas, - RELOC("rtas-size"), &rtas_size, sizeof(rtas_size)); + RELOC("rtas-size"), &RELOC(rtas_size), sizeof(rtas_size)); prom_print(RELOC("instantiating rtas...")); - if (rtas_size == 0) { + if (RELOC(rtas_size) == 0) { RELOC(rtas_data) = 0; } else { mem = (mem + 4095) & -4096; /* round to page bdry */ RELOC(rtas_data) = mem - KERNELBASE; - mem += rtas_size; + mem += RELOC(rtas_size); + } + prom_rtas = call_prom(RELOC("open"), 1, 1, RELOC("/rtas")); + RELOC(rtas_data) = ((ulong)chunk+4095)&-4096; + { + int i, nargs; + struct prom_args prom_args; + nargs = 3; + prom_args.service = RELOC("call-method"); + prom_args.nargs = nargs; + prom_args.nret = 2; + prom_args.args[0] = RELOC("instantiate-rtas"); + prom_args.args[1] = prom_rtas; + prom_args.args[2] = ((void *)RELOC(rtas_data)-KERNELBASE); + RELOC(prom)(&prom_args); + if (prom_args.args[nargs] != 0) + i = 0; + else + i = (int)prom_args.args[nargs+1]; + RELOC(rtas_entry) = i; } - RELOC(rtas_entry) = (unsigned int) - call_prom(RELOC("instantiate-rtas"), 1, 1, - RELOC(rtas_data)); - if (RELOC(rtas_entry) == -1) + if ((RELOC(rtas_entry) == -1) || (RELOC(rtas_entry) == 0)) prom_print(RELOC(" failed\n")); else prom_print(RELOC(" done\n")); } RELOC(klimit) = (char *) (mem - offset); +#ifdef CONFIG_ALL_PPC + { + + ihandle prom_root; + + RELOC(prom_root) = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); + call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root), + RELOC("device_type"), RELOC(OF_type), + (void *) 16); + call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root), + RELOC("model"), RELOC(OF_model), + (void *) 16); + } +#endif } /* @@ -397,12 +464,18 @@ inspect_node(phandle node, struct device_node *dad, return mem_start; } +/* + * finish_device_tree is called once things are running normally + * (i.e. with text and data mapped to the address they were linked at). + * It traverses the device tree and fills in the name, type, + * {n_}addrs and {n_}intrs fields of each node. + */ void finish_device_tree(void) { unsigned long mem = (unsigned long) klimit; - mem = finish_node(allnodes, mem, 0UL); + mem = finish_node(allnodes, mem, NULL); printk(KERN_INFO "device tree used %lu bytes\n", mem - (unsigned long) allnodes); klimit = (char *) mem; @@ -410,23 +483,53 @@ finish_device_tree(void) static unsigned long finish_node(struct device_node *np, unsigned long mem_start, - unsigned long base_address) + interpret_func *ifunc) { - struct reg_property *rp; - struct pci_reg_property *pci_addrs; - struct address_range *adr; struct device_node *child; - int i, l; np->name = get_property(np, "name", 0); np->type = get_property(np, "device_type", 0); - /* get all the device addresses and interrupts */ - adr = (struct address_range *) mem_start; + /* get the device addresses and interrupts */ + if (ifunc != NULL) + mem_start = ifunc(np, mem_start); + + if (!strcmp(np->name, "device-tree")) + ifunc = interpret_root_props; + else if (np->type == 0) + ifunc = NULL; + else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) + ifunc = interpret_pci_props; + else if (!strcmp(np->type, "dbdma") + || (ifunc == interpret_dbdma_props + && (!strcmp(np->type, "escc") + || !strcmp(np->type, "media-bay")))) + ifunc = interpret_dbdma_props; + else if (!strcmp(np->type, "mac-io")) + ifunc = interpret_macio_props; + else if (!strcmp(np->type, "isa")) + ifunc = interpret_isa_props; + else + ifunc = NULL; + + for (child = np->child; child != NULL; child = child->sibling) + mem_start = finish_node(child, mem_start, ifunc); + + return mem_start; +} + +static unsigned long +interpret_pci_props(struct device_node *np, unsigned long mem_start) +{ + struct address_range *adr; + struct pci_reg_property *pci_addrs; + int i, l, *ip; + pci_addrs = (struct pci_reg_property *) get_property(np, "assigned-addresses", &l); - i = 0; - if (pci_addrs != 0) { + if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; while ((l -= sizeof(struct pci_reg_property)) >= 0) { /* XXX assumes PCI addresses mapped 1-1 to physical */ adr[i].space = pci_addrs[i].addr.a_hi; @@ -434,36 +537,194 @@ finish_node(struct device_node *np, unsigned long mem_start, adr[i].size = pci_addrs[i].size_lo; ++i; } - } else { - rp = (struct reg_property *) get_property(np, "reg", &l); - if (rp != 0) { - while ((l -= sizeof(struct reg_property)) >= 0) { - adr[i].space = 0; - adr[i].address = rp[i].address + base_address; - adr[i].size = rp[i].size; - ++i; - } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 0; + } + } + + return mem_start; +} + +static unsigned long +interpret_dbdma_props(struct device_node *np, unsigned long mem_start) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; } } - if (i > 0) { + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } np->addrs = adr; np->n_addrs = i; mem_start += i * sizeof(struct address_range); } - np->intrs = (int *) get_property(np, "AAPL,interrupts", &l); - if (np->intrs == 0) - np->intrs = (int *) get_property(np, "interrupts", &l); - if (np->intrs != 0) + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 0; + } + } - if (np->type != 0 && np->n_addrs > 0 - && (strcmp(np->type, "dbdma") == 0 - || strcmp(np->type, "mac-io") == 0)) - base_address = np->addrs[0].address; + return mem_start; +} - for (child = np->child; child != NULL; child = child->sibling) - mem_start = finish_node(child, mem_start, base_address); +static unsigned long +interpret_macio_props(struct device_node *np, unsigned long mem_start) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; + } + } + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + ip = (int *) get_property(np, "interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / (2 * sizeof(int)); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = openpic_to_irq(*ip++); + np->intrs[i].sense = *ip++; + } + } + + return mem_start; +} + +static unsigned long +interpret_isa_props(struct device_node *np, unsigned long mem_start) +{ + struct isa_reg_property *rp; + struct address_range *adr; + int i, l, *ip; + + rp = (struct isa_reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct isa_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = rp[i].space; + adr[i].address = rp[i].address + + (adr[i].space? 0: _ISA_MEM_BASE); + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / (2 * sizeof(int)); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = *ip++; + } + } + + return mem_start; +} + +static unsigned long +interpret_root_props(struct device_node *np, unsigned long mem_start) +{ + struct reg_property *rp; + struct address_range *adr; + int i, l, *ip; + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 0; + } + } return mem_start; } @@ -648,14 +909,14 @@ call_rtas(const char *service, int nargs, int nret, printk(KERN_ERR "No RTAS service called %s\n", service); return -1; } - u.words[0] = *tokp; + u.words[0] = __pa(*tokp); u.words[1] = nargs; u.words[2] = nret; va_start(list, outputs); for (i = 0; i < nargs; ++i) u.words[i+3] = va_arg(list, unsigned long); va_end(list); - enter_rtas(&u); + enter_rtas((void *)__pa(&u)); if (nret > 1 && outputs != NULL) for (i = 0; i < nret-1; ++i) outputs[i] = u.words[i+nargs+4]; diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index 4f6068c6d..ce2f35058 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -390,8 +390,14 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) tmp = get_reg(child, addr); } else if (addr >= PT_FPR0 && addr <= PT_FPSCR) { +#ifdef __SMP__ + if (child->tss.regs->msr & MSR_FP ) + smp_giveup_fpu(child); +#else + /* only current can be last task to use math on SMP */ if (last_task_used_math == child) giveup_fpu(); +#endif tmp = ((long *)child->tss.fpr)[addr - PT_FPR0]; } else @@ -423,8 +429,13 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) goto out; } if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) { +#ifndef __SMP__ + if (child->tss.regs->msr & MSR_FP ) + smp_giveup_fpu(child); +#else if (last_task_used_math == child) giveup_fpu(); +#endif ((long *)child->tss.fpr)[addr - PT_FPR0] = data; ret = 0; goto out; diff --git a/arch/ppc/kernel/residual.c b/arch/ppc/kernel/residual.c index fdf62e921..b5516d0e5 100644 --- a/arch/ppc/kernel/residual.c +++ b/arch/ppc/kernel/residual.c @@ -1,5 +1,5 @@ /* - * $Id: residual.c,v 1.5 1997/10/30 21:25:19 cort Exp $ + * $Id: residual.c,v 1.7 1998/03/08 05:49:20 davem Exp $ * * Code to deal with the PReP residual data. * diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 86407e273..bdf05af76 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.48 1998/01/01 10:04:44 paulus Exp $ + * $Id: setup.c,v 1.68 1998/04/07 08:20:33 geert Exp $ * Common prep/pmac/chrp boot and setup code. */ @@ -13,10 +13,26 @@ #include <asm/adb.h> #include <asm/cuda.h> +#include <asm/pmu.h> #include <asm/residual.h> #include <asm/io.h> #include <asm/ide.h> #include <asm/prom.h> +#include <asm/processor.h> +#ifdef CONFIG_MBX +#include <asm/mbx.h> +#endif +/* ifdef APUS specific stuff until the merge is completed. -jskov */ +#ifdef CONFIG_APUS +#include <asm/bootinfo.h> +#include <asm/setup.h> +#include <asm/amigappc.h> +extern unsigned long m68k_machtype; +extern void amiga_reset (void); +extern struct mem_info m68k_ramdisk; +extern int m68k_parse_bootinfo(const struct bi_record *); +extern char _end[]; +#endif extern char cmd_line[512]; char saved_command_line[256]; @@ -26,13 +42,13 @@ unsigned char aux_device_present; unsigned long ISA_DMA_THRESHOLD; unsigned long DMA_MODE_READ, DMA_MODE_WRITE; int _machine; +/* if we have openfirmware */ +unsigned long have_of; #endif /* ! CONFIG_MACH_SPECIFIC */ /* copy of the residual data */ RESIDUAL res; int _prep_type; -/* if we have openfirmware */ -unsigned long have_of; /* * Perhaps we can put the pmac screen_info[] here @@ -40,6 +56,7 @@ unsigned long have_of; * Until we get multiple-console support in here * that is. -- Cort */ +#ifndef CONFIG_MBX #if !defined(CONFIG_PMAC_CONSOLE) struct screen_info screen_info = { 0, 25, /* orig-x, orig-y */ @@ -65,29 +82,66 @@ void pmac_find_display(void) } #endif +#else /* CONFIG_MBX */ + +/* We need this to satisfy some external references until we can + * strip the kernel down. + */ +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 */ + 0, /* orig-video-isVGA */ + 16 /* orig-video-points */ +}; +#endif /* CONFIG_MBX */ + /* cmd is ignored for now... */ void machine_restart(char *cmd) { struct adb_request req; unsigned long flags; unsigned long i = 10000; -#if 0 +#if 0 int err; #endif switch(_machine) { case _MACH_Pmac: - cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); - for (;;) - cuda_poll(); + switch (adb_hardware) { + case ADB_VIACUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_RESET_SYSTEM); + for (;;) + cuda_poll(); + break; + case ADB_VIAPMU: + pmu_request(&req, NULL, 1, PMU_RESET); + for (;;) + pmu_poll(); + break; + default: + } break; + case _MACH_chrp: #if 0 /* RTAS doesn't seem to work on Longtrail. For now, do it the same way as the PReP. */ - err = call_rtas("system-reboot", 0, 1, NULL); + /*err = call_rtas("system-reboot", 0, 1, NULL); printk("RTAS system-reboot returned %d\n", err); - for (;;); + for (;;);*/ + + { + extern unsigned int rtas_entry, rtas_data, rtas_size; + unsigned long status, value; + printk("rtas_entry: %08x rtas_data: %08x rtas_size: %08x\n", + rtas_entry,rtas_data,rtas_size); + } #endif case _MACH_prep: _disable_interrupts(); @@ -104,6 +158,23 @@ void machine_restart(char *cmd) while ( i != 0 ) i++; panic("restart failed\n"); break; + case _MACH_apus: + cli(); + /* APUS:FIXME: Reset the system. Apparently there's + * more magic to it than this!?!? + */ +#if 0 + APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); + APUS_WRITE(APUS_REG_RESET, + REGRESET_PPCRESET|REGRESET_M68KRESET| + REGRESET_AMIGARESET|REGRESET_AUXRESET| + REGRESET_SCSIRESET); +#endif + printk("\n**************************************\n"); + printk("*** You can make a hard reset now! ***\n"); + printk("**************************************\n"); + for(;;); + break; } } @@ -116,9 +187,23 @@ void machine_power_off(void) switch (_machine) { case _MACH_Pmac: - cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); - for (;;) - cuda_poll(); + switch (adb_hardware) { + case ADB_VIACUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_POWERDOWN); + for (;;) + cuda_poll(); + break; + case ADB_VIAPMU: + pmu_request(&req, NULL, 5, PMU_SHUTDOWN, + 'M', 'A', 'T', 'T'); + for (;;) + pmu_poll(); + break; + default: + } + break; + case _MACH_chrp: #if 0 /* RTAS doesn't seem to work on Longtrail. For now, do it the same way as the PReP. */ @@ -126,9 +211,19 @@ void machine_power_off(void) printk("RTAS system-reboot returned %d\n", err); for (;;); #endif + case _MACH_prep: machine_restart(NULL); +#ifdef CONFIG_APUS + case _MACH_apus: +#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) + apm_set_power_state(APM_STATE_OFF); + for (;;); +#endif +#endif } + for (;;) + ; } void machine_halt(void) @@ -141,18 +236,90 @@ void machine_halt(void) machine_power_off(); /* for now */ #endif } - else /* prep or chrp */ + else /* prep, chrp or apus */ machine_restart(NULL); } +#ifdef CONFIG_BLK_DEV_IDE void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) { - if ( _machine == _MACH_Pmac ) + switch (_machine) { + case _MACH_Pmac: pmac_ide_init_hwif_ports(p,base,irq); - else /* prep or chrp */ + break; + case _MACH_chrp: + chrp_ide_init_hwif_ports(p,base,irq); + break; + case _MACH_prep: prep_ide_init_hwif_ports(p,base,irq); + break; + } +} +#endif + +unsigned long cpu_temp(void) +{ + unsigned long i, temp, thrm1, dir; + int sanity; + /* + * setup thrm3 - need to give TAU at least 20us + * to do the compare so assume a 300MHz clock. + * We need 300*20 ticks then. + * -- Cort + */ + asm("mtspr 1020, %1\n\t" + "mtspr 1021, %1\n\t" + "mtspr 1022, %0\n\t":: + "r" ( ((300*20)<<18) | THRM3_E), "r" (0) ); + +#if 0 + for ( i = 127 ; i >= 0 ; i-- ) + { + asm("mtspr 1020, %0\n\t":: + "r" (THRM1_TID|THRM1_V|(i<<2)) ); + /* check value */ + while ( !( thrm1 & THRM1_TIV) ) + asm("mfspr %0, 1020 \n\t": "=r" (thrm1) ); + if ( thrm1 & THRM1_TIN ) + { + printk("tin set: %x tiv %x\n", thrm1,thrm1&THRM1_TIV); + goto out; + } + + } +#endif +#if 0 + i = 32; /* increment */ + dir = 1; /* direction we're checking 0=up 1=down */ + temp = 64; /* threshold checking against */ + while ( i ) + { + _set_THRM1((1<<29) | THRM1_V | (temp<<2) ); + printk("checking %d in dir %d thrm set to %x/%x\n", temp,dir, + ( (1<<29) | THRM1_V | (temp<<2)),_get_THRM1()); + /* check value */ + sanity = 0x0fffffff; + while ( (!( thrm1 & THRM1_TIV)) && (sanity--) ) + thrm1 = _get_THRM1(); + /*asm("mfspr %0, 1020 \n\t": "=r" (thrm1) );*/ + if ( ! sanity || sanity==0xffffffff ) printk("no sanity\n"); + /* temp is not in that direction */ + if ( !(thrm1 & THRM1_TIN) ) + { + printk("not in that dir thrm1 %x\n",thrm1); + if ( dir == 0 ) dir = 1; + else dir = 0; + } + if ( dir ) temp -= i; + else temp += i; + i /= 2; + } + asm("mtspr 1020, %0\n\t" + "mtspr 1022, %0\n\t" ::"r" (0) ); +#endif + return temp; } int get_cpuinfo(char *buffer) @@ -160,9 +327,11 @@ int get_cpuinfo(char *buffer) extern int pmac_get_cpuinfo(char *); extern int chrp_get_cpuinfo(char *); extern int prep_get_cpuinfo(char *); + extern int apus_get_cpuinfo(char *); unsigned long len = 0; unsigned long bogosum = 0; unsigned long i; + unsigned long cr; #ifdef __SMP__ extern unsigned long cpu_present_map; extern struct cpuinfo_PPC cpu_data[NR_CPUS]; @@ -202,7 +371,18 @@ int get_cpuinfo(char *buffer) len += sprintf(len+buffer, "603ev\n"); break; case 8: - len += sprintf(len+buffer, "750 (Arthur)\n"); + len += sprintf(len+buffer,"750\n"); + cr = _get_L2CR(); + len += sprintf(len+buffer,"L2CR\t\t: %lx\n",cr); + if ( cr & (0x1<<1)) cr = 256; + else if ( cr & (0x2<<1)) cr = 512; + else if ( cr & (0x3<<1)) cr = 1024; + else cr = 0; + len += sprintf(len+buffer,"on-chip l2\t: " + "%ld KB (%s)\n", + cr,(_get_L2CR()&1) ? "on" : "off"); + len += sprintf(len+buffer,"temperature \t: %lu C\n", + cpu_temp()); break; case 9: len += sprintf(len+buffer, "604e\n"); @@ -216,6 +396,7 @@ int get_cpuinfo(char *buffer) break; } + /* * Assume here that all clock rates are the same in a * smp system. -- Cort @@ -290,6 +471,11 @@ int get_cpuinfo(char *buffer) case _MACH_chrp: len += chrp_get_cpuinfo(buffer+len); break; +#ifdef CONFIG_APUS + case _MACH_apus: + len += apus_get_cpuinfo(buffer+len); + break; +#endif } return len; } @@ -302,43 +488,63 @@ __initfunc(unsigned long identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7)) { - extern unsigned long initrd_start, initrd_end; extern setup_pci_ptrs(void); - unsigned long boot_sdr1; - ihandle prom_root; - unsigned char type[16], model[16]; - - asm("mfspr %0,25\n\t" :"=r" (boot_sdr1)); +#ifndef CONFIG_MBX8xx - /* - * if we have a sdr1 then we have openfirmware - * and can ask it what machine we are (chrp/pmac/prep). - * otherwise we're definitely prep. -- Cort - */ - if ( !boot_sdr1 ) +#ifdef CONFIG_APUS + if ( r3 == 0x61707573 ) { - /* we know for certain we're prep if no OF */ + /* Parse bootinfo. The bootinfo is located right after + the kernel bss */ + m68k_parse_bootinfo((const struct bi_record *)&_end); + have_of = 0; - /* make a copy of residual data */ - if ( r3 ) - memcpy((void *)&res,(void *)(r3+KERNELBASE), - sizeof(RESIDUAL)); + +#ifdef CONFIG_BLK_DEV_INITRD + /* Take care of initrd if we have one. Use data from + bootinfo to avoid the need to initialize PPC + registers when kernel is booted via a PPC reset. */ + if ( m68k_ramdisk.addr ) { + initrd_start = (unsigned long) __va(m68k_ramdisk.addr); + initrd_end = (unsigned long) + __va(m68k_ramdisk.size + m68k_ramdisk.addr); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + return 0; + } +#endif + #ifndef CONFIG_MACH_SPECIFIC + /* prep boot loader tells us if we're prep or not */ + if ( *(unsigned long *)(KERNELBASE) == (0xdeadc0de) ) + { _machine = _MACH_prep; -#endif /* CONFIG_MACH_SPECIFIC */ + have_of = 0; + } else + { + /* need to ask OF if we're chrp or pmac */ + extern unsigned char OF_type[16], OF_model[16]; + prom_print(OF_type); + prom_print(OF_model); + if ( !strncmp("chrp", OF_type,4) ) + { + _machine = _MACH_chrp; + } + else + { + /*if ( !strncmp("Power Macintosh", type,15) )*/ + _machine = _MACH_Pmac; + } + _machine = _MACH_Pmac; + } - else +#endif /* CONFIG_MACH_SPECIFIC */ + + if ( have_of ) { - /* - * init prom here, then ask the openfirmware - * what machine we are (prep/chrp/pmac). We don't use - * OF on prep just yet. -- Cort - */ -#ifndef CONFIG_PREP /* don't use OF on prep yet */ - have_of = 1; /* prom_init has already been called from __start */ finish_device_tree(); - /* * If we were booted via quik, r3 points to the physical * address of the command-line parameters. @@ -356,7 +562,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, } else { struct device_node *chosen; char *p; - + #ifdef CONFIG_BLK_DEV_INITRD if (r3 - KERNELBASE < 0x800000 && r4 != 0 && r4 != 0xdeadbeef) { @@ -365,7 +571,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); } #endif - chosen = find_path_device("/chosen"); + chosen = find_devices("chosen"); if (chosen != NULL) { p = get_property(chosen, "bootargs", NULL); if (p != NULL) @@ -373,47 +579,18 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, } } cmd_line[sizeof(cmd_line) - 1] = 0; -#endif /* CONFIG_PREP */ - -#ifndef CONFIG_MACH_SPECIFIC -#if 0 - prom_root = call_prom("finddevice", 1, 1, "/"); - call_prom("getprop", 4, 1, prom_root, "device_type", &type, - (void *) sizeof(type)); - call_prom("getprop", 4, 1, prom_root, "model", &type, - (void *) sizeof(model)); - if ( !strncmp("chrp", type,4) ) - { - _machine = _MACH_chrp; - } - else - { - /*if ( !strncmp("Power Macintosh", type,15) )*/ - _machine = _MACH_Pmac; - } -#else - -#ifdef CONFIG_CHRP - _machine = _MACH_chrp; -#endif /* CONFIG_CHRP */ -#ifdef CONFIG_PMAC - _machine = _MACH_Pmac; -#endif /* CONFIG_PMAC */ -#ifdef CONFIG_PREP - _machine = _MACH_Prep; -#endif /* CONFIG_PREP */ -#endif /* #if */ -#endif /* CONFIG_MACH_SPECIFIC */ } +#ifdef CONFIG_PCI /* so that pmac/chrp can use pci to find its console -- Cort */ setup_pci_ptrs(); - +#endif + switch (_machine) { case _MACH_Pmac: #if !defined(CONFIG_MACH_SPECIFIC) - isa_io_base = PMAC_ISA_IO_BASE; + /* isa_io_base gets set in pmac_find_bridges */ isa_mem_base = PMAC_ISA_MEM_BASE; pci_dram_offset = PMAC_PCI_DRAM_OFFSET; ISA_DMA_THRESHOLD = ~0L; @@ -422,6 +599,10 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, #endif /* ! CONFIG_MACH_SPECIFIC */ break; case _MACH_prep: + /* make a copy of residual data */ + if ( r3 ) + memcpy((void *)&res,(void *)(r3+KERNELBASE), + sizeof(RESIDUAL)); #if !defined(CONFIG_MACH_SPECIFIC) isa_io_base = PREP_ISA_IO_BASE; isa_mem_base = PREP_ISA_MEM_BASE; @@ -434,13 +615,12 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, if ( res.ResidualLength != 0 ) { if ( !strncmp(res.VitalProductData.PrintableModel,"IBM",3) ) - _prep_type = 0x00; + _prep_type = _PREP_IBM; else - _prep_type = 0x01; + _prep_type = _PREP_Motorola; } else /* assume motorola if no residual (netboot?) */ _prep_type = _PREP_Motorola; - #ifdef CONFIG_BLK_DEV_RAM /* take care of initrd if we have one */ if ( r4 ) @@ -457,7 +637,14 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, } break; case _MACH_chrp: - /* LongTrail */ +#ifdef CONFIG_BLK_DEV_RAM + /* take care of initrd if we have one */ + if ( r3 ) + { + initrd_start = r3 + KERNELBASE; + initrd_end = r3+ r4 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_RAM */ #if !defined(CONFIG_MACH_SPECIFIC) isa_io_base = CHRP_ISA_IO_BASE; isa_mem_base = CHRP_ISA_MEM_BASE; @@ -470,13 +657,33 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, default: printk("Unknown machine type in identify_machine!\n"); } - return 0; -} +#else /* CONFIG_MBX8xx */ + extern setup_pci_ptrs(void); -__initfunc(unsigned long -bios32_init(unsigned long memory_start, unsigned long memory_end)) -{ - return memory_start; + if ( r3 ) + memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); + + setup_pci_ptrs(); + +#ifdef CONFIG_BLK_DEV_RAM + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_RAM */ + /* take care of cmd line */ + if ( r6 ) + { + + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + +#endif /* CONFIG_MBX */ + + return 0; } __initfunc(void setup_arch(char **cmdline_p, @@ -485,12 +692,18 @@ __initfunc(void setup_arch(char **cmdline_p, extern void pmac_setup_arch(unsigned long *, unsigned long *); extern void chrp_setup_arch(unsigned long *, unsigned long *); extern void prep_setup_arch(unsigned long *, unsigned long *); + extern void apus_setup_arch(char **, unsigned long *, unsigned long *); extern int panic_timeout; extern char _etext[], _edata[]; extern char *klimit; extern unsigned long find_available_memory(void); extern unsigned long *end_of_DRAM; +#ifdef CONFIG_XMON + extern void xmon_map_scc(void); + xmon_map_scc(); +#endif /* CONFIG_XMON */ + /* reboot on panic */ panic_timeout = 180; @@ -516,6 +729,12 @@ __initfunc(void setup_arch(char **cmdline_p, case _MACH_chrp: chrp_setup_arch(memory_start_p, memory_end_p); break; +#ifdef CONFIG_APUS + case _MACH_apus: + m68k_machtype = MACH_AMIGA; + apus_setup_arch(cmdline_p,memory_start_p,memory_end_p); + break; +#endif default: printk("Unknown machine %d in setup_arch()\n", _machine); } diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index ae6d3c5aa..1c977bb51 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -201,8 +201,13 @@ int sys_sigreturn(struct pt_regs *regs) if (sc == (struct sigcontext_struct *)(sigctx.regs)) { /* Last stacked signal - restore registers */ sr = (struct sigregs *) sigctx.regs; +#ifdef __SMP__ + if ( regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else if (last_task_used_math == current) giveup_fpu(); +#endif if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) goto badframe; @@ -249,8 +254,13 @@ setup_frame(struct pt_regs *regs, struct sigregs *frame, if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) goto badframe; - if (last_task_used_math == current) - giveup_fpu(); +#ifdef __SMP__ + if ( regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else + if (last_task_used_math == current) + giveup_fpu(); +#endif if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) || __copy_to_user(&frame->fp_regs, current->tss.fpr, ELF_NFPREG * sizeof(double)) diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index 33d3a23b4..217e695f8 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -1,5 +1,5 @@ /* - * $Id: smp.c,v 1.8 1998/01/06 06:44:57 cort Exp $ + * $Id: smp.c,v 1.22 1998/04/10 01:53:34 cort Exp $ * * Smp support for ppc. * @@ -30,22 +30,27 @@ #include <asm/init.h> #include <asm/io.h> +#include "time.h" + int smp_threads_ready = 0; volatile int smp_commenced = 0; int smp_num_cpus = 1; unsigned long cpu_present_map = 0; volatile int cpu_number_map[NR_CPUS]; volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; +volatile int __cpu_logical_map[NR_CPUS]; static unsigned char boot_cpu_id = 0; struct cpuinfo_PPC cpu_data[NR_CPUS]; -struct klock_info klock_info = { KLOCK_CLEAR, 0 }; +struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 }; volatile unsigned char active_kernel_processor = NO_PROC_ID; /* Processor holding kernel spinlock */ -volatile unsigned long hash_table_lock; +volatile unsigned long ipi_count; + +unsigned int prof_multiplier[NR_CPUS]; +unsigned int prof_counter[NR_CPUS]; int start_secondary(void *); -extern void init_IRQ(void); extern int cpu_idle(void *unused); void smp_boot_cpus(void) @@ -56,49 +61,75 @@ void smp_boot_cpus(void) struct task_struct *p; printk("Entering SMP Mode...\n"); + + for (i = 0; i < NR_CPUS; i++) { + cpu_number_map[i] = -1; + prof_counter[i] = 1; + prof_multiplier[i] = 1; + } + cpu_present_map = 0; for(i=0; i < NR_CPUS; i++) - cpu_number_map[i] = -1; + __cpu_logical_map[i] = -1; smp_store_cpu_info(boot_cpu_id); active_kernel_processor = boot_cpu_id; current->processor = boot_cpu_id; - cpu_present_map |= 1; + cpu_number_map[boot_cpu_id] = 0; + __cpu_logical_map[0] = boot_cpu_id; + + if ( _machine != _MACH_Pmac ) + { + printk("SMP not supported on this machine.\n"); + return; + } + /* assume a 2nd processor for now */ cpu_present_map |= (1 << 1); smp_num_cpus = 2; - cpu_number_map[boot_cpu_id] = 0; /* create a process for second processor */ kernel_thread(start_secondary, NULL, CLONE_PID); - cpu_number_map[1] = 1; p = task[1]; if ( !p ) panic("No idle task for secondary processor\n"); p->processor = 1; current_set[1] = p; + /* need to flush here since secondary bat's aren't setup */ + dcbf((volatile unsigned long *)¤t_set[1]); + /* setup entry point of secondary processor */ *(volatile unsigned long *)(0xf2800000) = (unsigned long)secondary_entry-KERNELBASE; - /* interrupt secondary to begin executing code */ eieio(); - *(volatile unsigned long *)(0xf80000c0) = 0; + /* interrupt secondary to begin executing code */ + *(volatile unsigned long *)(0xf80000c0) = 0L; eieio(); /* wait to see if the secondary made a callin (is actually up) */ - for ( timeout = 0; timeout < 1500 ; timeout++ ) + for ( timeout = 0; timeout < 15000 ; timeout++ ) + { + if(cpu_callin_map[1]) + break; udelay(100); + } if(cpu_callin_map[1]) { cpu_number_map[1] = 1; + __cpu_logical_map[i] = 1; printk("Processor 1 found.\n"); + +#if 0 /* this sync's the decr's */ + set_dec(decrementer_count); +#endif + /* interrupt secondary to sync the time bases */ + smp_message_pass(1,0xf0f0, 0, 0); + /* interrupt secondary to begin executing code */ + /**(volatile unsigned long *)(0xf80000c0) = 0L; + eieio();*/ } else { smp_num_cpus--; printk("Processor %d is stuck.\n", 1); } -{ - extern unsigned long amhere; - printk("amhere: %x\n", amhere); -} } void smp_commence(void) @@ -128,16 +159,15 @@ int start_secondary(void *unused) void smp_callin(void) { printk("SMP %d: smp_callin()\n",current->processor); - /*calibrate_delay();*/ smp_store_cpu_info(1); - - /* assume we're just the secondary processor for now */ - cpu_callin_map[1] = 1; - dcbf(&cpu_callin_map[1]); + set_dec(decrementer_count); current->mm->mmap->vm_page_prot = PAGE_SHARED; current->mm->mmap->vm_start = PAGE_OFFSET; current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; + + /* assume we're just the secondary processor for now */ + cpu_callin_map[1] = 1; while(!smp_commenced) barrier(); @@ -149,21 +179,120 @@ void smp_setup(char *str, int *ints) printk("SMP %d: smp_setup()\n",current->processor); } -void smp_message_pass(int target, int msg, unsigned long data, int wait) +void smp_local_timer_interrupt(struct pt_regs * regs) { - printk("SMP %d: sending smp message\n",current->processor); -#if 0 - if ( smp_processor_id() == 0 ) + int cpu = smp_processor_id(); + extern void update_one_process(struct task_struct *,unsigned long, + unsigned long,unsigned long,int); + + if (!--prof_counter[cpu]) { + int user=0,system=0; + struct task_struct * p = current; + + /* + * After doing the above, we need to make like + * a normal interrupt - otherwise timer interrupts + * ignore the global interrupt lock, which is the + * WrongThing (tm) to do. + */ + + if (user_mode(regs)) + user=1; + else + system=1; + + if (p->pid) { + update_one_process(p, 1, user, system, cpu); + + p->counter -= 1; + if (p->counter < 0) { + p->counter = 0; + need_resched = 1; + } + if (p->priority < DEF_PRIORITY) { + kstat.cpu_nice += user; + kstat.per_cpu_nice[cpu] += user; + } else { + kstat.cpu_user += user; + kstat.per_cpu_user[cpu] += user; + } + + kstat.cpu_system += system; + kstat.per_cpu_system[cpu] += system; + + } + prof_counter[cpu]=prof_multiplier[cpu]; + } +} + +/* + * Dirty hack to get smp message passing working. + * Right now it only works for stop cpu's but will be setup + * later for more general message passing. + * + * As it is now, if we're sending two message as the same time + * we have race conditions. I avoided doing locks here since + * all that works right now is the stop cpu message. + * + * -- Cort + */ +int smp_message[NR_CPUS]; +void smp_message_recv(void) +{ + int msg = smp_message[smp_processor_id()]; + + printk("SMP %d: smp_message_recv() msg %x\n", smp_processor_id(),msg); + + /* make sure msg is for us */ + if ( msg == -1 ) return; +printk("recv after msg check\n"); + switch( msg ) { - /* interrupt secondary */ - *(volatile unsigned long *)(0xf80000c0) = 0; + case MSG_STOP_CPU: + __cli(); + while (1) ; + break; + case 0xf0f0: /* syncing time bases - just return */ + break; + default: + printk("SMP %d: smp_message_recv(): unknown msg %d\n", + smp_processor_id(), msg); + break; } - else + /* reset message */ + smp_message[smp_processor_id()] = -1; +} + +spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED; +void smp_message_pass(int target, int msg, unsigned long data, int wait) +{ + printk("SMP %d: sending smp message\n", current->processor); + + spin_lock(&mesg_pass_lock); + if ( _machine != _MACH_Pmac ) + return; + +#define OTHER (~smp_processor_id() & 1) + + switch( target ) { - /* interrupt primary */ - *(volatile unsigned long *)(0xf3019000); + case MSG_ALL: + smp_message[smp_processor_id()] = msg; + /* fall through */ + case MSG_ALL_BUT_SELF: + smp_message[OTHER] = msg; + break; + default: + smp_message[target] = msg; + break; } -#endif + /* interrupt secondary processor */ + /**(volatile unsigned long *)(0xf80000c0) = 0xffffffff; + eieio();*/ + *(volatile unsigned long *)(0xf80000c0) = 0; + /* interrupt primary */ + /**(volatile unsigned long *)(0xf3019000);*/ + spin_unlock(&mesg_pass_lock); } int setup_profiling_timer(unsigned int multiplier) diff --git a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c new file mode 100644 index 000000000..dedf24806 --- /dev/null +++ b/arch/ppc/kernel/softemu8xx.c @@ -0,0 +1,96 @@ +/* + * Software emulation of some PPC instructions for the 8xx core. + * + * Copyright (C) 1998 Dan Malek (dmalek@jlc.net) + * + * Software floating emuation for the MPC8xx processor. I did this mostly + * because it was easier than trying to get the libraries compiled for + * software floating point. The goal is still to get the libraries done, + * but I lost patience and needed some hacks to at least get init and + * shells running. The first problem is the setjmp/longjmp that save + * and restore the floating point registers. + * + * For this emulation, our working registers are found on the register + * save area. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/interrupt.h> + +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/processor.h> + +/* Eventually we may need a look-up table, but this works for now. +*/ +#define LFD 50 +#define LFDU 51 +#define STFD 54 +#define STFDU 55 + +/* + * We return 0 on success, 1 on unimplemented instruction, and EFAULT + * if a load/store faulted. + */ +int +Soft_emulate_8xx(struct pt_regs *regs) +{ + uint inst, instword; + uint flreg, idxreg, disp; + uint retval; + uint *ea, *ip; + + retval = 0; + + instword = *((uint *)regs->nip); + inst = instword >> 26; + + flreg = (instword >> 21) & 0x1f; + idxreg = (instword >> 16) & 0x1f; + disp = instword & 0xffff; + + ea = (uint *)(regs->gpr[idxreg] + disp); + ip = (uint *)¤t->tss.fpr[flreg]; + + if (inst == LFD) { + if (copy_from_user(ip, ea, sizeof(double))) + retval = EFAULT; + } + else if (inst == LFDU) { + + if (copy_from_user(ip, ea, sizeof(double))) + retval = EFAULT; + else + regs->gpr[idxreg] = (uint)ea; + } + else if (inst == STFD) { + + if (copy_to_user(ea, ip, sizeof(double))) + retval = EFAULT; + } + else if (inst == STFDU) { + + if (copy_to_user(ea, ip, sizeof(double))) + retval = EFAULT; + else + regs->gpr[idxreg] = (uint)ea; + } + else { + retval = 1; + } + + if (retval == 0) + regs->nip += 4; + return(retval); +} diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c index 319db15de..5a1063d4e 100644 --- a/arch/ppc/kernel/time.c +++ b/arch/ppc/kernel/time.c @@ -1,11 +1,25 @@ /* - * $Id: time.c,v 1.17 1997/12/28 22:47:21 paulus Exp $ + * $Id: time.c,v 1.28 1998/04/07 18:49:49 cort Exp $ * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge * Paul Mackerras' version and mine for PReP and Pmac. + * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net). + * + * Since the MPC8xx has a programmable interrupt timer, I decided to + * use that rather than the decrementer. Two reasons: 1.) the clock + * frequency is low, causing 2.) a long wait in the timer interrupt + * while ((d = get_dec()) == dval) + * loop. The MPC8xx can be driven from a variety of input clocks, + * so a number of assumptions have been made here because the kernel + * parameter HZ is a constant. We assume (correctly, today :-) that + * the MPC8xx on the MBX board is driven from a 32.768 kHz crystal. + * This is then divided by 4, providing a 8192 Hz clock into the PIT. + * Since it is not possible to get a nice 100 Hz clock out of this, without + * creating a software PLL, I have set HZ to 128. -- Dan */ +#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -22,12 +36,21 @@ #include <asm/io.h> #include <asm/processor.h> #include <asm/nvram.h> +#include <asm/cache.h> +#ifdef CONFIG_MBX +#include <asm/mbx.h> +#endif +#ifdef CONFIG_8xx +#include <asm/8xx_immap.h> +#endif #include "time.h" /* this is set to the appropriate pmac/prep/chrp func in init_IRQ() */ int (*set_rtc_time)(unsigned long); +void smp_local_timer_interrupt(struct pt_regs *); + /* keep track of when we need to update the rtc */ unsigned long last_rtc_update = 0; @@ -48,6 +71,14 @@ unsigned count_period_den; /* count_period_num / count_period_den us */ void timer_interrupt(struct pt_regs * regs) { int dval, d; + unsigned long cpu = smp_processor_id(); + /* save the HID0 in case dcache was off - see idle.c + * this hack should leave for a better solution -- Cort */ + unsigned dcache_locked = unlock_dcache(); + +if ( smp_processor_id() ) printk("SMP 1: timer intr\n"); + hardirq_enter(cpu); + while ((dval = get_dec()) < 0) { /* * Wait for the decrementer to change, then jump @@ -57,17 +88,49 @@ void timer_interrupt(struct pt_regs * regs) while ((d = get_dec()) == dval) ; set_dec(d + decrementer_count); - do_timer(regs); - /* - * update the rtc when needed - */ - if ( xtime.tv_sec > last_rtc_update + 660 ) - if (set_rtc_time(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + if ( !smp_processor_id() ) + { + do_timer(regs); + /* + * update the rtc when needed + */ + if ( xtime.tv_sec > last_rtc_update + 660 ) + if (set_rtc_time(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 __SMP__ + smp_local_timer_interrupt(regs); +#endif + + hardirq_exit(cpu); + /* restore the HID0 in case dcache was off - see idle.c + * this hack should leave for a better solution -- Cort */ + lock_dcache(dcache_locked); +} + +#ifdef CONFIG_MBX +/* A place holder for time base interrupts, if they are ever enabled. +*/ +void timebase_interrupt(int irq, void * dev, struct pt_regs * regs) +{ +} + +/* The RTC on the MPC8xx is an internal register. + * We want to protect this during power down, so we need to unlock, + * modify, and re-lock. + */ +static int +mbx_set_rtc_time(unsigned long time) +{ + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY; + ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc = time; + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY; + return(0); } +#endif /* CONFIG_MBX */ /* * This version of gettimeofday has microsecond resolution. @@ -108,6 +171,7 @@ void do_settimeofday(struct timeval *tv) void time_init(void) { +#ifndef CONFIG_MBX if ((_get_PVR() >> 16) == 1) { /* 601 processor: dec counts down by 128 every 128ns */ decrementer_count = DECREMENTER_COUNT_601; @@ -119,9 +183,10 @@ time_init(void) case _MACH_Pmac: /* can't call pmac_get_rtc_time() yet, because via-cuda isn't initialized yet. */ - if ((_get_PVR() >> 16) != 1) + if ( (_get_PVR() >> 16) != 1 && (!smp_processor_id()) ) pmac_calibrate_decr(); - set_rtc_time = pmac_set_rtc_time; + if ( !smp_processor_id() ) + set_rtc_time = pmac_set_rtc_time; break; case _MACH_chrp: chrp_time_init(); @@ -135,18 +200,63 @@ time_init(void) prep_calibrate_decr(); set_rtc_time = prep_set_rtc_time; break; +/* ifdef APUS specific stuff until the merge is completed. -jskov */ +#ifdef CONFIG_APUS + case _MACH_apus: + { + xtime.tv_sec = apus_get_rtc_time(); + apus_calibrate_decr(); + set_rtc_time = apus_set_rtc_time; + break; } +#endif + } + xtime.tv_usec = 0; + set_dec(decrementer_count); +#else + mbx_calibrate_decr(); + set_rtc_time = mbx_set_rtc_time; + + /* First, unlock all of the registers we are going to modify. + * To protect them from corruption during power down, registers + * that are maintained by keep alive power are "locked". To + * modify these registers we have to write the key value to + * the key location associated with the register. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY; + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY; + + + /* Disable the RTC one second and alarm interrupts. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtcsc &= + ~(RTCSC_SIE | RTCSC_ALE); + + /* Enabling the decrementer also enables the timebase interrupts + * (or from the other point of view, to get decrementer interrupts + * we have to enable the timebase). The decrementer interrupt + * is wired into the vector table, nothing to do here for that. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_tbscr = + ((mk_int_int_mask(DEC_INTERRUPT) << 8) | + (TBSCR_TBF | TBSCR_TBE)); + if (request_irq(DEC_INTERRUPT, timebase_interrupt, 0, "tbint", NULL) != 0) + panic("Could not allocate timer IRQ!"); + + /* Get time from the RTC. + */ + xtime.tv_sec = ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc; xtime.tv_usec = 0; - /* - * mark the rtc/on-chip timer as in sync +#endif /* CONFIG_MBX */ + + /* mark the rtc/on-chip timer as in sync * so we don't update right away */ last_rtc_update = xtime.tv_sec; - - set_dec(decrementer_count); } +#ifndef CONFIG_MBX /* * Uses the on-board timer to calibrate the on-chip decrementer register * for prep systems. On the pmac the OF tells us what the frequency is @@ -158,6 +268,27 @@ volatile int *done_ptr = &calibrate_done; void prep_calibrate_decr(void) { unsigned long flags; + + /* the Powerstack II's have trouble with the timer so + * we use a default value -- Cort + */ + if ( (_prep_type == _PREP_Motorola) && + ((inb(0x800) & 0xF0) & 0x40) ) + { + unsigned long freq, divisor; + static unsigned long t2 = 0; + + t2 = 998700000/60; + freq = t2 * 60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", + freq, divisor,t2>>20); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; + return; + } + save_flags(flags); @@ -181,7 +312,7 @@ void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs) { unsigned long freq, divisor; static unsigned long t1 = 0, t2 = 0; - + if ( !t1 ) t1 = get_dec(); else if (!t2) @@ -189,11 +320,6 @@ void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs) t2 = get_dec(); t2 = t1-t2; /* decr's in 1/HZ */ t2 = t2*HZ; /* # decrs in 1s - thus in Hz */ -if ( (t2>>20) > 100 ) -{ - printk("Decrementer frequency too high: %luMHz. Using 15MHz.\n",t2>>20); - t2 = 998700000/60; -} freq = t2 * 60; /* try to make freq/1e6 an integer */ divisor = 60; printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", @@ -205,6 +331,32 @@ if ( (t2>>20) > 100 ) } } +#else /* CONFIG_MBX */ + +/* The decrementer counts at the system (internal) clock frequency divided by + * sixteen, or external oscillator divided by four. Currently, we only + * support the MBX, which is system clock divided by sixteen. + */ +void mbx_calibrate_decr(void) +{ + int freq, fp, divisor; + + if ((((immap_t *)MBX_IMAP_ADDR)->im_clkrst.car_sccr & 0x02000000) == 0) + printk("WARNING: Wrong decrementer source clock.\n"); + + /* The manual says the frequency is in Hz, but it is really + * as MHz. The value 'fp' is the number of decrementer ticks + * per second. + */ + /*fp = (mbx_board_info.bi_intfreq * 1000000) / 16;*/ + 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; +} +#endif /* CONFIG_MBX */ /* 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 diff --git a/arch/ppc/kernel/time.h b/arch/ppc/kernel/time.h index 538ac7bb1..64a07250c 100644 --- a/arch/ppc/kernel/time.h +++ b/arch/ppc/kernel/time.h @@ -1,5 +1,5 @@ /* - * $Id: time.h,v 1.7 1997/12/28 22:47:24 paulus Exp $ + * $Id: time.h,v 1.10 1998/04/01 07:46:03 geert Exp $ * Common time prototypes and such for all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -12,6 +12,7 @@ void prep_calibrate_decr_handler(int, void *,struct pt_regs *); void prep_calibrate_decr(void); void pmac_calibrate_decr(void); +extern void apus_calibrate_decr(void); extern unsigned decrementer_count; extern unsigned count_period_num; extern unsigned count_period_den; @@ -24,12 +25,16 @@ extern unsigned long last_rtc_update; unsigned long prep_get_rtc_time(void); unsigned long pmac_get_rtc_time(void); unsigned long chrp_get_rtc_time(void); +unsigned long apus_get_rtc_time(void); int prep_set_rtc_time(unsigned long nowtime); int pmac_set_rtc_time(unsigned long nowtime); int chrp_set_rtc_time(unsigned long nowtime); +int apus_set_rtc_time(unsigned long nowtime); void pmac_read_rtc_time(void); void chrp_calibrate_decr(void); void chrp_time_init(void); +int via_calibrate_decr(void); +void mbx_calibrate_decr(void); /* Accessor functions for the decrementer register. */ static __inline__ unsigned int get_dec(void) diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 7199dd5c1..c5c90b527 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -39,13 +39,31 @@ extern int fix_alignment(struct pt_regs *); extern void bad_page_fault(struct pt_regs *, unsigned long); #ifdef CONFIG_XMON +extern void xmon(struct pt_regs *regs); extern int xmon_bpt(struct pt_regs *regs); extern int xmon_sstep(struct pt_regs *regs); -extern void xmon(struct pt_regs *regs); extern int xmon_iabr_match(struct pt_regs *regs); +extern int xmon_dabr_match(struct pt_regs *regs); extern void (*xmon_fault_handler)(struct pt_regs *regs); #endif +#ifdef CONFIG_XMON +void (*debugger)(struct pt_regs *regs) = xmon; +int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt; +int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep; +int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match; +int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match; +void (*debugger_fault_handler)(struct pt_regs *regs); +#else +#ifdef CONFIG_KGDB +void (*debugger)(struct pt_regs *regs); +int (*debugger_bpt)(struct pt_regs *regs); +int (*debugger_sstep)(struct pt_regs *regs); +int (*debugger_iabr_match)(struct pt_regs *regs); +int (*debugger_dabr_match)(struct pt_regs *regs); +void (*debugger_fault_handler)(struct pt_regs *regs); +#endif +#endif /* * Trap & Exception support */ @@ -61,8 +79,8 @@ _exception(int signr, struct pt_regs *regs) if (!user_mode(regs)) { show_regs(regs); -#ifdef CONFIG_XMON - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); #endif print_backtrace((unsigned long *)regs->gpr[1]); panic("Exception in kernel pc %lx signal %d",regs->nip,signr); @@ -75,9 +93,9 @@ MachineCheckException(struct pt_regs *regs) { if ( !user_mode(regs) ) { -#ifdef CONFIG_XMON - if (xmon_fault_handler) { - xmon_fault_handler(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler) { + debugger_fault_handler(regs); return; } #endif @@ -103,8 +121,8 @@ MachineCheckException(struct pt_regs *regs) printk("Unknown values in msr\n"); } show_regs(regs); -#ifdef CONFIG_XMON - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); #endif print_backtrace((unsigned long *)regs->gpr[1]); panic("machine check"); @@ -123,8 +141,8 @@ UnknownException(struct pt_regs *regs) void InstructionBreakpoint(struct pt_regs *regs) { -#ifdef CONFIG_XMON - if (xmon_iabr_match(regs)) +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_iabr_match(regs)) return; #endif _exception(SIGTRAP, regs); @@ -144,8 +162,8 @@ ProgramCheckException(struct pt_regs *regs) _exception(SIGFPE, regs); } else if (regs->msr & 0x20000) { /* trap exception */ -#ifdef CONFIG_XMON - if (xmon_bpt(regs)) +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_bpt(regs)) return; #endif _exception(SIGTRAP, regs); @@ -158,8 +176,8 @@ void SingleStepException(struct pt_regs *regs) { regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ -#ifdef CONFIG_XMON - if (xmon_sstep(regs)) +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_sstep(regs)) return; #endif _exception(SIGTRAP, regs); @@ -170,8 +188,13 @@ AlignmentException(struct pt_regs *regs) { int fixed; +#ifdef __SMP__ + if (regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else if (last_task_used_math == current) giveup_fpu(); +#endif fixed = fix_alignment(regs); if (fixed == 1) { regs->nip += 4; /* skip over emulated instruction */ @@ -190,8 +213,8 @@ StackOverflow(struct pt_regs *regs) { printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n", current, regs->gpr[1]); -#ifdef CONFIG_XMON - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); #endif show_regs(regs); print_backtrace((unsigned long *)regs->gpr[1]); @@ -205,3 +228,41 @@ trace_syscall(struct pt_regs *regs) current, current->pid, regs->nip, regs->link, regs->gpr[0], regs->ccr&0x10000000?"Error=":"", regs->gpr[3]); } + +#ifdef CONFIG_8xx + +void +SoftwareEmulation(struct pt_regs *regs) +{ + int errcode; + extern int Soft_emulate_8xx (struct pt_regs *regs); + + if (user_mode(regs)) { +#if 0 + printk("(user mode)\n"); + _exception(SIGTRAP, regs); +#else + if (errcode = Soft_emulate_8xx(regs)) { +printk("Software Emulation 0x%x: 0x%x ", + regs->nip, *((uint *)regs->nip)); +print_8xx_pte(current->mm, regs->nip); + if (errcode == EFAULT) + _exception(SIGBUS, regs); + else + _exception(SIGILL, regs); + } +#endif + } + else { + printk("(kernel mode)\n"); + panic("Kernel Mode Software Emulation"); + } +} +#endif + +void +TAUException(struct pt_regs *regs) +{ + printk("TAU trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); +} diff --git a/arch/ppc/lib/locks.c b/arch/ppc/lib/locks.c index 5e2ceb889..55cc665d7 100644 --- a/arch/ppc/lib/locks.c +++ b/arch/ppc/lib/locks.c @@ -1,5 +1,5 @@ /* - * $Id: locks.c,v 1.7 1998/01/06 06:44:59 cort Exp $ + * $Id: locks.c,v 1.17 1998/03/26 22:19:38 cort Exp $ * * Locks for smp ppc * @@ -9,53 +9,78 @@ #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/delay.h> #include <asm/processor.h> #include <asm/system.h> #include <asm/spinlock.h> +#include <asm/io.h> #define DEBUG_LOCKS 1 #undef INIT_STUCK -#define INIT_STUCK 10000 - -#undef STUCK -#define STUCK \ -if(!--stuck) { printk("spin_lock(%p) CPU#%d nip %08lx\n", lock, cpu, nip); stuck = INIT_STUCK; } +#define INIT_STUCK 1000000 void _spin_lock(spinlock_t *lock) { - unsigned long val, nip = (unsigned long)__builtin_return_address(0); int cpu = smp_processor_id(); +#ifdef DEBUG_LOCKS int stuck = INIT_STUCK; - -again: +#endif /* DEBUG_LOCKS */ /* try expensive atomic load/store to get lock */ - __asm__ __volatile__( - "10: \n\t" - "lwarx %0,0,%1 \n\t" - "stwcx. %2,0,%1 \n\t" - "bne- 10b \n\t" - : "=r" (val) - : "r" (&(lock->lock)), "r" ( (cpu&3)|(nip&~3L) )); - if(val) { + while((unsigned long )xchg_u32((void *)&lock->lock,0xffffffff)) { /* try cheap load until it's free */ while(lock->lock) { - STUCK; +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("_spin_lock(%p) CPU#%d NIP %p" + " holder: cpu %ld pc %08lX\n", + lock, cpu, __builtin_return_address(0), + lock->owner_cpu,lock->owner_pc); + stuck = INIT_STUCK; + /* steal the lock */ + /*xchg_u32((void *)&lock->lock,0);*/ + } +#endif /* DEBUG_LOCKS */ barrier(); } - goto again; } + lock->owner_pc = (unsigned long)__builtin_return_address(0); + lock->owner_cpu = cpu; +} + +int spin_trylock(spinlock_t *lock) +{ + unsigned long result; + + result = (unsigned long )xchg_u32((void *)&lock->lock,0xffffffff); + if ( !result ) + { + lock->owner_cpu = smp_processor_id(); + lock->owner_pc = (unsigned long)__builtin_return_address(0); + } + return (result == 0); } + + void _spin_unlock(spinlock_t *lp) { +#ifdef DEBUG_LOCKS + if ( !lp->lock ) + panic("_spin_unlock(%p): no lock cpu %d %s/%d\n", lp, + smp_processor_id(),current->comm,current->pid); + if ( lp->owner_cpu != smp_processor_id() ) + panic("_spin_unlock(%p): cpu %d trying clear of cpu %d pc %lx val %lx\n", + lp, smp_processor_id(), (int)lp->owner_cpu, + lp->owner_pc,lp->lock); +#endif /* DEBUG_LOCKS */ + lp->owner_pc = lp->owner_cpu = 0; + eieio(); lp->lock = 0; + eieio(); } -#undef STUCK -#define STUCK \ -if(!--stuck) { printk("_read_lock(%p) CPU#%d\n", rw, cpu); stuck = INIT_STUCK; } - /* * Just like x86, implement read-write locks as a 32-bit counter * with the high bit (sign) being the "write" bit. @@ -63,8 +88,10 @@ if(!--stuck) { printk("_read_lock(%p) CPU#%d\n", rw, cpu); stuck = INIT_STUCK; } */ void _read_lock(rwlock_t *rw) { +#ifdef DEBUG_LOCKS unsigned long stuck = INIT_STUCK; int cpu = smp_processor_id(); +#endif /* DEBUG_LOCKS */ again: /* get our read lock in there */ @@ -76,7 +103,13 @@ again: /* wait for the write lock to go away */ while ((signed long)((rw)->lock) < 0) { - STUCK; +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("_read_lock(%p) CPU#%d\n", rw, cpu); + stuck = INIT_STUCK; + } +#endif /* DEBUG_LOCKS */ } /* try to get the read lock again */ goto again; @@ -87,33 +120,34 @@ void _read_unlock(rwlock_t *rw) { #ifdef DEBUG_LOCKS if ( rw->lock == 0 ) - { - if ( current) printk("_read_unlock(): %s/%d (nip %08lX) lock %lx", - current->comm,current->pid,current->tss.regs->nip, + current->comm,current->pid,current->tss.regs->nip, rw->lock); - else - printk("no current\n"); - } #endif /* DEBUG_LOCKS */ atomic_dec((atomic_t *) &(rw)->lock); } -#undef STUCK -#define STUCK \ -if(!--stuck) { printk("write_lock(%p) CPU#%d lock %lx)\n", rw, cpu,rw->lock); stuck = INIT_STUCK; } - void _write_lock(rwlock_t *rw) { +#ifdef DEBUG_LOCKS unsigned long stuck = INIT_STUCK; int cpu = smp_processor_id(); +#endif /* DEBUG_LOCKS */ again: if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */ { while ( (rw)->lock & (1<<31) ) /* wait for write lock */ { - STUCK; +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("write_lock(%p) CPU#%d lock %lx)\n", + rw, cpu,rw->lock); + stuck = INIT_STUCK; + } +#endif /* DEBUG_LOCKS */ + barrier(); } goto again; } @@ -124,7 +158,15 @@ again: clear_bit(31,&(rw)->lock); while ( (rw)->lock & ~(1<<31) ) { - STUCK; +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("write_lock(%p) 2 CPU#%d lock %lx)\n", + rw, cpu,rw->lock); + stuck = INIT_STUCK; + } +#endif /* DEBUG_LOCKS */ + barrier(); } goto again; } @@ -134,14 +176,9 @@ void _write_unlock(rwlock_t *rw) { #ifdef DEBUG_LOCKS if ( !(rw->lock & (1<<31)) ) - { - if ( current) printk("_write_lock(): %s/%d (nip %08lX) lock %lx", current->comm,current->pid,current->tss.regs->nip, rw->lock); - else - printk("no current\n"); - } #endif /* DEBUG_LOCKS */ clear_bit(31,&(rw)->lock); } @@ -149,6 +186,8 @@ void _write_unlock(rwlock_t *rw) void __lock_kernel(struct task_struct *task) { #ifdef DEBUG_LOCKS + unsigned long stuck = INIT_STUCK; + if ( (signed long)(task->lock_depth) < 0 ) { printk("__lock_kernel(): %s/%d (nip %08lX) lock depth %x\n", @@ -156,20 +195,40 @@ void __lock_kernel(struct task_struct *task) task->lock_depth); } #endif /* DEBUG_LOCKS */ + + if ( atomic_inc_return((atomic_t *) &task->lock_depth) != 1 ) + return; /* mine! */ - if ( atomic_inc_return((atomic_t *) &task->lock_depth) == 1 ) - klock_info.akp = smp_processor_id(); + while ( xchg_u32( (void *)&klock_info.kernel_flag, KLOCK_HELD) ) + { + /* try cheap load until it's free */ + while(klock_info.kernel_flag) { +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("_lock_kernel() CPU#%d NIP %p\n", + smp_processor_id(), + __builtin_return_address(0)); + stuck = INIT_STUCK; + } +#endif /* DEBUG_LOCKS */ + barrier(); + } + } + + klock_info.akp = smp_processor_id(); /* my kernel mode! mine!!! */ } - + void __unlock_kernel(struct task_struct *task) { #ifdef DEBUG_LOCKS - if ( task->lock_depth == 0 ) + if ( (task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD) ) { - printk("__unlock_kernel(): %s/%d (nip %08lX) lock depth %x\n", - task->comm,task->pid,task->tss.regs->nip, - task->lock_depth); + printk("__unlock_kernel(): %s/%d (nip %08lX) " + "lock depth %x flags %lx\n", + task->comm,task->pid,task->tss.regs->nip, + task->lock_depth, klock_info.kernel_flag); klock_info.akp = NO_PROC_ID; klock_info.kernel_flag = 0; return; @@ -177,8 +236,8 @@ void __unlock_kernel(struct task_struct *task) #endif /* DEBUG_LOCKS */ if ( atomic_dec_and_test((atomic_t *) &task->lock_depth) ) { - klock_info.akp = NO_PROC_ID; - klock_info.kernel_flag = 0; + klock_info.akp = NO_PROC_ID; + klock_info.kernel_flag = KLOCK_CLEAR; } } @@ -192,4 +251,3 @@ void reacquire_kernel_lock(struct task_struct *task, int cpu,int depth) __sti(); } } - diff --git a/arch/ppc/mbx_defconfig b/arch/ppc/mbx_defconfig new file mode 100644 index 000000000..e498b5249 --- /dev/null +++ b/arch/ppc/mbx_defconfig @@ -0,0 +1,196 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +# CONFIG_6xx is not set +CONFIG_8xx=y +# CONFIG_PMAC is not set +# CONFIG_PREP is not set +# CONFIG_CHRP is not set +# CONFIG_ALL_PPC is not set +CONFIG_MBX=y +CONFIG_MACH_SPECIFIC=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +# CONFIG_MODULES is not set +CONFIG_PCI=y +CONFIG_PCI_OLD_PROC=y +CONFIG_NET=y +# CONFIG_SYSCTL is not set +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set +# CONFIG_PMAC_CONSOLE is not set +# CONFIG_MAC_KEYBOARD is not set +# CONFIG_MAC_FLOPPY is not set +# CONFIG_PROC_DEVICETREE is not set +# CONFIG_XMON is not set +CONFIG_VGA_CONSOLE=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_IDE is not set +# CONFIG_BLK_DEV_HD_ONLY is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_HD is not set + +# +# NEW devices (io_request, all ALPHA and dangerous) +# +# CONFIG_IO_REQUEST is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_RARP is not set +CONFIG_IP_NOSR=y +# CONFIG_SKB_LARGE is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Network device support +# +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 is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_EISA is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set +# CONFIG_WAN_DRIVERS is not set +# CONFIG_LAPBETHER is not set +# CONFIG_X25_ASY is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO 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 +# CONFIG_CDROM is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_PROC_FS is not set +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set +# CONFIG_SMB_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_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 is not set +# CONFIG_NLS is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_MOUSE is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c index 8eaa49e92..9d0914979 100644 --- a/arch/ppc/mm/fault.c +++ b/arch/ppc/mm/fault.c @@ -35,11 +35,11 @@ #include <asm/system.h> #include <asm/uaccess.h> -#ifdef CONFIG_XMON -extern void xmon(struct pt_regs *); -extern void (*xmon_fault_handler)(struct pt_regs *); -extern int xmon_dabr_match(struct pt_regs *); -int xmon_kernel_faults = 0; +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) +extern void (*debugger)(struct pt_regs *); +extern void (*debugger_fault_handler)(struct pt_regs *); +extern int (*debugger_dabr_match)(struct pt_regs *); +int debugger_kernel_faults = 0; #endif unsigned long htab_reloads = 0; /* updated by head.S:hash_page() */ @@ -72,14 +72,14 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, (regs->trap == 0x400)?"instr":"data" );*/ -#ifdef CONFIG_XMON - if (xmon_fault_handler && regs->trap == 0x300) { - xmon_fault_handler(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler && regs->trap == 0x300) { + debugger_fault_handler(regs); return; } if (error_code & 0x00400000) { /* DABR match */ - if (xmon_dabr_match(regs)) + if (debugger_dabr_match(regs)) return; } #endif @@ -90,9 +90,9 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, printk("page fault in interrupt handler, addr=%lx\n", address); show_regs(regs); -#ifdef CONFIG_XMON - if (xmon_kernel_faults) - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_kernel_faults) + debugger(regs); #endif } } @@ -112,11 +112,22 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, goto bad_area; good_area: +#ifdef CONFIG_6xx if (error_code & 0x95700000) /* an error such as lwarx to I/O controller space, address matching DABR, eciwx, etc. */ +#endif /* CONFIG_6xx */ +#ifdef CONFIG_8xx + /* The MPC8xx seems to always set 0x80000000, which is + * "undefined". Of those that can be set, this is the only + * one which seems bad. + */ + if (error_code & 0x10000000) + /* Guarded storage error. */ +#endif /* CONFIG_8xx */ goto bad_area; + /* a write */ if (error_code & 0x02000000) { if (!(vma->vm_flags & VM_WRITE)) @@ -190,14 +201,47 @@ bad_page_fault(struct pt_regs *regs, unsigned long address) /* kernel has accessed a bad area */ show_regs(regs); print_backtrace( (unsigned long *)regs->gpr[1] ); -#ifdef CONFIG_XMON - if (xmon_kernel_faults) - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_kernel_faults) + debugger(regs); #endif panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d", regs->nip,regs->link,address,current->comm,current->pid); } +/* + * I need a va to pte function for the MPC8xx so I can set the cache + * attributes on individual pages used by the Communication Processor + * Module. + */ +pte_t *va_to_pte(struct task_struct *tsk, unsigned long address) +{ + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + + dir = pgd_offset(tsk->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)) + { + return(pte); + } + } else + { + return (0); + } + } else + { + return (0); + } + return (0); +} + unsigned long va_to_phys(unsigned long address) { pgd_t *dir; @@ -226,6 +270,57 @@ unsigned long va_to_phys(unsigned long address) return (0); } +void +print_8xx_pte(struct mm_struct *mm, unsigned long addr) +{ + pgd_t * pgd; + pmd_t * pmd; + pte_t * pte; + + printk(" pte @ 0x%8lx: ", addr); + pgd = pgd_offset(mm, addr & PAGE_MASK); + if (pgd) { + pmd = pmd_offset(pgd, addr & PAGE_MASK); + if (pmd && pmd_present(*pmd)) { + pte = pte_offset(pmd, addr & PAGE_MASK); + if (pte) { + printk(" (0x%08lx)->(0x%08lx)->0x%08lx\n", + (long)pgd, (long)pte, (long)pte_val(*pte)); + } + else { + printk("no pte\n"); + } + } + else { + printk("no pmd\n"); + } + } + else { + printk("no pgd\n"); + } +} + +int +get_8xx_pte(struct mm_struct *mm, unsigned long addr) +{ + pgd_t * pgd; + pmd_t * pmd; + pte_t * pte; + int retval = 0; + + pgd = pgd_offset(mm, addr & PAGE_MASK); + if (pgd) { + pmd = pmd_offset(pgd, addr & PAGE_MASK); + if (pmd && pmd_present(*pmd)) { + pte = pte_offset(pmd, addr & PAGE_MASK); + if (pte) { + retval = (int)pte_val(*pte); + } + } + } + return(retval); +} + #if 0 /* * Misc debugging functions. Please leave them here. -- Cort diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index 273076d15..5967e29e6 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -7,6 +7,7 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -31,6 +32,7 @@ #include <linux/mm.h> #include <linux/swap.h> #include <linux/stddef.h> +#include <linux/vmalloc.h> #include <asm/prom.h> #include <asm/io.h> #include <asm/mmu_context.h> @@ -40,12 +42,27 @@ #ifdef CONFIG_BLK_DEV_INITRD #include <linux/blk.h> /* for initrd_* */ #endif +#ifdef CONFIG_8xx +#include <asm/8xx_immap.h> +#endif +#ifdef CONFIG_MBX +#include <asm/mbx.h> +#endif -int prom_trashed; -int next_mmu_context; +#ifndef CONFIG_8xx unsigned long _SDR1; PTE *Hash, *Hash_end; unsigned long Hash_size, Hash_mask; +#endif /* CONFIG_8xx */ + +/* ifdef APUS specific stuff until the merge is completed. -jskov */ +#ifdef CONFIG_APUS +#include <asm/setup.h> +#include <asm/amigahw.h> +#endif + +int prom_trashed; +int next_mmu_context; unsigned long *end_of_DRAM; int mem_init_done; extern pgd_t swapper_pg_dir[]; @@ -55,10 +72,16 @@ extern char __init_begin, __init_end; extern RESIDUAL res; char *klimit = _end; struct device_node *memory_node; +unsigned long ioremap_base; +unsigned long ioremap_bot; +#ifndef __SMP__ +struct pgtable_cache_struct quicklists; +#endif -void *find_mem_piece(unsigned, unsigned); -static void mapin_ram(void); +#ifndef CONFIG_8xx static void hash_init(void); +#endif /* CONFIG_8xx */ +static void mapin_ram(void); static void *MMU_get_page(void); void map_page(struct task_struct *, unsigned long va, unsigned long pa, int flags); @@ -68,6 +91,38 @@ extern unsigned long *find_end_of_memory(void); extern struct task_struct *current_set[NR_CPUS]; +#ifdef CONFIG_MBX +/* This is a big hack that may not yet work correctly. + * The MBX8xx boards have a single DIMM socket for additional memory. + * Although it appears you can set magical locations in the serial + * EEPROM to get EPPC-Bug to configure this memory, there are no tools + * (i.e. commands) to make this easy. If you screw up, you will most + * likely end up with a board that will not boot until you find a + * way to program the EEPROM correctly. I decided to simply program + * the memory controller here to add the additional memory. + * The reason this may not work correctly is that depending upon the + * on-board and DIMM memory size, there may be holes in the physical + * address space. This is the case for me, I have a 4 MB local memory + * and a 32 MB DIMM. + * The DIMM is 64 bits wide, and we see it as two banks of 32 bit + * memory. The holes are caused by the requirement to map the + * memory on a natural alignment, that is a 16 MB bank must begin on + * a 16 MB boundary. The DIMM_SIZE below represents the size of the + * bank, which is the total size divided by two. + * Although I may not have all of this working, the intention is to + * mark all of the page maps in the "hole" as reserved, and adjust + * num_physpages accordingly. In the current implementation, this + * seems to work, but there are some assumptions about contiguous + * memory. The correct solution is to modify the memory allocators + * to know about holes, but that will have to wait for another day. + * + * define DIMM_8xx to enable this feature. + * define DIMM_SIZE to reflect the bank size (DIMM size divided by two). + */ +/*#define DIMM_8xx 1 */ +#define DIMM_SIZE (16 * 1024 * 1024) +#endif /* CONFIG_MBX */ + /* * this tells the system to map all of ram with the segregs * (i.e. page tables) instead of the bats. @@ -77,6 +132,34 @@ extern struct task_struct *current_set[NR_CPUS]; /* optimization for 603 to load the tlb directly from the linux table */ #define NO_RELOAD_HTAB 1 /* change in kernel/head.S too! */ +void __bad_pte(pmd_t *pmd) +{ + printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); + pmd_val(*pmd) = (unsigned long) BAD_PAGETABLE; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + clear_page((unsigned long)pte); + pmd_val(*pmd) = (unsigned long)pte; + return pte + offset; + } + pmd_val(*pmd) = (unsigned long)BAD_PAGETABLE; + return NULL; + } + free_page((unsigned long)pte); + if (pmd_bad(*pmd)) { + __bad_pte(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a @@ -121,65 +204,32 @@ 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 sort_mem_pieces(struct mem_pieces *); -static void coalesce_mem_pieces(struct mem_pieces *); -static void append_mem_piece(struct mem_pieces *, unsigned, unsigned); static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); +void *find_mem_piece(unsigned, unsigned); static void print_mem_pieces(struct mem_pieces *); -static void -sort_mem_pieces(struct mem_pieces *mp) -{ - unsigned long a, s; - int i, j; - - for (i = 1; i < mp->n_regions; ++i) { - 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; - } -} - -static void -coalesce_mem_pieces(struct mem_pieces *mp) +/* + * Scan a region for a piece of a given size with the required alignment. + */ +void * +find_mem_piece(unsigned size, unsigned align) { - unsigned long a, e; - int i, j, d; + int i; + unsigned a, e; + struct mem_pieces *mp = &phys_avail; - d = 0; - for (i = 0; i < mp->n_regions; i = j) { + for (i = 0; i < mp->n_regions; ++i) { a = mp->regions[i].address; e = a + mp->regions[i].size; - for (j = i + 1; j < mp->n_regions - && mp->regions[j].address <= e; ++j) - e = mp->regions[j].address + mp->regions[j].size; - mp->regions[d].address = a; - mp->regions[d].size = e - a; - ++d; + a = (a + align - 1) & -align; + if (a + size <= e) { + remove_mem_piece(mp, a, size, 1); + return __va(a); + } } - mp->n_regions = d; -} - -/* - * Add some memory to an array of pieces - */ -static void -append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size) -{ - struct reg_property *rp; - - if (mp->n_regions >= MAX_MEM_REGIONS) - return; - rp = &mp->regions[mp->n_regions++]; - rp->address = start; - rp->size = size; + printk("Couldn't find %u bytes at %u alignment\n", size, align); + abort(); + return NULL; } /* @@ -252,33 +302,73 @@ print_mem_pieces(struct mem_pieces *mp) printk("\n"); } -/* - * Scan a region for a piece of a given size with the required alignment. - */ -void * -find_mem_piece(unsigned size, unsigned align) + + +#ifndef CONFIG_8xx +static void hash_init(void); +static void get_mem_prop(char *, struct mem_pieces *); +static void sort_mem_pieces(struct mem_pieces *); +static void coalesce_mem_pieces(struct mem_pieces *); +static void append_mem_piece(struct mem_pieces *, unsigned, unsigned); + +static void +sort_mem_pieces(struct mem_pieces *mp) { - int i; - unsigned a, e; - struct mem_pieces *mp = &phys_avail; + unsigned long a, s; + int i, j; - for (i = 0; i < mp->n_regions; ++i) { + for (i = 1; 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); + 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; } - printk("Couldn't find %u bytes at %u alignment\n", size, align); - abort(); - return NULL; +} + +static void +coalesce_mem_pieces(struct mem_pieces *mp) +{ + unsigned long a, e; + int i, j, d; + + d = 0; + for (i = 0; i < mp->n_regions; i = j) { + a = mp->regions[i].address; + e = a + mp->regions[i].size; + for (j = i + 1; j < mp->n_regions + && mp->regions[j].address <= e; ++j) + e = mp->regions[j].address + mp->regions[j].size; + mp->regions[d].address = a; + mp->regions[d].size = e - a; + ++d; + } + mp->n_regions = d; +} + +/* + * Add some memory to an array of pieces + */ +static void +append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size) +{ + struct reg_property *rp; + + if (mp->n_regions >= MAX_MEM_REGIONS) + return; + rp = &mp->regions[mp->n_regions++]; + rp->address = start; + rp->size = size; } /* * Read in a property describing some pieces of memory. */ + static void get_mem_prop(char *name, struct mem_pieces *mp) { @@ -365,6 +455,41 @@ unsigned long *pmac_find_end_of_memory(void) return __va(total); } +#endif /* CONFIG_8xx */ + +#ifdef CONFIG_APUS +#define HARDWARE_MAPPED_SIZE (512*1024) +unsigned long *apus_find_end_of_memory(void) +{ + unsigned long kstart, ksize; + + /* Add the chunk that ADOS does not see. Removed again below. */ + m68k_memory[0].size += HARDWARE_MAPPED_SIZE; + + append_mem_piece(&phys_mem, m68k_memory[0].addr, m68k_memory[0].size); + + phys_avail = phys_mem; + kstart = __pa(_stext); + ksize = PAGE_ALIGN(klimit - _stext); + remove_mem_piece(&phys_avail, kstart, ksize, 1); + + /* Remove the upper HARDWARE_MAPPED_SIZE bytes where the address + * range 0xfff00000-0xfffx0000 is mapped to. + * We do it this way to ensure that the memory registered in the + * system has a power-of-two size. + */ + remove_mem_piece(&phys_avail, + (m68k_memory[0].addr + m68k_memory[0].size + - HARDWARE_MAPPED_SIZE), + HARDWARE_MAPPED_SIZE, 1); + + /* FIXME:APUS: Only handles one block of memory! Problem is + * that the VTOP/PTOV code in head.S would be a mess if it had + * to handle more than one block. + */ + return __va(m68k_memory[0].addr + m68k_memory[0].size); +} +#endif /* * Find some memory for setup_arch to return. @@ -381,6 +506,16 @@ unsigned long find_available_memory(void) unsigned long start, end; free = 0; + if (_machine == _MACH_mbx) { + /* Return the first, not the last region, because we + * may not yet have properly initialized the additonal + * memory DIMM. + */ + a = PAGE_ALIGN(phys_avail.regions[0].address); + avail_start = (unsigned long) __va(a); + return avail_start; + } + for (i = 0; i < phys_avail.n_regions - 1; ++i) { start = phys_avail.regions[i].address; end = start + phys_avail.regions[i].size; @@ -396,7 +531,7 @@ unsigned long find_available_memory(void) void show_mem(void) { int i,free = 0,total = 0,reserved = 0; - int shared = 0; + int shared = 0, cached = 0; struct task_struct *p; printk("Mem-info:\n"); @@ -407,6 +542,8 @@ void show_mem(void) total++; if (PageReserved(mem_map+i)) reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; else if (!atomic_read(&mem_map[i].count)) free++; else @@ -416,6 +553,8 @@ void show_mem(void) printk("%d free pages\n",free); printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%d pages in page table cache\n",(int)pgtable_cache_size); show_buffers(); #ifdef CONFIG_NET show_net_buffers(); @@ -487,6 +626,7 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) int codepages = 0; int datapages = 0; int initpages = 0; + extern unsigned int rtas_data, rtas_size; end_mem &= PAGE_MASK; high_memory = (void *) end_mem; @@ -496,6 +636,7 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) /* mark usable pages in the mem_map[] */ start_mem = PAGE_ALIGN(start_mem); +#ifndef CONFIG_8xx remove_mem_piece(&phys_avail, __pa(avail_start), start_mem - avail_start, 1); @@ -520,7 +661,52 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); } prom_trashed = 1; - +#else /* CONFIG_8xx */ + /* When we get here, all of the page maps have been set up and + * Linux thinks we have contiguous memory. Since the MBX can + * have memory holes, we need to compensate for that here. + * The memory holes are currently pages marked reserved (all + * pages right now are marked reserved). + * All of the memory allocated by the kernel up to this point + * had to come from region 0. + */ + + /* First, unreserve all memory from the page following start_mem + * to the end of region 0. + */ + for (addr = start_mem + PAGE_SIZE ; + addr < (ulong) __va(phys_mem.regions[0].size); + addr += PAGE_SIZE) { + clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + } + + /* Now add any additional regions to the system. + */ + for (i = 1; 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); + } + phys_avail.n_regions = 0; /* Nothing available, kernel owns */ + /* Count up the size of the holes. We look for the space + * between the end of one region and the start of the next. + */ + lim = 0; + for (i = 0; i < phys_mem.n_regions-1; ++i) { + a = (unsigned long) phys_mem.regions[i].address; + a += phys_mem.regions[i].size; + lim += phys_mem.regions[i+1].address - a; + } + + /* It appears that num_physpages is only used for quota checking, + * when pages are locked down. We subtract the size of the holes + * from it now. + */ + num_physpages -= lim/PAGE_SIZE; +#endif /* CONFIG_8xx */ + for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { if (PageReserved(mem_map + MAP_NR(addr))) { if (addr < (ulong) etext) @@ -537,7 +723,12 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) if (!initrd_start || addr < (initrd_start & PAGE_MASK) || addr >= initrd_end) #endif /* CONFIG_BLK_DEV_INITRD */ - free_page(addr); +#ifndef CONFIG_8xx + if ( !rtas_data || + addr < (rtas_data & PAGE_MASK) || + addr >= (rtas_data+rtas_size)) +#endif /* CONFIG_8xx */ + free_page(addr); } printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n", @@ -594,6 +785,7 @@ void si_meminfo(struct sysinfo *val) return; } +#ifndef CONFIG_8xx union ubat { /* BAT register values to be loaded */ BAT bat; P601_BAT bat_601; @@ -692,7 +884,7 @@ unsigned long *prep_find_end_of_memory(void) return (__va(total)); } - +#endif /* CONFIG_8xx */ /* * Map in all of physical memory starting at KERNELBASE. @@ -702,8 +894,9 @@ unsigned long *prep_find_end_of_memory(void) static void mapin_ram() { int i; - unsigned long tot, bl, done; unsigned long v, p, s, f; +#ifndef CONFIG_8xx + unsigned long tot, mem_base, bl, done; #ifndef MAP_RAM_WITH_SEGREGS /* Set up BAT2 and if necessary BAT3 to cover RAM. */ @@ -711,15 +904,17 @@ static void mapin_ram() for (bl = 128<<10; bl < 256<<20; bl <<= 1) if (bl * 2 > tot) break; - setbat(2, KERNELBASE, 0, bl, RAM_PAGE); - done = __pa(bat_addrs[2].limit) + 1; + + mem_base = __pa(KERNELBASE); + setbat(2, KERNELBASE, mem_base, bl, RAM_PAGE); + done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1; if (done < tot) { /* use BAT3 to cover a bit more */ tot -= done; for (bl = 128<<10; bl < 256<<20; bl <<= 1) if (bl * 2 > tot) break; - setbat(3, KERNELBASE+done, done, bl, RAM_PAGE); + setbat(3, KERNELBASE+done, mem_base+done, bl, RAM_PAGE); } #endif @@ -734,6 +929,27 @@ static void mapin_ram() /* On the powerpc, no user access forces R/W kernel access */ f |= _PAGE_USER; +#else /* CONFIG_8xx */ + for (i = 0; i < phys_mem.n_regions; ++i) { + v = (ulong)__va(phys_mem.regions[i].address); + p = phys_mem.regions[i].address; + for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { + /* On the MPC8xx, we want the page shared so we + * don't get ASID compares on kernel space. + */ + f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED; + + /* I don't really need the rest of this code, but + * I grabbed it because I think the line: + * f |= _PAGE_USER + * is incorrect. It needs to be set to bits we + * don't define to cause a kernel read-only. On + * the MPC8xx, the PAGE_DIRTY takes care of that + * for us (along with the RW software state). + */ + if ((char *) v < _stext || (char *) v >= etext) + f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; +#endif /* CONFIG_8xx */ map_page(&init_task, v, p, f); v += PAGE_SIZE; p += PAGE_SIZE; @@ -741,6 +957,7 @@ static void mapin_ram() } } +#ifndef CONFIG_8xx /* * Initialize the hash table and patch the instructions in head.S. */ @@ -820,7 +1037,7 @@ static void hash_init(void) Hash_end = 0; } - +#endif /* CONFIG_8xx */ /* * Do very early mm setup such as finding the size of memory @@ -832,13 +1049,19 @@ static void hash_init(void) void MMU_init(void) { +#ifndef CONFIG_8xx if (have_of) end_of_DRAM = pmac_find_end_of_memory(); +#ifdef CONFIG_APUS + else if (_machine == _MACH_apus ) + end_of_DRAM = apus_find_end_of_memory(); +#endif else /* prep */ end_of_DRAM = prep_find_end_of_memory(); hash_init(); _SDR1 = __pa(Hash) | (Hash_mask >> 10); + ioremap_base = 0xf8000000; /* Map in all of RAM starting at KERNELBASE */ mapin_ram(); @@ -856,16 +1079,40 @@ MMU_init(void) IO_PAGE + ((_prep_type == _PREP_IBM)? _PAGE_USER: 0)); break; case _MACH_chrp: - setbat(0, 0xc0000000, 0xc0000000, 0x10000000, IO_PAGE); - setbat(1, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE); - setbat(3, 0x80000000, 0x80000000, 0x10000000, IO_PAGE); + setbat(0, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE); break; case _MACH_Pmac: setbat(0, 0xf3000000, 0xf3000000, 0x100000, IO_PAGE); - /* this is used to cover registers used by smp boards -- Cort */ - setbat(3, 0xf8000000, 0xf8000000, 0x100000, IO_PAGE); + ioremap_base = 0xf0000000; break; +#ifdef CONFIG_APUS + case _MACH_apus: + /* Map Cyberstorm PPC registers. */ + /* FIXME:APUS: Performance penalty here. Restrict it + * to the Cyberstorm registers. + */ + setbat(0, 0xfff00000, 0xfff00000, 0x00080000, IO_PAGE); + /* Map chip and ZorroII memory */ + setbat(1, zTwoBase, 0x00000000, 0x01000000, IO_PAGE); + break; +#endif } + ioremap_bot = ioremap_base; +#else /* CONFIG_8xx */ + + /* Map in all of RAM starting at KERNELBASE */ + mapin_ram(); + + /* Now map in some of the I/O space that is generically needed + * or shared with multiple devices. + * All of this fits into the same 4Mbyte region, so it only + * requires one page table page. + */ + ioremap(NVRAM_ADDR, NVRAM_SIZE); + ioremap(MBX_CSR_ADDR, MBX_CSR_SIZE); + ioremap(MBX_IMAP_ADDR, MBX_IMAP_SIZE); + ioremap(PCI_CSR_ADDR, PCI_CSR_SIZE); +#endif /* CONFIG_8xx */ } static void * @@ -887,27 +1134,98 @@ MMU_get_page() void * ioremap(unsigned long addr, unsigned long size) { - unsigned long p, end = addr + size; + return __ioremap(addr, size, _PAGE_NO_CACHE); +} + +void * +__ioremap(unsigned long addr, unsigned long size, unsigned long flags) +{ + unsigned long p, v, i; + + /* + * Choose an address to map it to. + * Once the vmalloc system is running, we use it. + * Before then, we map addresses >= ioremap_base + * virt == phys; for addresses below this we use + * space going down from ioremap_base (ioremap_bot + * records where we're up to). + * + * We should also look out for a frame buffer and + * map it with a free BAT register, if there is one. + */ + p = addr & PAGE_MASK; + size = PAGE_ALIGN(addr + size) - p; + if (size == 0) + return NULL; - for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE) - map_page(&init_task, p, p, - pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); - return (void *) addr; + if (mem_init_done) { + struct vm_struct *area; + area = get_vm_area(size); + if (area == 0) + return NULL; + v = VMALLOC_VMADDR(area->addr); + } else { + if (p >= ioremap_base) + v = p; + else + v = (ioremap_bot -= size); + } + + flags |= pgprot_val(PAGE_KERNEL); + if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU)) + flags |= _PAGE_GUARDED; + for (i = 0; i < size; i += PAGE_SIZE) + map_page(&init_task, v+i, p+i, flags); + + return (void *) (v + (addr & ~PAGE_MASK)); } -void iounmap(unsigned long *addr) +void iounmap(void *addr) { /* XXX todo */ } +unsigned long iopa(unsigned long addr) +{ + unsigned long idx; + pmd_t *pd; + pte_t *pg; +#ifndef CONFIG_8xx + int b; +#endif + idx = addr & ~PAGE_MASK; + addr = addr & PAGE_MASK; + +#ifndef CONFIG_8xx + /* Check the BATs */ + for (b = 0; b < 4; ++b) + if (addr >= bat_addrs[b].start && addr <= bat_addrs[b].limit) + return bat_addrs[b].phys | idx; +#endif /* CONFIG_8xx */ + /* Do we have a page table? */ + if (init_task.mm->pgd == NULL) + return 0; + + /* Use upper 10 bits of addr to index the first level map */ + pd = (pmd_t *) (init_task.mm->pgd + (addr >> PGDIR_SHIFT)); + if (pmd_none(*pd)) + return 0; + + /* Use middle 10 bits of addr to index the second-level map */ + pg = pte_offset(pd, addr); + return (pte_val(*pg) & PAGE_MASK) | idx; +} + void map_page(struct task_struct *tsk, unsigned long va, unsigned long pa, int flags) { pmd_t *pd; pte_t *pg; +#ifndef CONFIG_8xx int b; - +#endif + if (tsk->mm->pgd == NULL) { /* Allocate upper level page map */ tsk->mm->pgd = (pgd_t *) MMU_get_page(); @@ -915,6 +1233,7 @@ map_page(struct task_struct *tsk, unsigned long va, /* Use upper 10 bits of VA to index the first level map */ pd = (pmd_t *) (tsk->mm->pgd + (va >> PGDIR_SHIFT)); if (pmd_none(*pd)) { +#ifndef CONFIG_8xx /* * Need to allocate second-level table, but first * check whether this address is already mapped by @@ -927,14 +1246,16 @@ map_page(struct task_struct *tsk, unsigned long va, return; } } +#endif /* CONFIG_8xx */ 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);*/ +#ifndef CONFIG_8xx flush_hash_page(0, va); +#endif } /* @@ -957,11 +1278,14 @@ map_page(struct task_struct *tsk, unsigned long va, void local_flush_tlb_all(void) { +#ifndef CONFIG_8xx memset(Hash, 0, Hash_size); _tlbia(); +#else + asm volatile ("tlbia" : : ); +#endif } - /* * Flush all the (user) entries for the address space described * by mm. We can't rely on mm->mmap describing all the entries @@ -970,33 +1294,43 @@ local_flush_tlb_all(void) void local_flush_tlb_mm(struct mm_struct *mm) { +#ifndef CONFIG_8xx mm->context = NO_CONTEXT; if (mm == current->mm) { get_mmu_context(current); /* done by get_mmu_context() now -- Cort */ /*set_context(current->mm->context);*/ } +#else + asm volatile ("tlbia" : : ); +#endif } void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) { +#ifndef CONFIG_8xx if (vmaddr < TASK_SIZE) flush_hash_page(vma->vm_mm->context, vmaddr); else flush_hash_page(0, vmaddr); +#else + asm volatile ("tlbia" : : ); +#endif } -/* 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 - */ +/* + * 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 + */ void local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { +#ifndef CONFIG_8xx start &= PAGE_MASK; if (end - start > 20 * PAGE_SIZE) @@ -1009,6 +1343,9 @@ local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long e { flush_hash_page(mm->context, start); } +#else + asm volatile ("tlbia" : : ); +#endif } /* @@ -1020,6 +1357,7 @@ local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long e void mmu_context_overflow(void) { +#ifndef CONFIG_8xx struct task_struct *tsk; printk(KERN_DEBUG "mmu_context_overflow\n"); @@ -1034,6 +1372,12 @@ mmu_context_overflow(void) /* make sure current always has a context */ current->mm->context = MUNGE_CONTEXT(++next_mmu_context); set_context(current->mm->context); +#else + /* We set the value to -1 because it is pre-incremented before + * before use. + */ + next_mmu_context = -1; +#endif } #if 0 @@ -1090,3 +1434,69 @@ void local_flush_cache_range(struct mm_struct *mm, unsigned long start, } #endif +#ifdef CONFIG_MBX +/* + * This is a big hack right now, but it may turn into something real + * someday. + * + * For the MBX860 (at this time anyway), there is nothing to initialize + * associated the the PROM. Rather than include all of the prom.c + * functions in the image just to get prom_init, all we really need right + * now is the initialization of the physical memory region. + */ +void +set_mbx_memory(void) +{ + unsigned long kstart, ksize; + bd_t *binfo; +#ifdef DIMM_8xx + volatile memctl8xx_t *mcp; +#endif + + binfo = (bd_t *)&res; + + /* The MBX can have up to three memory regions, the on-board + * DRAM plus two more banks of DIMM socket memory. The DIMM is + * 64 bits, seen from the processor as two 32 bit banks. + * The on-board DRAM is reflected in the board information + * structure, and is either 4 Mbytes or 16 Mbytes. + * I think there is a way to program the serial EEPROM information + * so EPPC-Bug will initialize this memory, but I have not + * done that and it may not be a wise thing to do. If you + * remove the DIMM without reprogramming the EEPROM, bad things + * could happen since EPPC-Bug tries to use the upper 128K of + * memory. + */ + phys_mem.n_regions = 1; + phys_mem.regions[0].address = 0; + phys_mem.regions[0].size = binfo->bi_memsize; + end_of_DRAM = __va(binfo->bi_memsize); + +#ifdef DIMM_8xx + /* This is a big hack. It assumes my 32 Mbyte DIMM in a 40 MHz + * MPC860. Don't do this (or change this) if you are running + * something else. + */ + mcp = (memctl8xx_t *)(&(((immap_t *)MBX_IMAP_ADDR)->im_memctl)); + + mcp->memc_or2 = (~(DIMM_SIZE-1) | 0x00000400); + mcp->memc_br2 = DIMM_SIZE | 0x00000081; + mcp->memc_or3 = (~((2*DIMM_SIZE)-1) | 0x00000400); + mcp->memc_br3 = 2*DIMM_SIZE | 0x00000081; + + + phys_mem.regions[phys_mem.n_regions].address = DIMM_SIZE; + phys_mem.regions[phys_mem.n_regions++].size = DIMM_SIZE; + phys_mem.regions[phys_mem.n_regions].address = 2 * DIMM_SIZE; + phys_mem.regions[phys_mem.n_regions++].size = DIMM_SIZE; + + end_of_DRAM = __va(3 * DIMM_SIZE); +#endif + + phys_avail = phys_mem; + + kstart = __pa(_stext); /* should be 0 */ + ksize = PAGE_ALIGN(_end - _stext); + remove_mem_piece(&phys_avail, kstart, ksize, 0); +} +#endif diff --git a/arch/ppc/pmac_defconfig b/arch/ppc/pmac_defconfig index 529b33138..8cf7eb8a6 100644 --- a/arch/ppc/pmac_defconfig +++ b/arch/ppc/pmac_defconfig @@ -1,5 +1,5 @@ # -# Automatically generated by make menuconfig: don't edit +# Automatically generated make config: don't edit # # @@ -7,11 +7,12 @@ # CONFIG_PPC=y CONFIG_NATIVE=y +CONFIG_PPC6XX=y +# CONFIG_PPC8XX is not set CONFIG_MACH_SPECIFIC=y CONFIG_PMAC=y # CONFIG_PREP is not set # CONFIG_CHRP is not set -CONFIG_COMMON=y # # General setup @@ -19,8 +20,9 @@ CONFIG_COMMON=y CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y # CONFIG_MODVERSIONS is not set -CONFIG_KMOD=y +CONFIG_KERNELD=y CONFIG_PCI=y +CONFIG_PCI_OLD_PROC=y CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y @@ -28,14 +30,20 @@ CONFIG_SYSVIPC=y CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y CONFIG_BINFMT_MISC=m -CONFIG_BINFMT_JAVA=m +# CONFIG_BINFMT_JAVA is not set +# CONFIG_ABSTRACT_CONSOLE is not set CONFIG_PMAC_CONSOLE=y CONFIG_MAC_KEYBOARD=y CONFIG_MAC_FLOPPY=y +CONFIG_MACMOUSE=y CONFIG_PROC_DEVICETREE=y # CONFIG_XMON is not set +CONFIG_CONTROL_VIDEO=y +CONFIG_PLATINUM_VIDEO=y +CONFIG_VALKYRIE_VIDEO=y CONFIG_ATY_VIDEO=y CONFIG_IMSTT_VIDEO=y +CONFIG_CHIPS_VIDEO=y # # Plug and Play support @@ -47,6 +55,10 @@ CONFIG_IMSTT_VIDEO=y # # CONFIG_BLK_DEV_FD is not set CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y @@ -55,14 +67,20 @@ CONFIG_BLK_DEV_IDECD=y # 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_BLK_DEV_IDEPCI is not set # CONFIG_IDE_CHIPSETS is not set + +# +# Additional Block Devices +# CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set -# CONFIG_BLK_DEV_EZ is not set +CONFIG_PARIDE_PARPORT=m +# CONFIG_PARIDE is not set # CONFIG_BLK_DEV_HD is not set # @@ -77,6 +95,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set CONFIG_NET_ALIAS=y +# CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y CONFIG_IP_MULTICAST=y @@ -85,37 +104,56 @@ CONFIG_IP_MULTICAST=y # CONFIG_IP_ACCT is not set # CONFIG_IP_MASQUERADE is not set # CONFIG_IP_ROUTER is not set -CONFIG_NET_IPIP=m +# CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set CONFIG_IP_ALIAS=y # CONFIG_SYN_COOKIES is not set + +# +# (it is safe to leave these untouched) +# CONFIG_INET_RARP=y CONFIG_IP_NOSR=y 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_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set # CONFIG_NET_SCHED is not set +# CONFIG_NET_PROFILE is not set # # SCSI support # CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# 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=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers @@ -124,7 +162,12 @@ CONFIG_SCSI_CONSTANTS=y # 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_AIC7XXX=m +# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set +# CONFIG_OVERRIDE_CMDS is not set +# CONFIG_AIC7XXX_PAGE_ENABLE is not set +CONFIG_AIC7XXX_PROC_STATS=y +CONFIG_AIC7XXX_RESET_DELAY=15 # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set @@ -134,12 +177,16 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set # 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_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_SEAGATE is not set @@ -158,27 +205,47 @@ CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set -# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y CONFIG_MACE=y -CONFIG_DEC_ELCP=m # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set -# CONFIG_NET_EISA is not set +CONFIG_NET_EISA=y +# CONFIG_PCNET32 is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_DE4X5=m +CONFIG_DEC_ELCP=m +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_TLAN 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=m -# CONFIG_NET_RADIO is not set + +# +# CCP compressors for PPP are only built as modules. +# # CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_SHAPER is not set # +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# # ISDN subsystem # # CONFIG_ISDN is not set @@ -187,28 +254,68 @@ CONFIG_PPP=m # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set +CONFIG_CDROM=y # # Filesystems # # CONFIG_QUOTA is not set -CONFIG_MINIX_FS=m +# CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y CONFIG_ISO9660_FS=y -# CONFIG_NLS is not set +# CONFIG_JOLIET is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=m CONFIG_PROC_FS=y CONFIG_NFS_FS=y -# CONFIG_NFSD is not set +CONFIG_NFSD=y CONFIG_SUNRPC=y CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=m # CONFIG_ROMFS_FS is not set CONFIG_AUTOFS_FS=y # CONFIG_UFS_FS is not set +# CONFIG_ADFS_FS is not set CONFIG_MAC_PARTITION=y +CONFIG_NLS=y + +# +# Native Language Support +# +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set # # Character devices @@ -217,20 +324,25 @@ CONFIG_VT=y CONFIG_VT_CONSOLE=y # CONFIG_SOFTCURSOR is not set CONFIG_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set # CONFIG_PRINTER is not set # CONFIG_MOUSE is not set # CONFIG_UMISC is not set # CONFIG_QIC02_TAPE is not set -# CONFIG_FTAPE is not set # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set # CONFIG_VIDEO_DEV is not set -# CONFIG_VIDEO_BT848 is not set CONFIG_NVRAM=y # CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set # # Sound diff --git a/arch/ppc/prep_defconfig b/arch/ppc/prep_defconfig index 147fa3005..a323e652e 100644 --- a/arch/ppc/prep_defconfig +++ b/arch/ppc/prep_defconfig @@ -6,12 +6,14 @@ # Platform support # CONFIG_PPC=y -CONFIG_NATIVE=y -CONFIG_MACH_SPECIFIC=y +CONFIG_6xx=y +# CONFIG_8xx is not set # CONFIG_PMAC is not set CONFIG_PREP=y # CONFIG_CHRP is not set -CONFIG_COMMON=y +# CONFIG_ALL_PPC is not set +# CONFIG_MBX is not set +CONFIG_MACH_SPECIFIC=y # # General setup @@ -19,20 +21,25 @@ CONFIG_COMMON=y CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y CONFIG_MODVERSIONS=y -CONFIG_KMOD=y +CONFIG_KERNELD=y CONFIG_PCI=y +# CONFIG_PCI_QUIRKS is not set # CONFIG_PCI_OPTIMIZE is not set +CONFIG_PCI_OLD_PROC=y CONFIG_NET=y -# CONFIG_SYSCTL is not set +CONFIG_SYSCTL=y CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set +# CONFIG_ABSTRACT_CONSOLE is not set # CONFIG_PMAC_CONSOLE is not set # CONFIG_MAC_KEYBOARD is not set # CONFIG_MAC_FLOPPY is not set +# CONFIG_MACMOUSE is not set # CONFIG_PROC_DEVICETREE is not set # CONFIG_XMON is not set CONFIG_VGA_CONSOLE=y @@ -55,14 +62,16 @@ CONFIG_BLK_DEV_IDECD=y # 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_BLK_DEV_IDEPCI is not set # CONFIG_IDE_CHIPSETS is not set -# CONFIG_BLK_DEV_LOOP is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set -# CONFIG_BLK_DEV_EZ is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set # CONFIG_BLK_DEV_HD is not set # @@ -77,32 +86,34 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set # CONFIG_IP_PNP is not set -# CONFIG_IP_ACCT is not set -# CONFIG_IP_MASQUERADE is not set +CONFIG_IP_ACCT=y # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_ALIAS is not set -# CONFIG_SYN_COOKIES is not set +CONFIG_SYN_COOKIES=y CONFIG_INET_RARP=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_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set # CONFIG_NET_SCHED is not set +# CONFIG_NET_PROFILE is not set # # SCSI support @@ -115,6 +126,7 @@ CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_CHR_DEV_SG is not set # CONFIG_SCSI_MULTI_LUN is not set # CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers @@ -133,6 +145,7 @@ CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set @@ -143,8 +156,10 @@ CONFIG_SCSI_NCR53C8XX_IOMAPPED=y CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 CONFIG_SCSI_NCR53C8XX_SYNC=5 # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set -# CONFIG_SCSI_PPA is not set # CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_SEAGATE is not set @@ -152,6 +167,7 @@ CONFIG_SCSI_NCR53C8XX_SYNC=5 # CONFIG_SCSI_T128 is not set # CONFIG_SCSI_U14_34F is not set # CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_DEBUG is not set # CONFIG_SCSI_MESH is not set # CONFIG_SCSI_MAC53C94 is not set @@ -162,12 +178,13 @@ CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set -# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y # CONFIG_NET_VENDOR_3COM is not set CONFIG_LANCE=y # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y CONFIG_PCNET32=y @@ -184,14 +201,18 @@ CONFIG_DE4X5=y # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set -# CONFIG_PLIP is not set -CONFIG_PPP=m -# CONFIG_NET_RADIO is not set +CONFIG_PPP=y # CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_SHAPER is not set # +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# # ISDN subsystem # # CONFIG_ISDN is not set @@ -200,6 +221,7 @@ CONFIG_PPP=m # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set +CONFIG_CDROM=y # # Filesystems @@ -208,29 +230,59 @@ CONFIG_PPP=m # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y CONFIG_ISO9660_FS=y -# CONFIG_JOLIET is not set -# CONFIG_FAT_FS is not set -# CONFIG_MSDOS_FS is not set +CONFIG_JOLIET=y +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y # CONFIG_UMSDOS_FS is not set -# CONFIG_VFAT_FS is not set +CONFIG_VFAT_FS=y CONFIG_PROC_FS=y CONFIG_NFS_FS=y -# CONFIG_NFSD is not set +CONFIG_NFSD=y CONFIG_SUNRPC=y CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_ADFS_FS is not set # CONFIG_MAC_PARTITION is not set +CONFIG_NLS=y # # Native Language Support # -# CONFIG_NLS is not set +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set # # Character devices @@ -239,14 +291,9 @@ CONFIG_VT=y CONFIG_VT_CONSOLE=y # CONFIG_SOFTCURSOR is not set 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=y +# CONFIG_SERIAL_EXTENDED 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 @@ -256,14 +303,18 @@ CONFIG_PSMOUSE=y # CONFIG_PC110_PAD is not set # CONFIG_UMISC is not set # CONFIG_QIC02_TAPE is not set -# CONFIG_FTAPE is not set # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set # CONFIG_VIDEO_DEV is not set -# CONFIG_VIDEO_BT848 is not set # CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set # # Sound diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile index 7a8d46a07..a4870e117 100644 --- a/arch/sparc/Makefile +++ b/arch/sparc/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.29 1997/07/11 11:05:23 jj Exp $ +# $Id: Makefile,v 1.34 1998/04/06 16:09:34 jj Exp $ # sparc/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -23,14 +23,18 @@ LINKFLAGS = -T arch/sparc/vmlinux.lds HEAD := arch/sparc/kernel/head.o arch/sparc/kernel/init_task.o -SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm \ - arch/sparc/prom +# Note arch/sparc/mm has to be the last subdir +SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/prom \ + arch/sparc/mm CORE_FILES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(CORE_FILES) LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/prom/promlib.a \ $(TOPDIR)/arch/sparc/lib/lib.a +SUBDIRS += arch/sparc/math-emu +CORE_FILES += arch/sparc/math-emu/math-emu.o + ifdef CONFIG_AP1000 SUBDIRS := $(SUBDIRS) arch/sparc/ap1000 mpp CORE_FILES := $(TOPDIR)/arch/sparc/ap1000/ap1000lib.o \ @@ -40,11 +44,30 @@ CFLAGS := $(CFLAGS) -D__MPP__=1 endif archclean: + -$(MAKE) -C arch/sparc/boot archclean + -$(MAKE) -C arch/sparc/math-emu cleansymlinks archdep: + -$(MAKE) -C arch/sparc/math-emu symlinks check_asm: $(MAKE) -C arch/sparc/kernel check_asm tftpboot.img: $(MAKE) -C arch/sparc/boot tftpboot.img + +vmlinux.o: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs + $(LD) -r $(VMLINUX.OBJS) -o vmlinux.o + +arch/sparc/boot/btfix.s: arch/sparc/boot/btfixupprep vmlinux.o + $(OBJDUMP) -x vmlinux.o | arch/sparc/boot/btfixupprep > arch/sparc/boot/btfix.s + +arch/sparc/boot/btfix.o: arch/sparc/boot/btfix.s + $(CC) -c -o arch/sparc/boot/btfix.o arch/sparc/boot/btfix.s + +arch/sparc/boot/btfixupprep: arch/sparc/boot/btfixupprep.c + $(MAKE) -C arch/sparc/boot btfixupprep + +vmlinux: arch/sparc/boot/btfix.o + $(LD) $(LINKFLAGS) vmlinux.o arch/sparc/boot/btfix.o -o vmlinux + $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map diff --git a/arch/sparc/ap1000/apmmu.c b/arch/sparc/ap1000/apmmu.c index 0bafe3fc9..e07b4f4b1 100644 --- a/arch/sparc/ap1000/apmmu.c +++ b/arch/sparc/ap1000/apmmu.c @@ -36,16 +36,10 @@ #include <asm/viking.h> -static unsigned long (*mmu_getpage)(void); -static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp); -static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep); - -static void (*flush_page_for_dma)(unsigned long page); -static void (*flush_cache_page_to_uncache)(unsigned long page); -static void (*flush_tlb_page_for_cbit)(unsigned long page); - extern void mc_tlb_flush_all(void); +static void poke_viking(void); +static void viking_flush_tlb_page_for_cbit)(unsigned long page); static struct apmmu_stats { int invall; @@ -103,11 +97,6 @@ static inline unsigned long apmmu_swap(unsigned long *addr, unsigned long value) static unsigned int apmmu_pmd_align(unsigned int addr) { return APMMU_PMD_ALIGN(addr); } static unsigned int apmmu_pgdir_align(unsigned int addr) { return APMMU_PGDIR_ALIGN(addr); } -static unsigned long apmmu_vmalloc_start(void) -{ - return APMMU_VMALLOC_START; -} - static inline int apmmu_device_memory(unsigned long x) { return ((x & 0xF0000000) != 0); @@ -152,13 +141,6 @@ static int apmmu_pgd_present(pgd_t pgd) static void apmmu_pgd_clear(pgd_t * pgdp) { set_pte((pte_t *)pgdp, __pte(0)); } -static int apmmu_pte_write(pte_t pte) { return pte_val(pte) & APMMU_WRITE; } -static int apmmu_pte_dirty(pte_t pte) { return pte_val(pte) & APMMU_DIRTY; } -static int apmmu_pte_young(pte_t pte) { return pte_val(pte) & APMMU_REF; } - -static pte_t apmmu_pte_wrprotect(pte_t pte) { return __pte(pte_val(pte) & ~APMMU_WRITE);} -static pte_t apmmu_pte_mkclean(pte_t pte) { return __pte(pte_val(pte) & ~APMMU_DIRTY);} -static pte_t apmmu_pte_mkold(pte_t pte) { return __pte(pte_val(pte) & ~APMMU_REF);} static pte_t apmmu_pte_mkwrite(pte_t pte) { return __pte(pte_val(pte) | APMMU_WRITE);} static pte_t apmmu_pte_mkdirty(pte_t pte) { return __pte(pte_val(pte) | APMMU_DIRTY);} static pte_t apmmu_pte_mkyoung(pte_t pte) { return __pte(pte_val(pte) | APMMU_REF);} @@ -221,7 +203,7 @@ static void apmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { if(tsk->mm->context != NO_CONTEXT) { flush_cache_mm(current->mm); - ctxd_set(&apmmu_context_table[tsk->mm->context], pgdp); + apmmu_ctxd_set(&apmmu_context_table[tsk->mm->context], pgdp); flush_tlb_mm(current->mm); } } @@ -311,8 +293,6 @@ static inline unsigned long apmmu_hwprobe(unsigned long vaddr) return retval; } - - static inline void apmmu_uncache_page(unsigned long addr) { pgd_t *pgdp = apmmu_pgd_offset(init_task.mm, addr); @@ -330,9 +310,8 @@ static inline void apmmu_uncache_page(unsigned long addr) } } - flush_cache_page_to_uncache(addr); set_pte(ptep, __pte((pte_val(*ptep) & ~APMMU_CACHE))); - flush_tlb_page_for_cbit(addr); + viking_flush_tlb_page_for_cbit(addr); } static inline void apmmu_recache_page(unsigned long addr) @@ -352,10 +331,10 @@ static inline void apmmu_recache_page(unsigned long addr) } } set_pte(ptep, __pte((pte_val(*ptep) | APMMU_CACHE))); - flush_tlb_page_for_cbit(addr); + viking_flush_tlb_page_for_cbit(addr); } -static unsigned long apmmu_getpage(void) +static inline unsigned long apmmu_getpage(void) { unsigned long page = get_free_page(GFP_KERNEL); @@ -368,13 +347,44 @@ static inline void apmmu_putpage(unsigned long page) } /* The easy versions. */ -#define NEW_PGD() (pgd_t *) mmu_getpage() -#define NEW_PMD() (pmd_t *) mmu_getpage() -#define NEW_PTE() (pte_t *) mmu_getpage() +#define NEW_PGD() (pgd_t *) apmmu_getpage() +#define NEW_PMD() (pmd_t *) apmmu_getpage() +#define NEW_PTE() (pte_t *) apmmu_getpage() #define FREE_PGD(chunk) apmmu_putpage((unsigned long)(chunk)) #define FREE_PMD(chunk) apmmu_putpage((unsigned long)(chunk)) #define FREE_PTE(chunk) apmmu_putpage((unsigned long)(chunk)) +static pte_t *apmmu_get_pte_fast(void) +{ + return (pte_t *)0; +} + +static pmd_t *apmmu_get_pmd_fast(void) +{ + return (pmd_t *)0; +} + +static pgd_t *apmmu_get_pgd_fast(void) +{ + return (pgd_t *)0; +} + +static void apmmu_free_pte_slow(pte_t *pte) +{ +/* TBD */ +} + +static void apmmu_free_pmd_slow(pmd_t *pmd) +{ +/* TBD */ +} + +static void apmmu_free_pgd_slow(pgd_t *pgd) +{ +/* TBD */ +} + + /* * Allocate and free page tables. The xxx_kernel() versions are * used to allocate a kernel page table - this turns on ASN bits @@ -392,17 +402,17 @@ static pte_t *apmmu_pte_alloc_kernel(pmd_t *pmd, unsigned long address) pte_t *page = NEW_PTE(); if(apmmu_pmd_none(*pmd)) { if(page) { - pmd_set(pmd, page); + apmmu_pmd_set(pmd, page); return page + address; } - pmd_set(pmd, BAD_PAGETABLE); + apmmu_pmd_set(pmd, BAD_PAGETABLE); return NULL; } FREE_PTE(page); } if(apmmu_pmd_bad(*pmd)) { printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); - pmd_set(pmd, BAD_PAGETABLE); + apmmu_pmd_set(pmd, BAD_PAGETABLE); return NULL; } return (pte_t *) apmmu_pmd_page(*pmd) + address; @@ -449,17 +459,17 @@ static pte_t *apmmu_pte_alloc(pmd_t * pmd, unsigned long address) pte_t *page = NEW_PTE(); if(apmmu_pmd_none(*pmd)) { if(page) { - pmd_set(pmd, page); + apmmu_pmd_set(pmd, page); return page + address; } - pmd_set(pmd, BAD_PAGETABLE); + apmmu_pmd_set(pmd, BAD_PAGETABLE); return NULL; } FREE_PTE(page); } if(apmmu_pmd_bad(*pmd)) { printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); - pmd_set(pmd, BAD_PAGETABLE); + apmmu_pmd_set(pmd, BAD_PAGETABLE); return NULL; } return ((pte_t *) apmmu_pmd_page(*pmd)) + address; @@ -525,7 +535,7 @@ static inline void alloc_context(struct task_struct *tsk) struct mm_struct *mm = tsk->mm; struct ctx_list *ctxp; - if (tsk->taskid >= MPP_TASK_BASE) { + if (tsk->taskid >= MPP_TASK_BASE) { mm->context = MPP_CONTEXT_BASE + (tsk->taskid - MPP_TASK_BASE); return; } @@ -570,7 +580,7 @@ static void apmmu_switch_to_context(struct task_struct *tsk) if(tsk->mm->context == NO_CONTEXT) { alloc_context(tsk); flush_cache_mm(current->mm); - ctxd_set(&apmmu_context_table[tsk->mm->context], tsk->mm->pgd); + apmmu_ctxd_set(&apmmu_context_table[tsk->mm->context], tsk->mm->pgd); flush_tlb_mm(current->mm); } apmmu_set_context(tsk->mm->context); @@ -590,29 +600,11 @@ struct task_struct *apmmu_alloc_task_struct(void) return (struct task_struct *) kmalloc(sizeof(struct task_struct), GFP_KERNEL); } -static unsigned long apmmu_alloc_kernel_stack(struct task_struct *tsk) -{ - unsigned long kstk = __get_free_pages(GFP_KERNEL, 1); - - if(!kstk) - kstk = (unsigned long) vmalloc(PAGE_SIZE << 1); - - return kstk; -} - static void apmmu_free_task_struct(struct task_struct *tsk) { kfree(tsk); } -static void apmmu_free_kernel_stack(unsigned long stack) -{ - if(stack < VMALLOC_START) - free_pages(stack, 1); - else - vfree((char *)stack); -} - static void apmmu_null_func(void) { } @@ -925,22 +917,20 @@ extern unsigned long sparc_context_init(unsigned long, int); extern int physmem_mapped_contig; extern int linux_num_cpus; -void (*poke_apmmu)(void); - __initfunc(unsigned long apmmu_paging_init(unsigned long start_mem, unsigned long end_mem)) { int i; physmem_mapped_contig = 1; /* for init.c:taint_real_pages() */ - num_contexts = AP_NUM_CONTEXTS; + num_contexts = AP_NUM_CONTEXTS; mempool = PAGE_ALIGN(start_mem); memset(swapper_pg_dir, 0, PAGE_SIZE); apmmu_allocate_ptable_skeleton(KERNBASE, end_mem); mempool = PAGE_ALIGN(mempool); map_kernel(); - ap_setup_mappings(); + ap_setup_mappings(); /* the MSC wants this aligned on a 16k boundary */ apmmu_context_table = @@ -950,14 +940,14 @@ __initfunc(unsigned long apmmu_paging_init(unsigned long start_mem, unsigned lon num_contexts*sizeof(ctxd_t)); apmmu_ctx_table_phys = (ctxd_t *) apmmu_v2p((unsigned long) apmmu_context_table); for(i = 0; i < num_contexts; i++) - ctxd_set(&apmmu_context_table[i], swapper_pg_dir); + apmmu_ctxd_set(&apmmu_context_table[i], swapper_pg_dir); start_mem = PAGE_ALIGN(mempool); flush_cache_all(); apmmu_set_ctable_ptr((unsigned long) apmmu_ctx_table_phys); flush_tlb_all(); - poke_apmmu(); + poke_viking(); /* on the AP we don't put the top few contexts into the free context list as these are reserved for parallel tasks */ @@ -967,11 +957,10 @@ __initfunc(unsigned long apmmu_paging_init(unsigned long start_mem, unsigned lon return PAGE_ALIGN(start_mem); } -static char apmmuinfo[512]; - -static char *apmmu_mmu_info(void) +static int apmmu_mmu_info(char *buf) { - sprintf(apmmuinfo, "MMU type\t: %s\n" + return sprintf(buf, + "MMU type\t: %s\n" "invall\t\t: %d\n" "invmm\t\t: %d\n" "invrnge\t\t: %d\n" @@ -984,35 +973,12 @@ static char *apmmu_mmu_info(void) module_stats.invpg, num_contexts ); - return apmmuinfo; } static void apmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) { } -static void apmmu_exit_hook(void) -{ - struct mm_struct *mm = current->mm; - - if(mm->context != NO_CONTEXT && mm->count == 1) { - ctxd_set(&apmmu_context_table[mm->context], swapper_pg_dir); - viking_flush_tlb_mm(mm); - free_context(mm->context); - mm->context = NO_CONTEXT; - } -} - -static void apmmu_flush_hook(void) -{ - if(current->tss.flags & SPARC_FLAG_KTHREAD) { - alloc_context(current); - ctxd_set(&apmmu_context_table[current->mm->context], current->mm->pgd); - viking_flush_tlb_mm(current->mm); - apmmu_set_context(current->mm->context); - } -} - __initfunc(static void poke_viking(void)) { unsigned long mreg = apmmu_get_mmureg(); @@ -1020,7 +986,7 @@ __initfunc(static void poke_viking(void)) mreg |= VIKING_SPENABLE; mreg |= (VIKING_ICENABLE | VIKING_DCENABLE); mreg &= ~VIKING_ACENABLE; - mreg &= ~VIKING_SBENABLE; + mreg &= ~VIKING_SBENABLE; mreg |= VIKING_TCENABLE; apmmu_set_mmureg(mreg); } @@ -1029,24 +995,18 @@ __initfunc(static void init_viking(void)) { apmmu_name = "TI Viking/AP1000"; - flush_cache_page_to_uncache = apmmu_null_func; - flush_page_for_dma = apmmu_null_func; - - flush_cache_all = apmmu_null_func; - flush_cache_mm = apmmu_null_func; - flush_cache_page = apmmu_null_func; - flush_cache_range = apmmu_null_func; + BTFIXUPSET_CALL(flush_cache_all, apmmu_null_func, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_cache_mm, apmmu_null_func, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_cache_page, apmmu_null_func, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_cache_range, apmmu_null_func, BTFIXUPCALL_NOP); - flush_tlb_all = viking_flush_tlb_all; - flush_tlb_mm = viking_flush_tlb_mm; - flush_tlb_page = viking_flush_tlb_page; - flush_tlb_range = viking_flush_tlb_range; + BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM); - flush_page_to_ram = apmmu_null_func; - flush_sig_insns = apmmu_null_func; - flush_tlb_page_for_cbit = viking_flush_tlb_page_for_cbit; - - poke_apmmu = poke_viking; + BTFIXUPSET_CALL(flush_page_to_ram, apmmu_null_func, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_sig_insns, apmmu_null_func, BTFIXUPCALL_NOP); } @@ -1062,7 +1022,7 @@ extern unsigned long srmmu_fault; iaddr = &(insn); \ daddr = &(dest); \ *iaddr = SPARC_BRANCH((unsigned long) daddr, (unsigned long) iaddr); \ - } while(0); + } while(0); __initfunc(static void patch_window_trap_handlers(void)) { @@ -1077,113 +1037,109 @@ __initfunc(static void patch_window_trap_handlers(void)) PATCH_BRANCH(sparc_ttable[SP_TRAP_DACC].inst_three, srmmu_fault); } -/* Load up routines and constants for sun4m mmu */ +/* Load up routines and constants for apmmu */ __initfunc(void ld_mmu_apmmu(void)) { /* First the constants */ - pmd_shift = APMMU_PMD_SHIFT; - pmd_size = APMMU_PMD_SIZE; - pmd_mask = APMMU_PMD_MASK; - pgdir_shift = APMMU_PGDIR_SHIFT; - pgdir_size = APMMU_PGDIR_SIZE; - pgdir_mask = APMMU_PGDIR_MASK; - - ptrs_per_pte = APMMU_PTRS_PER_PTE; - ptrs_per_pmd = APMMU_PTRS_PER_PMD; - ptrs_per_pgd = APMMU_PTRS_PER_PGD; - - page_none = APMMU_PAGE_NONE; - page_shared = APMMU_PAGE_SHARED; - page_copy = APMMU_PAGE_COPY; - page_readonly = APMMU_PAGE_RDONLY; - page_kernel = APMMU_PAGE_KERNEL; + BTFIXUPSET_SIMM13(pmd_shift, APMMU_PMD_SHIFT); + BTFIXUPSET_SETHI(pmd_size, APMMU_PMD_SIZE); + BTFIXUPSET_SETHI(pmd_mask, APMMU_PMD_MASK); + BTFIXUPSET_SIMM13(pgdir_shift, APMMU_PGDIR_SHIFT); + BTFIXUPSET_SETHI(pgdir_size, APMMU_PGDIR_SIZE); + BTFIXUPSET_SETHI(pgdir_mask, APMMU_PGDIR_MASK); + + BTFIXUPSET_SIMM13(ptrs_per_pte, APMMU_PTRS_PER_PTE); + BTFIXUPSET_SIMM13(ptrs_per_pmd, APMMU_PTRS_PER_PMD); + BTFIXUPSET_SIMM13(ptrs_per_pgd, APMMU_PTRS_PER_PGD); + + BTFIXUPSET_INT(page_none, pgprot_val(APMMU_PAGE_NONE)); + BTFIXUPSET_INT(page_shared, pgprot_val(APMMU_PAGE_SHARED)); + BTFIXUPSET_INT(page_copy, pgprot_val(APMMU_PAGE_COPY)); + BTFIXUPSET_INT(page_readonly, pgprot_val(APMMU_PAGE_RDONLY)); + BTFIXUPSET_INT(page_kernel, pgprot_val(APMMU_PAGE_KERNEL)); pg_iobits = APMMU_VALID | APMMU_WRITE | APMMU_REF; /* Functions */ - mmu_getpage = apmmu_getpage; - set_pte = apmmu_set_pte_cacheable; - switch_to_context = apmmu_switch_to_context; - pmd_align = apmmu_pmd_align; - pgdir_align = apmmu_pgdir_align; - vmalloc_start = apmmu_vmalloc_start; - - pte_page = apmmu_pte_page; - pmd_page = apmmu_pmd_page; - pgd_page = apmmu_pgd_page; - - sparc_update_rootmmu_dir = apmmu_update_rootmmu_dir; - - pte_none = apmmu_pte_none; - pte_present = apmmu_pte_present; - pte_clear = apmmu_pte_clear; - - pmd_none = apmmu_pmd_none; - pmd_bad = apmmu_pmd_bad; - pmd_present = apmmu_pmd_present; - pmd_clear = apmmu_pmd_clear; - - pgd_none = apmmu_pgd_none; - pgd_bad = apmmu_pgd_bad; - pgd_present = apmmu_pgd_present; - pgd_clear = apmmu_pgd_clear; - - mk_pte = apmmu_mk_pte; - mk_pte_phys = apmmu_mk_pte_phys; - pgd_set = apmmu_pgd_set; - mk_pte_io = apmmu_mk_pte_io; - pte_modify = apmmu_pte_modify; - pgd_offset = apmmu_pgd_offset; - pmd_offset = apmmu_pmd_offset; - pte_offset = apmmu_pte_offset; - pte_free_kernel = apmmu_pte_free_kernel; - pmd_free_kernel = apmmu_pmd_free_kernel; - pte_alloc_kernel = apmmu_pte_alloc_kernel; - pmd_alloc_kernel = apmmu_pmd_alloc_kernel; - pte_free = apmmu_pte_free; - pte_alloc = apmmu_pte_alloc; - pmd_free = apmmu_pmd_free; - pmd_alloc = apmmu_pmd_alloc; - pgd_free = apmmu_pgd_free; - pgd_alloc = apmmu_pgd_alloc; - pgd_flush = apmmu_pgd_flush; - - pte_write = apmmu_pte_write; - pte_dirty = apmmu_pte_dirty; - pte_young = apmmu_pte_young; - pte_wrprotect = apmmu_pte_wrprotect; - pte_mkclean = apmmu_pte_mkclean; - pte_mkold = apmmu_pte_mkold; - pte_mkwrite = apmmu_pte_mkwrite; - pte_mkdirty = apmmu_pte_mkdirty; - pte_mkyoung = apmmu_pte_mkyoung; - update_mmu_cache = apmmu_update_mmu_cache; - mmu_exit_hook = apmmu_exit_hook; - mmu_flush_hook = apmmu_flush_hook; - mmu_lockarea = apmmu_lockarea; - mmu_unlockarea = apmmu_unlockarea; - - mmu_get_scsi_one = NULL; - mmu_get_scsi_sgl = NULL; - mmu_release_scsi_one = NULL; - mmu_release_scsi_sgl = NULL; - - mmu_info = apmmu_mmu_info; - mmu_v2p = apmmu_v2p; - mmu_p2v = apmmu_p2v; + BTFIXUPSET_CALL(get_pte_fast, apmmu_get_pte_fast, BTFIXUPCALL_RETINT(0)); + BTFIXUPSET_CALL(get_pmd_fast, apmmu_get_pmd_fast, BTFIXUPCALL_RETINT(0)); + BTFIXUPSET_CALL(get_pgd_fast, apmmu_get_pgd_fast, BTFIXUPCALL_RETINT(0)); + BTFIXUPSET_CALL(free_pte_slow, apmmu_free_pte_slow, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(free_pmd_slow, apmmu_free_pmd_slow, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(free_pgd_slow, apmmu_free_pgd_slow, BTFIXUPCALL_NOP); - /* Task struct and kernel stack allocating/freeing. */ - alloc_kernel_stack = apmmu_alloc_kernel_stack; - alloc_task_struct = apmmu_alloc_task_struct; - free_kernel_stack = apmmu_free_kernel_stack; - free_task_struct = apmmu_free_task_struct; + BTFIXUPSET_CALL(set_pte, apmmu_set_pte_cacheable, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(switch_to_context, apmmu_switch_to_context, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(pte_page, apmmu_pte_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_page, apmmu_pmd_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_page, apmmu_pgd_page, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(sparc_update_rootmmu_dir, apmmu_update_rootmmu_dir, BTFIXUPCALL_NORM); + + BTFIXUPSET_SETHI(none_mask, 0xF0000000); + + BTFIXUPSET_CALL(pte_present, apmmu_pte_present, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_clear, apmmu_pte_clear, BTFIXUPCALL_NORM); - quick_kernel_fault = apmmu_quick_kernel_fault; + BTFIXUPSET_CALL(pmd_bad, apmmu_pmd_bad, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_present, apmmu_pmd_present, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_clear, apmmu_pmd_clear, BTFIXUPCALL_NORM); - ctxd_set = apmmu_ctxd_set; - pmd_set = apmmu_pmd_set; + BTFIXUPSET_CALL(pgd_none, apmmu_pgd_none, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_bad, apmmu_pgd_bad, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_present, apmmu_pgd_present, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_clear, apmmu_pgd_clear, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(mk_pte, apmmu_mk_pte, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mk_pte_phys, apmmu_mk_pte_phys, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mk_pte_io, apmmu_mk_pte_io, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_set, apmmu_pgd_set, BTFIXUPCALL_NORM); + + BTFIXUPSET_INT(pte_modify_mask, APMMU_CHG_MASK); + BTFIXUPSET_CALL(pgd_offset, apmmu_pgd_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_offset, apmmu_pmd_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_offset, apmmu_pte_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_free_kernel, apmmu_pte_free_kernel, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_free_kernel, apmmu_pmd_free_kernel, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_alloc_kernel, apmmu_pte_alloc_kernel, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_alloc_kernel, apmmu_pmd_alloc_kernel, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_free, apmmu_pte_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_alloc, apmmu_pte_alloc, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_free, apmmu_pmd_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_alloc, apmmu_pmd_alloc, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_free, apmmu_pgd_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_alloc, apmmu_pgd_alloc, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_flush, apmmu_pgd_flush, BTFIXUPCALL_NORM); + + BTFIXUPSET_HALF(pte_writei, APMMU_WRITE); + BTFIXUPSET_HALF(pte_dirtyi, APMMU_DIRTY); + BTFIXUPSET_HALF(pte_youngi, APMMU_REF); + BTFIXUPSET_HALF(pte_wrprotecti, APMMU_WRITE); + BTFIXUPSET_HALF(pte_mkcleani, APMMU_DIRTY); + BTFIXUPSET_HALF(pte_mkoldi, APMMU_REF); + BTFIXUPSET_CALL(pte_mkwrite, apmmu_pte_mkwrite, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_mkdirty, apmmu_pte_mkdirty, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_mkyoung, apmmu_pte_mkyoung, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(update_mmu_cache, apmmu_update_mmu_cache, BTFIXUPCALL_NOP); + + BTFIXUPSET_CALL(mmu_lockarea, apmmu_lockarea, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_unlockarea, apmmu_unlockarea, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(mmu_get_scsi_one, apmmu_null_func, BTFIXUPCALL_RETO0); + BTFIXUPSET_CALL(mmu_get_scsi_sgl, apmmu_null_func, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(mmu_release_scsi_one, apmmu_null_func, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(mmu_release_scsi_sgl, apmmu_null_func, BTFIXUPCALL_NOP); + + BTFIXUPSET_CALL(mmu_info, apmmu_mmu_info, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_v2p, apmmu_v2p, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_p2v, apmmu_p2v, BTFIXUPCALL_NORM); + + /* Task struct and kernel stack allocating/freeing. */ + BTFIXUPSET_CALL(alloc_task_struct, apmmu_alloc_task_struct, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(free_task_struct, apmmu_free_task_struct, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(quick_kernel_fault, apmmu_quick_kernel_fault, BTFIXUPCALL_NORM); init_viking(); patch_window_trap_handlers(); } - - diff --git a/arch/sparc/boot/Makefile b/arch/sparc/boot/Makefile index af462db3a..c9301a79e 100644 --- a/arch/sparc/boot/Makefile +++ b/arch/sparc/boot/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.4 1997/07/11 11:05:18 jj Exp $ +# $Id: Makefile,v 1.6 1998/02/23 01:44:39 rth Exp $ # Makefile for the Sparc boot stuff. # # Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -19,5 +19,11 @@ tftpboot.img: piggyback piggyback: piggyback.c $(HOSTCC) $(HOSTCFLAGS) -o piggyback piggyback.c +btfixupprep: btfixupprep.c + $(HOSTCC) $(HOSTCFLAGS) -o btfixupprep btfixupprep.c + +archclean: + rm -f btfixupprep piggyback tftpboot.img + dep: diff --git a/arch/sparc/boot/btfixupprep.c b/arch/sparc/boot/btfixupprep.c new file mode 100644 index 000000000..1bef965af --- /dev/null +++ b/arch/sparc/boot/btfixupprep.c @@ -0,0 +1,345 @@ +/* $Id: btfixupprep.c,v 1.3 1998/03/09 14:03:10 jj Exp $ + Simple utility to prepare vmlinux image for sparc. + Resolves all BTFIXUP uses and settings and creates + a special .s object to link to the image. + + Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <malloc.h> + +#define MAXSYMS 1024 + +static char *relrec = "RELOCATION RECORDS FOR ["; +static int rellen; + +struct _btfixup; + +typedef struct _btfixuprel { + char *sect; + unsigned long offset; + struct _btfixup *f; + int frel; + struct _btfixuprel *next; +} btfixuprel; + +typedef struct _btfixup { + int type; + int setinitval; + unsigned int initval; + char *initvalstr; + char *name; + btfixuprel *rel; +} btfixup; + +btfixup array[MAXSYMS]; +int last = 0; +char buffer[1024]; +unsigned long lastfoffset = -1; +unsigned long lastfrelno; +btfixup *lastf; + +void fatal(void) __attribute__((noreturn)); +void fatal(void) +{ + fprintf(stderr, "Malformed output from objdump\n%s\n", buffer); + exit(1); +} + +btfixup *find(int type, char *name) +{ + int i; + for (i = 0; i < last; i++) { + if (array[i].type == type && !strcmp(array[i].name, name)) + return array + i; + } + array[last].type = type; + array[last].name = strdup(name); + array[last].setinitval = 0; + if (!array[last].name) fatal(); + array[last].rel = NULL; + last++; + if (last >= MAXSYMS) { + fprintf(stderr, "Ugh. Something strange. More than %d different BTFIXUP symbols\n", MAXSYMS); + exit(1); + } + return array + last - 1; +} + +int main(int argc,char **argv) +{ + char *p, *q; + char *sect; + int i, j, k; + unsigned int initval; + int shift; + btfixup *f; + btfixuprel *r, **rr; + unsigned long offset; + char *initvalstr; + + rellen = strlen(relrec); + while (fgets (buffer, 1024, stdin) != NULL) + if (!strncmp (buffer, relrec, rellen)) + goto main1; + fatal(); +main1: + sect = malloc(strlen (buffer + rellen) + 1); + if (!sect) fatal(); + strcpy (sect, buffer + rellen); + p = strchr (sect, ']'); + if (!p) fatal(); + *p = 0; + if (fgets (buffer, 1024, stdin) == NULL) + fatal(); + while (fgets (buffer, 1024, stdin) != NULL) { + if (!strncmp (buffer, relrec, rellen)) + goto main1; + p = strchr (buffer, '\n'); + if (p) *p = 0; + if (strlen (buffer) < 30) + continue; + if (strncmp (buffer + 8, " R_SPARC_", 9)) + continue; + if (buffer[27] != '_' || buffer[28] != '_' || buffer[29] != '_') + continue; + switch (buffer[30]) { + case 'f': /* CALL */ + case 'b': /* BLACKBOX */ + case 's': /* SIMM13 */ + case 'a': /* HALF */ + case 'h': /* SETHI */ + case 'i': /* INT */ + break; + default: + continue; + } + p = strchr (buffer + 32, '+'); + if (p) *p = 0; + shift = 32; + if (buffer[31] == 's' && buffer[32] == '_') { + shift = 33; + if (strcmp (sect, ".text.init")) { + fprintf(stderr, "Wrong use of '%s' BTFIXUPSET.\nBTFIXUPSET_CALL can be used only in __init sections\n", buffer+shift); + exit(1); + } + } else if (buffer[31] != '_') + continue; + if (strcmp (sect, ".text") && strcmp (sect, ".text.init") && (strcmp (sect, "__ksymtab") || buffer[30] != 'f')) { + if (buffer[30] == 'f') + fprintf(stderr, "Wrong use of '%s' in '%s' section. It can be only used in .text, .text.init and __ksymtab\n", buffer + shift, sect); + else + fprintf(stderr, "Wrong use of '%s' in '%s' section. It can be only used in .text and .text.init\n", buffer + shift, sect); + exit(1); + } + p = strstr (buffer + shift, "__btset_"); + if (p && buffer[31] == 's') { + fprintf(stderr, "__btset_ in BTFIXUP name can only be used when defining the variable, not for setting\n%s\n", buffer); + exit(1); + } + initval = 0; + initvalstr = NULL; + if (p) { + if (p[8] != '0' || p[9] != 'x') { + fprintf(stderr, "Pre-initialized values can be only initialized with hexadecimal constants starting 0x\n%s\n", buffer); + exit(1); + } + initval = strtoul(p + 10, &q, 16); + if (*q || !initval) { + fprintf(stderr, "Pre-initialized values can be only in the form name__btset_0xXXXXXXXX where X are hex digits.\nThey cannot be name__btset_0x00000000 though. Use BTFIXUPDEF_XX instead of BTFIXUPDEF_XX_INIT then.\n%s\n", buffer); + exit(1); + } + initvalstr = p + 10; + *p = 0; + } + f = find(buffer[30], buffer + shift); + if (buffer[31] == 's') + continue; + switch (buffer[30]) { + case 'f': + if (initval) { + fprintf(stderr, "Cannot use pre-initalized fixups for calls\n%s\n", buffer); + exit(1); + } + if (!strcmp (sect, "__ksymtab")) { + if (strncmp (buffer + 17, "32 ", 10)) { + fprintf(stderr, "BTFIXUP_CALL in EXPORT_SYMBOL results in relocation other than R_SPARC_32\n\%s\n", buffer); + exit(1); + } + } else if (strncmp (buffer + 17, "WDISP30 ", 10) && + strncmp (buffer + 17, "HI22 ", 10) && + strncmp (buffer + 17, "LO10 ", 10)) { + fprintf(stderr, "BTFIXUP_CALL results in relocation other than R_SPARC_WDISP30, R_SPARC_HI22 or R_SPARC_LO10\n%s\n", buffer); + exit(1); + } + break; + case 'b': + if (initval) { + fprintf(stderr, "Cannot use pre-initialized fixups for blackboxes\n%s\n", buffer); + exit(1); + } + if (strncmp (buffer + 17, "HI22 ", 10)) { + fprintf(stderr, "BTFIXUP_BLACKBOX results in relocation other than R_SPARC_HI22\n%s\n", buffer); + exit(1); + } + break; + case 's': + if (initval + 0x1000 >= 0x2000) { + fprintf(stderr, "Wrong initializer for SIMM13. Has to be from $fffff000 to $00000fff\n%s\n", buffer); + exit(1); + } + if (strncmp (buffer + 17, "13 ", 10)) { + fprintf(stderr, "BTFIXUP_SIMM13 results in relocation other than R_SPARC_13\n%s\n", buffer); + exit(1); + } + break; + case 'a': + if (initval + 0x1000 >= 0x2000 && (initval & 0x3ff)) { + fprintf(stderr, "Wrong initializer for HALF.\n%s\n", buffer); + exit(1); + } + if (strncmp (buffer + 17, "13 ", 10)) { + fprintf(stderr, "BTFIXUP_HALF results in relocation other than R_SPARC_13\n%s\n", buffer); + exit(1); + } + break; + case 'h': + if (initval & 0x3ff) { + fprintf(stderr, "Wrong initializer for SETHI. Cannot have set low 10 bits\n%s\n", buffer); + exit(1); + } + if (strncmp (buffer + 17, "HI22 ", 10)) { + fprintf(stderr, "BTFIXUP_SETHI results in relocation other than R_SPARC_HI22\n%s\n", buffer); + exit(1); + } + break; + case 'i': + if (initval) { + fprintf(stderr, "Cannot use pre-initalized fixups for INT\n%s\n", buffer); + exit(1); + } + if (strncmp (buffer + 17, "HI22 ", 10) && strncmp (buffer + 17, "LO10 ", 10)) { + fprintf(stderr, "BTFIXUP_INT results in relocation other than R_SPARC_HI22 and R_SPARC_LO10\n%s\n", buffer); + exit(1); + } + break; + } + if (!f->setinitval) { + f->initval = initval; + if (initvalstr) { + f->initvalstr = strdup(initvalstr); + if (!f->initvalstr) fatal(); + } + f->setinitval = 1; + } else if (f->initval != initval) { + fprintf(stderr, "Btfixup %s previously used with initializer %s which doesn't match with current initializer\n%s\n", + f->name, f->initvalstr ? : "0x00000000", buffer); + exit(1); + } else if (initval && strcmp(f->initvalstr, initvalstr)) { + fprintf(stderr, "Btfixup %s previously used with initializer %s which doesn't match with current initializer.\n" + "Initializers have to match literally as well.\n%s\n", + f->name, f->initvalstr, buffer); + exit(1); + } + offset = strtoul(buffer, &q, 16); + if (q != buffer + 8 || (!offset && strncmp (buffer, "00000000 ", 9))) { + fprintf(stderr, "Malformed relocation address in\n%s\n", buffer); + exit(1); + } + for (k = 0, r = f->rel, rr = &f->rel; r; rr = &r->next, r = r->next, k++) + if (r->offset == offset && !strcmp(r->sect, sect)) { + fprintf(stderr, "Ugh. One address has two relocation records\n"); + exit(1); + } + *rr = malloc(sizeof(btfixuprel)); + if (!*rr) fatal(); + (*rr)->offset = offset; + (*rr)->f = NULL; + if (buffer[30] == 'f') { + lastf = f; + lastfoffset = offset; + lastfrelno = k; + } else if (lastfoffset + 4 == offset) { + (*rr)->f = lastf; + (*rr)->frel = lastfrelno; + } + (*rr)->sect = sect; + (*rr)->next = NULL; + } + printf("! Generated by btfixupprep. Do not edit.\n\n"); + printf("\t.section\t\".data.init\",#alloc,#write\n\t.align\t4\n\n"); + printf("\t.global\t___btfixup_start\n___btfixup_start:\n\n"); + for (i = 0; i < last; i++) { + f = array + i; + printf("\t.global\t___%cs_%s\n", f->type, f->name); + if (f->type == 'f') + printf("___%cs_%s:\n\t.word 0x%08x,0,0,", f->type, f->name, f->type << 24); + else + printf("___%cs_%s:\n\t.word 0x%08x,0,", f->type, f->name, f->type << 24); + for (j = 0, r = f->rel; r != NULL; j++, r = r->next); + if (j) + printf("%d\n\t.word\t", j * 2); + else + printf("0\n"); + for (r = f->rel, j--; r != NULL; j--, r = r->next) { + if (!strcmp (r->sect, ".text")) + printf ("_stext+0x%08x", r->offset); + else if (!strcmp (r->sect, ".text.init")) + printf ("__init_begin+0x%08x", r->offset); + else if (!strcmp (r->sect, "__ksymtab")) + printf ("__start___ksymtab+0x%08x", r->offset); + else + fatal(); + if (f->type == 'f' || !r->f) + printf (",0"); + else + printf (",___fs_%s+0x%08x", r->f->name, (4 + r->frel*2)*4 + 4); + if (j) printf (","); + else printf ("\n"); + } + printf("\n"); + } + printf("\n\t.global\t___btfixup_end\n___btfixup_end:\n"); + printf("\n\n! Define undefined references\n\n"); + for (i = 0; i < last; i++) { + f = array + i; + if (f->type == 'f') { + printf("\t.global\t___f_%s\n", f->name); + printf("___f_%s:\n", f->name); + } + } + printf("\tretl\n\t nop\n\n"); + for (i = 0; i < last; i++) { + f = array + i; + if (f->type != 'f') { + if (!f->initval) { + printf("\t.global\t___%c_%s\n", f->type, f->name); + printf("___%c_%s = 0\n", f->type, f->name); + } else { + printf("\t.global\t___%c_%s__btset_0x%s\n", f->type, f->name, f->initvalstr); + printf("___%c_%s__btset_0x%s = 0x%08x\n", f->type, f->name, f->initvalstr, f->initval); + } + } + } + printf("\n\n"); + exit(0); +} diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 4a087fca2..3f0ab09b6 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.51 1998/01/08 04:16:54 baccala Exp $ +# $Id: config.in,v 1.54 1998/03/27 06:59:39 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -34,6 +34,8 @@ if [ "$CONFIG_AP1000" = "y" ]; then define_bool CONFIG_APBIF y tristate 'OPIU DDV Driver' CONFIG_DDV else + bool 'Support for SUN4 machines (disables SUN4[CDM] support)' CONFIG_SUN4 + # Global things across all Sun machines. define_bool CONFIG_SBUS y define_bool CONFIG_SBUSCHAR y @@ -45,8 +47,13 @@ else define_bool CONFIG_SUN_CONSOLE y define_bool CONFIG_SUN_AUXIO y define_bool CONFIG_SUN_IO y - source drivers/sbus/char/Config.in - source drivers/sbus/audio/Config.in + if [ "$CONFIG_SUN4" = "y" ]; then + bool 'Sun FB drivers appear in PROCFS' SUN_FBS_IN_PROCFS + bool 'bwtwo support' SUN_FB_BWTWO + else + source drivers/sbus/char/Config.in + source drivers/sbus/audio/Config.in + fi fi tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS @@ -159,17 +166,6 @@ if [ "$CONFIG_NET" = "y" ]; then endmenu fi -# Conditionally compile in the Uniform CD-ROM driver -if [ "$CONFIG_BLK_DEV_SR" = "y" ]; then - define_bool CONFIG_CDROM y -else - if [ "$CONFIG_BLK_DEV_SR" = "m" ]; then - define_bool CONFIG_CDROM m - else - define_bool CONFIG_CDROM n - fi -fi - source fs/Config.in source fs/nls/Config.in diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index 38b6096cc..1abeca2d6 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -20,11 +20,13 @@ CONFIG_KMOD=y CONFIG_VT=y CONFIG_VT_CONSOLE=y # CONFIG_AP1000 is not set +# CONFIG_SUN4 is not set CONFIG_SBUS=y CONFIG_SBUSCHAR=y CONFIG_SUN_MOUSE=y CONFIG_SERIAL=y CONFIG_SUN_SERIAL=y +CONFIG_SERIAL_CONSOLE=y CONFIG_SUN_KEYBOARD=y CONFIG_SUN_CONSOLE=y CONFIG_SUN_AUXIO=y @@ -80,6 +82,7 @@ CONFIG_MD_RAID5=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_NBD is not set # # Networking options @@ -89,8 +92,8 @@ CONFIG_NETLINK=y CONFIG_RTNETLINK=y # CONFIG_NETLINK_DEV is not set CONFIG_FIREWALL=y -# CONFIG_NET_SECURITY is not set CONFIG_NET_ALIAS=y +# CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y CONFIG_IP_MULTICAST=y @@ -107,11 +110,18 @@ CONFIG_IP_MASQUERADE=y # # Protocol-specific masquerading support will be built as modules. # +# CONFIG_IP_MASQUERADE_ICMP is not set + +# +# Protocol-specific masquerading support will be built as modules. +# +# CONFIG_IP_MASQUERADE_IPAUTOFW is not set +# CONFIG_IP_MASQUERADE_IPPORTFW is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set -CONFIG_IP_ALIAS=m +CONFIG_IP_ALIAS=y # CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set @@ -123,31 +133,39 @@ CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y CONFIG_IPV6=m # CONFIG_IPV6_EUI64 is not set -# CONFIG_IPV6_NO_PB is not set # # # CONFIG_IPX=m + +# +# IPX options +# # CONFIG_IPX_INTERN is not set -# CONFIG_IPX_PPROP_ROUTING is not set CONFIG_ATALK=m -# CONFIG_AX25 is not set CONFIG_X25=m # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set # CONFIG_LLC is not set # CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set CONFIG_NET_SCHED=y CONFIG_NET_SCH_CBQ=m CONFIG_NET_SCH_CSZ=m -CONFIG_NET_SCH_HFQ=m CONFIG_NET_SCH_RED=m CONFIG_NET_SCH_SFQ=m CONFIG_NET_SCH_TBF=y CONFIG_NET_SCH_PFIFO=y CONFIG_NET_SCH_PRIO=y +# CONFIG_NET_PROFILE is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set # # SCSI support @@ -176,6 +194,21 @@ CONFIG_SCSI_SUNESP=y CONFIG_SCSI_QLOGICPTI=m # +# Fibre Channel support +# +CONFIG_FC4=m + +# +# FC4 drivers +# +CONFIG_FC4_SOC=m + +# +# FC4 targets +# +CONFIG_SCSI_PLUTO=m + +# # Network device support # CONFIG_NETDEVICES=y @@ -193,6 +226,7 @@ CONFIG_SUNLANCE=y CONFIG_HAPPYMEAL=m CONFIG_SUNQE=m CONFIG_MYRI_SBUS=m +CONFIG_CDROM=y # # Filesystems @@ -211,24 +245,35 @@ CONFIG_NFS_FS=y CONFIG_NFSD=m CONFIG_SUNRPC=y CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set CONFIG_SMB_FS=m CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set CONFIG_HPFS_FS=m +# CONFIG_NTFS_FS is not set CONFIG_SYSV_FS=m CONFIG_AFFS_FS=m +# CONFIG_HFS_FS is not set CONFIG_ROMFS_FS=m CONFIG_AUTOFS_FS=m CONFIG_AMIGA_PARTITION=y CONFIG_UFS_FS=y CONFIG_BSD_DISKLABEL=y CONFIG_SMD_DISKLABEL=y +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_ADFS_FS is not set # CONFIG_MAC_PARTITION is not set +CONFIG_NLS=y # # Native Language Support # -CONFIG_NLS=y # CONFIG_NLS_CODEPAGE_437 is not set # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 7be8ddd3a..53ed6d340 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.41 1997/11/19 15:11:59 jj Exp $ +# $Id: Makefile,v 1.43 1998/03/09 14:03:34 jj Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -15,8 +15,6 @@ ifdef SMP .S.o: $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $< -o $*.o -CHECKASM_CC = $(CC) -D__SMP__ - else .S.s: @@ -25,7 +23,6 @@ else .S.o: $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o -CHECKASM_CC = $(CC) endif all: kernel.o head.o init_task.o @@ -42,7 +39,7 @@ O_OBJS := entry.o wof.o wuf.o etrap.o rtrap.o traps.o ${IRQ_OBJS} \ OX_OBJS := sparc_ksyms.o ifdef SMP -O_OBJS += trampoline.o smp.o +O_OBJS += trampoline.o smp.o sun4m_smp.o sun4d_smp.o endif ifdef CONFIG_SUN_AUXIO @@ -62,18 +59,61 @@ head.o: head.S endif check_asm: dummy + @echo "/* Automatically generated. Do not edit. */" > asm_offsets.h + @echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h + @echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#ifndef __SMP__" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#include <linux/sched.h>" > tmp.c + $(CC) -E tmp.c -o tmp.i + @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/sched.h>" >> check_asm.c + @echo 'struct task_struct _task;' >> check_asm.c + @echo 'struct mm_struct _mm;' >> check_asm.c + @echo 'struct thread_struct _thread;' >> check_asm.c + @echo 'int main(void) {' >> check_asm.c + $(SH) ./check_asm.sh task tmp.i check_asm.c + $(SH) ./check_asm.sh mm tmp.i check_asm.c + $(SH) ./check_asm.sh thread tmp.i check_asm.c + @echo 'return 0; }' >> check_asm.c + @rm -f tmp.[ci] + $(CC) -o check_asm check_asm.c + ./check_asm >> asm_offsets.h + @rm -f check_asm check_asm.c + @echo "" >> asm_offsets.h + @echo "#else /* __SMP__ */" >> asm_offsets.h + @echo "" >> asm_offsets.h @echo "#include <linux/sched.h>" > tmp.c - $(CHECKASM_CC) -E tmp.c -o tmp.i - @echo "/* Automatically generated. Do not edit. */" > check_asm.c; echo "#include <linux/sched.h>" >> check_asm.c; echo 'struct task_struct _task; struct mm_struct _mm; struct thread_struct _thread; int main(void) { printf ("/* Automatically generated. Do not edit. */\n#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n\n");' >> check_asm.c + $(CC) -D__SMP__ -E tmp.c -o tmp.i + @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/sched.h>" >> check_asm.c + @echo 'struct task_struct _task;' >> check_asm.c + @echo 'struct mm_struct _mm;' >> check_asm.c + @echo 'struct thread_struct _thread;' >> check_asm.c + @echo 'int main(void) {' >> check_asm.c $(SH) ./check_asm.sh task tmp.i check_asm.c $(SH) ./check_asm.sh mm tmp.i check_asm.c $(SH) ./check_asm.sh thread tmp.i check_asm.c - @echo 'printf ("\n#endif /* __ASM_OFFSETS_H__ */\n"); return 0; }' >> check_asm.c + @echo 'return 0; }' >> check_asm.c @rm -f tmp.[ci] - $(CHECKASM_CC) -o check_asm check_asm.c - ./check_asm > asm_offsets.h - @if test -r $(HPATH)/asm/asm_offsets.h; then if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then echo $(HPATH)/asm/asm_offsets.h is unchanged; rm -f asm_offsets.h; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi + $(CC) -D__SMP__ -o check_asm check_asm.c + ./check_asm >> asm_offsets.h @rm -f check_asm check_asm.c + @echo "" >> asm_offsets.h + @echo "#endif /* __SMP__ */" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h + @if test -r $(HPATH)/asm/asm_offsets.h; then \ + if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then \ + echo $(HPATH)/asm/asm_offsets.h is unchanged; \ + rm -f asm_offsets.h; \ + else \ + mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \ + fi; \ + else \ + mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \ + fi include $(TOPDIR)/Rules.make diff --git a/arch/sparc/kernel/auxio.c b/arch/sparc/kernel/auxio.c index 5835347d1..13d34310f 100644 --- a/arch/sparc/kernel/auxio.c +++ b/arch/sparc/kernel/auxio.c @@ -17,9 +17,13 @@ __initfunc(void auxio_probe(void)) int node, auxio_nd; struct linux_prom_registers auxregs[1]; - if (sparc_cpu_model == sun4d) { + switch (sparc_cpu_model) { + case sun4d: + case sun4: auxio_register = 0; return; + default: + break; } node = prom_getchild(prom_root_node); auxio_nd = prom_searchsiblings(node, "auxiliary-io"); diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c index aeb5a46c8..1ca98407a 100644 --- a/arch/sparc/kernel/cpu.c +++ b/arch/sparc/kernel/cpu.c @@ -6,6 +6,8 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <linux/smp.h> +#include <linux/tasks.h> #include <asm/oplib.h> #include <asm/page.h> #include <asm/head.h> @@ -116,22 +118,25 @@ struct cpu_iu_info linux_sparc_chips[] = { #define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info)) -char *sparc_cpu_type[NCPUS] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" }; -char *sparc_fpu_type[NCPUS] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" }; +char *sparc_cpu_type[NR_CPUS] = { 0 }; +char *sparc_fpu_type[NR_CPUS] = { 0 }; unsigned int fsr_storage; __initfunc(void cpu_probe(void)) { int psr_impl, psr_vers, fpu_vers; - int i, cpuid; + int i, cpuid, psr; - cpuid = get_cpuid(); + cpuid = hard_smp_processor_id(); psr_impl = ((get_psr()>>28)&0xf); psr_vers = ((get_psr()>>24)&0xf); + psr = get_psr(); + put_psr(psr | PSR_EF); fpu_vers = ((get_fsr()>>17)&0x7); + put_psr(psr); for(i = 0; i<NSPARCCHIPS; i++) { if(linux_sparc_chips[i].psr_impl == psr_impl) diff --git a/arch/sparc/kernel/devices.c b/arch/sparc/kernel/devices.c index b9c6495cf..dd4dbb3c6 100644 --- a/arch/sparc/kernel/devices.c +++ b/arch/sparc/kernel/devices.c @@ -14,7 +14,7 @@ #include <asm/smp.h> #include <asm/system.h> -struct prom_cpuinfo linux_cpus[NCPUS]; +struct prom_cpuinfo linux_cpus[NR_CPUS]; int linux_num_cpus; extern void cpu_probe(void); @@ -26,7 +26,7 @@ device_scan(unsigned long mem_start)) { char node_str[128]; int nd, prom_node_cpu, thismid; - int cpu_nds[NCPUS]; /* One node for each cpu */ + int cpu_nds[NR_CPUS]; /* One node for each cpu */ int cpu_ctr = 0; prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str)); @@ -62,11 +62,9 @@ device_scan(unsigned long mem_start)) prom_getstring(node, "device_type", node_str, sizeof(node_str)); if (strcmp(node_str, "cpu") == 0) { prom_getproperty(node, "cpu-id", (char *) &thismid, sizeof(thismid)); - if (cpu_ctr < NCPUS) { - cpu_nds[cpu_ctr] = node; - linux_cpus[cpu_ctr].prom_node = node; - linux_cpus[cpu_ctr].mid = thismid; - } + cpu_nds[cpu_ctr] = node; + linux_cpus[cpu_ctr].prom_node = node; + linux_cpus[cpu_ctr].mid = thismid; prom_printf("Found CPU %d <node=%08lx,mid=%d>\n", cpu_ctr, (unsigned long) node, thismid); @@ -74,8 +72,6 @@ device_scan(unsigned long mem_start)) } } } - if (cpu_ctr > NCPUS) - cpu_ctr = NCPUS; } if(cpu_ctr == 0) { printk("No CPU nodes found, cannot continue.\n"); @@ -99,7 +95,7 @@ device_scan(unsigned long mem_start)) #endif clock_stop_probe(); - if (sparc_cpu_model == sun4c) + if (ARCH_SUN4C_SUN4) sun4c_probe_memerr_reg(); return mem_start; diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index d82c098d5..d393a9543 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1,10 +1,11 @@ -/* $Id: entry.S,v 1.142 1998/01/07 06:33:47 baccala Exp $ +/* $Id: entry.S,v 1.149 1998/03/19 15:36:30 jj Exp $ * arch/sparc/kernel/entry.S: Sparc trap low-level entry points. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) - * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au) */ #include <linux/config.h> @@ -21,9 +22,15 @@ #include <asm/vaddrs.h> #include <asm/memreg.h> #include <asm/page.h> +#ifdef CONFIG_SUN4 +#include <asm/pgtsun4.h> +#else #include <asm/pgtsun4c.h> +#endif #include <asm/winmacro.h> #include <asm/signal.h> +#include <asm/obio.h> +#include <asm/mxcc.h> #include <asm/asmmacro.h> @@ -288,10 +295,14 @@ real_irq_entry: SAVE_ALL #ifdef __SMP__ + .globl patchme_maybe_smp_msg + cmp %l7, 12 - bgu maybe_smp_msg +patchme_maybe_smp_msg: + bgu maybe_smp4m_msg nop #endif + real_irq_continue: or %l0, PSR_PIL, %g2 wr %g2, 0x0, %psr @@ -309,14 +320,14 @@ patch_handler_irq: #ifdef __SMP__ /* SMP per-cpu ticker interrupts are handled specially. */ -smp_ticker: - bne real_irq_continue +smp4m_ticker: + bne real_irq_continue+4 or %l0, PSR_PIL, %g2 wr %g2, 0x0, %psr WRITE_PAUSE wr %g2, PSR_ET, %psr WRITE_PAUSE - call C_LABEL(smp_percpu_timer_interrupt) + call C_LABEL(smp4m_percpu_timer_interrupt) add %sp, REGWIN_SZ, %o0 wr %l0, PSR_ET, %psr WRITE_PAUSE @@ -326,7 +337,7 @@ smp_ticker: * on some level other than 15 which is the NMI and only used * for cross calls. That has a seperate entry point below. */ -maybe_smp_msg: +maybe_smp4m_msg: GET_PROCESSOR_MID(o3, o2) set C_LABEL(sun4m_interrupts), %l5 ld [%l5], %o5 @@ -334,7 +345,7 @@ maybe_smp_msg: sll %o3, 12, %o3 ld [%o5 + %o3], %o1 andcc %o1, %o4, %g0 - be,a smp_ticker + be,a smp4m_ticker cmp %l7, 14 cmp %l7, 13 add %o5, %o3, %o5 @@ -383,7 +394,7 @@ linux_trap_ipi15_sun4m: WRITE_PAUSE wr %l4, PSR_ET, %psr WRITE_PAUSE - call C_LABEL(smp_cross_call_irq) + call C_LABEL(smp4m_cross_call_irq) nop b ret_trap_lockless_ipi clr %l6 @@ -409,6 +420,64 @@ linux_trap_ipi15_sun4m: ld [%l5], %g0 WRITE_PAUSE RESTORE_ALL + + .globl smp4d_ticker + /* SMP per-cpu ticker interrupts are handled specially. */ +smp4d_ticker: + SAVE_ALL + or %l0, PSR_PIL, %g2 + sethi %hi(CC_ICLR), %o0 + sethi %hi(1 << 14), %o1 + or %o0, %lo(CC_ICLR), %o0 + stha %o1, [%o0] ASI_M_MXCC /* Clear PIL 14 in MXCC's ICLR */ + wr %g2, 0x0, %psr + WRITE_PAUSE + wr %g2, PSR_ET, %psr + WRITE_PAUSE + call C_LABEL(smp4d_percpu_timer_interrupt) + add %sp, REGWIN_SZ, %o0 + wr %l0, PSR_ET, %psr + WRITE_PAUSE + RESTORE_ALL + + .align 4 + .globl linux_trap_ipi15_sun4d +linux_trap_ipi15_sun4d: + SAVE_ALL + sethi %hi(CC_BASE), %o4 + sethi %hi(MXCC_ERR_ME|MXCC_ERR_PEW|MXCC_ERR_ASE|MXCC_ERR_PEE), %o2 + or %o4, (CC_EREG - CC_BASE), %o0 + ldda [%o0] ASI_M_MXCC, %o0 + andcc %o0, %o2, %g0 + bne 1f + sethi %hi(BB_STAT2), %o2 + lduba [%o2] ASI_M_CTL, %o2 + andcc %o2, BB_STAT2_MASK, %g0 + bne 2f + or %o4, (CC_ICLR - CC_BASE), %o0 + sethi %hi(1 << 15), %o1 + stha %o1, [%o0] ASI_M_MXCC /* Clear PIL 15 in MXCC's ICLR */ + or %l0, PSR_PIL, %l4 + wr %l4, 0x0, %psr + WRITE_PAUSE + wr %l4, PSR_ET, %psr + WRITE_PAUSE + call C_LABEL(smp4d_cross_call_irq) + nop + b ret_trap_lockless_ipi + clr %l6 + +1: /* MXCC error */ +2: /* BB error */ + /* Disable PIL 15 */ + set CC_IMSK, %l4 + lduha [%l4] ASI_M_MXCC, %l5 + sethi %hi(1 << 15), %l7 + or %l5, %l7, %l5 + stha %l5, [%l4] ASI_M_MXCC + /* FIXME */ +1: b,a 1b + #endif /* __SMP__ */ /* This routine handles illegal instructions and privileged @@ -417,6 +486,12 @@ linux_trap_ipi15_sun4m: .align 4 .globl bad_instruction bad_instruction: + sethi %hi(0xc1f80000), %l4 + ld [%l1], %l5 + sethi %hi(0x81d80000), %l7 + and %l5, %l4, %l5 + cmp %l5, %l7 + be 1f SAVE_ALL wr %l0, PSR_ET, %psr ! re-enable traps @@ -430,6 +505,10 @@ bad_instruction: RESTORE_ALL +1: /* unimplemented flush - just skip */ + jmpl %l2, %g0 + rett %l2 + 4 + .align 4 .globl priv_instruction priv_instruction: @@ -601,23 +680,6 @@ do_cp_disabled: RESTORE_ALL - /* This routine handles Unimplemented FLUSH Exceptions. */ - .align 4 - .globl do_bad_flush -do_bad_flush: - SAVE_ALL - - wr %l0, PSR_ET, %psr ! re-enable traps - WRITE_PAUSE - - add %sp, REGWIN_SZ, %o0 - mov %l1, %o1 - mov %l2, %o2 - call C_LABEL(handle_bad_flush) - mov %l0, %o3 - - RESTORE_ALL - /* This routine handles Co-Processor Exceptions. */ .align 4 .globl do_cp_exception @@ -766,6 +828,12 @@ C_LABEL(invalid_segment_patch1_ff): cmp %l4, 0xff C_LABEL(invalid_segment_patch2_ff): mov 0xff, %l4 .align 4 + .globl C_LABEL(invalid_segment_patch1_1ff) + .globl C_LABEL(invalid_segment_patch2_1ff) +C_LABEL(invalid_segment_patch1_1ff): cmp %l4, 0x1ff +C_LABEL(invalid_segment_patch2_1ff): mov 0x1ff, %l4 + + .align 4 .globl C_LABEL(num_context_patch1_16), C_LABEL(num_context_patch2_16) C_LABEL(num_context_patch1_16): mov 0x10, %l7 C_LABEL(num_context_patch2_16): mov 0x10, %l7 @@ -776,7 +844,17 @@ C_LABEL(vac_linesize_patch_32): subcc %l7, 32, %l7 .align 4 .globl C_LABEL(vac_hwflush_patch1_on), C_LABEL(vac_hwflush_patch2_on) + +/* + * Ugly, but we cant use hardware flushing on the sun4 and we'd require + * two instructions (Anton) + */ +#ifdef CONFIG_SUN4 +C_LABEL(vac_hwflush_patch1_on): nop +#else C_LABEL(vac_hwflush_patch1_on): subcc %l7, (PAGE_SIZE - 4), %l7 +#endif + C_LABEL(vac_hwflush_patch2_on): sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG .globl C_LABEL(invalid_segment_patch1), C_LABEL(invalid_segment_patch2) @@ -786,11 +864,50 @@ C_LABEL(vac_hwflush_patch2_on): sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG .align 4 .globl sun4c_fault + +! %l0 = %psr +! %l1 = %pc +! %l2 = %npc +! %l3 = %wim +! %l7 = 1 for textfault +! We want error in %l5, vaddr in %l6 sun4c_fault: +#ifdef CONFIG_SUN4 + sethi C_LABEL(sun4c_memerr_reg), %l4 + ld [%l4+%lo(C_LABEL(sun4c_memerr_reg))], %l4 ! memerr ctrl reg addr + ld [%l4], %l6 ! memerr ctrl reg + ld [%l4 + 4], %l5 ! memerr vaddr reg + andcc %l6, 0x80, %g0 ! check for error type + st %g0, [%l4 + 4] ! clear the error + be 0f ! normal error + sethi %hi(AC_BUS_ERROR), %l4 ! bus err reg addr + + call C_LABEL(prom_halt) ! something weird happened + ! what exactly did happen? + ! what should we do here? + +0: or %l4, %lo(AC_BUS_ERROR), %l4 ! bus err reg addr + lduba [%l4] ASI_CONTROL, %l6 ! bus err reg + + cmp %l7, 1 ! text fault? + be 1f ! yes + nop + + ld [%l1], %l4 ! load instruction that caused fault + srl %l4, 21, %l4 + andcc %l4, 1, %g0 ! store instruction? + + be 1f ! no + sethi %hi(SUN4C_SYNC_BADWRITE), %l4 ! yep + ! %lo(SUN4C_SYNC_BADWRITE) = 0 + or %l4, %l6, %l6 ! set write bit to emulate sun4c +1: +#else sethi %hi(AC_SYNC_ERR), %l4 add %l4, 0x4, %l6 ! AC_SYNC_VA in %l6 lda [%l6] ASI_CONTROL, %l5 ! Address lda [%l4] ASI_CONTROL, %l6 ! Error, retained for a bit +#endif andn %l5, 0xfff, %l5 ! Encode all info into l7 srl %l6, 14, %l4 @@ -830,17 +947,21 @@ sun4c_fault: sethi %hi(SUN4C_VMALLOC_START), %l4 cmp %l5, %l4 blu,a C_LABEL(invalid_segment_patch1) - lduba [%l5] ASI_SEGMAP, %l4 + lduXa [%l5] ASI_SEGMAP, %l4 - srl %l5, SUN4C_PGDIR_SHIFT, %l6 sethi %hi(C_LABEL(swapper_pg_dir)), %l4 + srl %l5, SUN4C_PGDIR_SHIFT, %l6 or %l4, %lo(C_LABEL(swapper_pg_dir)), %l4 sll %l6, 2, %l6 ld [%l4 + %l6], %l4 +#ifdef CONFIG_SUN4 + sethi PAGE_MASK, %l6 + andcc %l4, %l6, %g0 +#else andcc %l4, PAGE_MASK, %g0 - +#endif be sun4c_fault_fromuser - lduba [%l5] ASI_SEGMAP, %l4 + lduXa [%l5] ASI_SEGMAP, %l4 C_LABEL(invalid_segment_patch1): cmp %l4, 0x7f @@ -889,7 +1010,11 @@ C_LABEL(invalid_segment_patch1): ld [%l6 + 0x08], %l3 ! tmp = entry->vaddr ! Flush segment from the cache. +#ifdef CONFIG_SUN4 + sethi %hi((128 * 1024)), %l7 +#else sethi %hi((64 * 1024)), %l7 +#endif 9: C_LABEL(vac_hwflush_patch1): C_LABEL(vac_linesize_patch): @@ -928,7 +1053,7 @@ C_LABEL(invalid_segment_patch2): deccc %l7 stba %l7, [%l3] ASI_CONTROL bne 3b - stba %l4, [%l5] ASI_SEGMAP + stXa %l4, [%l5] ASI_SEGMAP stba %l6, [%l3] ASI_CONTROL @@ -952,7 +1077,7 @@ C_LABEL(num_context_patch2): deccc %l7 stba %l7, [%l3] ASI_CONTROL bne 3b - stba %l4, [%l5] ASI_SEGMAP + stXa %l4, [%l5] ASI_SEGMAP stba %l6, [%l3] ASI_CONTROL @@ -988,7 +1113,12 @@ C_LABEL(num_context_patch2): or %l4, %lo(C_LABEL(swapper_pg_dir)), %l4 sll %l3, 2, %l3 ld [%l4 + %l3], %l4 +#ifndef CONFIG_SUN4 and %l4, PAGE_MASK, %l4 +#else + sethi PAGE_MASK, %l6 + and %l4, %l6, %l4 +#endif srl %l5, (PAGE_SHIFT - 2), %l6 and %l6, ((SUN4C_PTRS_PER_PTE - 1) << 2), %l6 @@ -1640,9 +1770,8 @@ C_LABEL(udelay): call .umul ld [%o3 + %lo(C_LABEL(loops_per_sec))], %o1 #else - GET_PROCESSOR_OFFSET(o4) + GET_PROCESSOR_OFFSET(o4, o2) set C_LABEL(cpu_data), %o3 - sll %o4, 1, %o4 call .umul ld [%o3 + %o4], %o1 #endif @@ -1718,4 +1847,11 @@ kuw_patch1: retl ! return st %g0, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] ! no windows saved + .align 4 + .globl C_LABEL(restore_current) +C_LABEL(restore_current): + LOAD_CURRENT(g6, o0) + retl + nop + /* End of entry.S */ diff --git a/arch/sparc/kernel/etrap.S b/arch/sparc/kernel/etrap.S index fc6c4cead..5496b061e 100644 --- a/arch/sparc/kernel/etrap.S +++ b/arch/sparc/kernel/etrap.S @@ -1,4 +1,4 @@ -/* $Id: etrap.S,v 1.26 1997/05/01 08:53:32 davem Exp $ +/* $Id: etrap.S,v 1.29 1998/02/09 13:48:40 jj Exp $ * etrap.S: Sparc trap window preparation for entry into the * Linux kernel. * @@ -13,6 +13,7 @@ #include <asm/psr.h> #include <asm/ptrace.h> #include <asm/winmacro.h> +#include <asm/asmmacro.h> /* Registers to not touch at all. */ #define t_psr l0 /* Set by caller */ @@ -126,13 +127,13 @@ tsetup_patch2: jmpl %t_retpc + 0x8, %g0 ! return to caller mov %t_kstack, %sp ! and onto new kernel stack +#define STACK_OFFSET (TASK_UNION_SIZE - (TRACEREG_SZ + REGWIN_SZ)) trap_setup_from_user: /* We can't use %curptr yet. */ LOAD_CURRENT(t_kstack, t_twinmask) - mov 1, %t_twinmask - sll %t_twinmask, (PAGE_SHIFT + 1), %t_twinmask - sub %t_twinmask, (TRACEREG_SZ + REGWIN_SZ), %t_twinmask + sethi %hi(STACK_OFFSET), %t_twinmask + or %t_twinmask, %lo(STACK_OFFSET), %t_twinmask add %t_kstack, %t_twinmask, %t_kstack mov 1, %t_twinmask @@ -141,11 +142,18 @@ trap_setup_from_user: /* Build pt_regs frame. */ STORE_PT_ALL(t_kstack, t_psr, t_pc, t_npc, g2) - /* Clear current->tss.w_saved */ - mov 1, %curptr - sll %curptr, (PAGE_SHIFT + 1), %curptr - sub %curptr, (TRACEREG_SZ + REGWIN_SZ), %curptr +#if 0 + /* If we're sure every task_struct is TASK_UNION_SIZE aligned, + we can speed this up. */ + sethi %hi(STACK_OFFSET), %curptr + or %curptr, %lo(STACK_OFFSET), %curptr sub %t_kstack, %curptr, %curptr +#else + sethi %hi(~(TASK_UNION_SIZE - 1)), %curptr + and %t_kstack, %curptr, %curptr +#endif + + /* Clear current->tss.w_saved */ st %g0, [%curptr + AOFF_task_tss + AOFF_thread_w_saved] /* See if we are in the trap window. */ @@ -269,9 +277,8 @@ tsetup_sun4c_onepage: .globl C_LABEL(tsetup_srmmu_stackchk) C_LABEL(tsetup_srmmu_stackchk): /* Check results of callers andcc %sp, 0x7, %g0 */ - sethi %hi(C_LABEL(page_offset)), %glob_tmp bne trap_setup_user_stack_is_bolixed - ld [%glob_tmp + %lo(C_LABEL(page_offset))], %glob_tmp + GET_PAGE_OFFSET(glob_tmp) cmp %glob_tmp, %sp bleu,a 1f diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S index 2de97e92b..1536a5e55 100644 --- a/arch/sparc/kernel/head.S +++ b/arch/sparc/kernel/head.S @@ -1,10 +1,11 @@ -/* $Id: head.S,v 1.84 1997/11/19 15:12:01 jj Exp $ +/* $Id: head.S,v 1.90 1998/03/24 18:12:05 jj Exp $ * head.S: The initial boot code for the Sparc port of Linux. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995 Peter Zaitcev (Zaitcev@ipmce.su) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 Michael A. Griffith (grif@acm.org) */ #include <linux/version.h> @@ -60,9 +61,16 @@ C_LABEL(cputypvar_sun4m): .asciz "compatible" .align 4 + +#ifndef CONFIG_SUN4 sun4_notsup: - .asciz "Sparc-Linux sun4 support not implemented yet\n\n" + .asciz "Sparc-Linux sun4 needs a specially compiled kernel, turn CONFIG_SUN4 on.\n\n" + .align 4 +#else +sun4cdm_notsup: + .asciz "Kernel compiled with CONFIG_SUN4 cannot run on SUN4C/SUN4M/SUN4D\nTurn CONFIG_SUN4 off.\n\n" .align 4 +#endif sun4e_notsup: .asciz "Sparc-Linux sun4e support does not exist\n\n" @@ -111,13 +119,14 @@ t_irq14:TRAP_ENTRY_INTERRUPT(14) /* IRQ Timer #2 */ #ifndef __SMP__ t_nmi: NMI_TRAP /* Level 15 (NMI) */ #else - TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m) + .globl t_nmi +t_nmi: TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m) #endif t_racc: TRAP_ENTRY(0x20, do_reg_access) /* General Register Access Error */ t_iacce:BAD_TRAP(0x21) /* Instr Access Error */ t_bad22:BAD_TRAP(0x22) BAD_TRAP(0x23) t_cpdis:TRAP_ENTRY(0x24, do_cp_disabled) /* Co-Processor Disabled */ -t_uflsh:TRAP_ENTRY(0x25, do_bad_flush) /* Unimplemented FLUSH inst. */ +t_uflsh:SKIP_TRAP(0x25, unimp_flush) /* Unimplemented FLUSH inst. */ t_bad26:BAD_TRAP(0x26) BAD_TRAP(0x27) t_cpexc:TRAP_ENTRY(0x28, do_cp_exception) /* Co-Processor Exception */ t_dacce:SPARC_DFAULT /* Data Access Error */ @@ -205,7 +214,7 @@ C_LABEL(trapbase_cpu1): TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14) TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m) TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22) - BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) TRAP_ENTRY(0x25, do_bad_flush) + BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush) BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception) SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c) BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31) @@ -273,7 +282,7 @@ C_LABEL(trapbase_cpu2): TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14) TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m) TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22) - BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) TRAP_ENTRY(0x25, do_bad_flush) + BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush) BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception) SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c) BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31) @@ -341,7 +350,7 @@ C_LABEL(trapbase_cpu3): TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14) TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m) TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22) - BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) TRAP_ENTRY(0x25, do_bad_flush) + BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush) BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception) SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c) BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31) @@ -394,28 +403,26 @@ C_LABEL(trapbase_cpu3): BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) #endif - .skip 4096 + .align 4096 /* This was the only reasonable way I could think of to properly align * these page-table data structures. */ .globl C_LABEL(bootup_user_stack) - .globl C_LABEL(bootup_kernel_stack) .globl C_LABEL(pg0), C_LABEL(pg1), C_LABEL(pg2), C_LABEL(pg3) .globl C_LABEL(empty_bad_page) .globl C_LABEL(empty_bad_page_table) .globl C_LABEL(empty_zero_page) .globl C_LABEL(swapper_pg_dir) C_LABEL(bootup_user_stack): .skip 0x2000 -C_LABEL(bootup_kernel_stack): .skip 0x2000 -C_LABEL(swapper_pg_dir): .skip 0x1000 -C_LABEL(pg0): .skip 0x1000 -C_LABEL(pg1): .skip 0x1000 -C_LABEL(pg2): .skip 0x1000 -C_LABEL(pg3): .skip 0x1000 -C_LABEL(empty_bad_page): .skip 0x1000 -C_LABEL(empty_bad_page_table): .skip 0x1000 -C_LABEL(empty_zero_page): .skip 0x1000 +C_LABEL(swapper_pg_dir): .skip PAGE_SIZE +C_LABEL(pg0): .skip PAGE_SIZE +C_LABEL(pg1): .skip PAGE_SIZE +C_LABEL(pg2): .skip PAGE_SIZE +C_LABEL(pg3): .skip PAGE_SIZE +C_LABEL(empty_bad_page): .skip PAGE_SIZE +C_LABEL(empty_bad_page_table): .skip PAGE_SIZE +C_LABEL(empty_zero_page): .skip PAGE_SIZE .global C_LABEL(root_flags) .global C_LABEL(ram_flags) @@ -778,18 +785,24 @@ execute_in_high_mem: * your code. Sun probably still does that because they don't even * trust their own "OpenBoot" specifications. */ - set LOAD_ADDR, %g6 cmp %o0, %g6 ! an old sun4? - be no_sun4_here + be sun4_init nop found_version: - +#ifdef CONFIG_SUN4 +/* For people who try sun4 kernels, even if Configure.help advises them. */ + ld [%g7 + 0x68], %o1 + set sun4cdm_notsup, %o0 + call %o1 + nop + b halt_me + nop +#endif /* Get the machine type via the mysterious romvec node operations. */ - or %g0, %g7, %l1 - add %l1, 0x1c, %l1 + add %g7, 0x1c, %l1 ld [%l1], %l0 ld [%l0], %l0 call %l0 @@ -825,10 +838,11 @@ got_prop: set C_LABEL(cputypval), %o2 ldub [%o2 + 0x4], %l1 - cmp %l1, 'c' ! We already know we are not - be 1f ! on a plain sun4 because of - ! the check for 0x4000 in %o0 - cmp %l1, 'm' ! at start + cmp %l1, ' ' + be 1f + cmp %l1, 'c' + be 1f + cmp %l1, 'm' be 1f cmp %l1, 'd' be 1f @@ -853,6 +867,9 @@ got_prop: b sun4c_continue_boot nop +/* CPUID in bootbus can be found at PA 0xff0140000 */ +#define SUN4D_BOOTBUS_CPUID 0xf0140000 + sun4d_init: /* Need to patch call to handler_irq */ set C_LABEL(patch_handler_irq), %g4 @@ -862,6 +879,21 @@ sun4d_init: srl %g5, 2, %g5 or %g5, %g3, %g5 st %g5, [%g4] + +#ifdef __SMP__ + /* Get our CPU id out of bootbus */ + set SUN4D_BOOTBUS_CPUID, %g3 + lduba [%g3] ASI_M_CTL, %g3 + and %g3, 0xf8, %g3 + srl %g3, 3, %g4 + sta %g4, [%g0] ASI_M_VIKING_TMP1 + sethi %hi(boot_cpu_id), %g5 + stb %g4, [%g5 + %lo(boot_cpu_id)] + sll %g4, 2, %g4 + sethi %hi(boot_cpu_id4), %g5 + stb %g4, [%g5 + %lo(boot_cpu_id4)] +#endif + /* Fall through to sun4m_init */ sun4m_init: @@ -974,7 +1006,8 @@ sun4c_continue_boot: /* I want a kernel stack NOW! */ set C_LABEL(bootup_user_stack), %g1 - add %g1, (PAGE_SIZE - REGWIN_SZ), %sp + set (0x2000 - REGWIN_SZ), %g2 + add %g1, %g2, %sp mov 0, %fp /* And for good luck */ /* Zero out our BSS section. */ @@ -988,10 +1021,16 @@ sun4c_continue_boot: add %o0, 0x1, %o0 /* Initialize the umask value for init_task just in case. - * But first make current_set[0] point to something useful. + * But first make current_set[boot_cpu_id] point to something useful. */ set C_LABEL(init_task_union), %g6 set C_LABEL(current_set), %g2 +#ifdef __SMP__ + sethi %hi(C_LABEL(boot_cpu_id4)), %g3 + ldub [%g3 + %lo(C_LABEL(boot_cpu_id4))], %g3 + st %g6, [%g2] + add %g2, %g3, %g2 +#endif st %g6, [%g2] st %g0, [%g6 + AOFF_task_tss + AOFF_thread_uwinmask] @@ -1114,19 +1153,28 @@ sun4c_continue_boot: call halt_me nop +sun4_init: +#ifdef CONFIG_SUN4 /* There, happy now Adrian? */ + set C_LABEL(cputypval), %o2 ! Let everyone know we + set ' ', %o0 ! are a "sun4 " architecture + stb %o0, [%o2 + 0x4] - /* XXX Fix this... XXX */ -no_sun4_here: - sethi %hi(SUN4_PROM_VECTOR+SUN4_PRINTF), %o1 - ld [%o1 + %lo(SUN4_PROM_VECTOR+SUN4_PRINTF)], %o1 - set sun4_notsup, %o0 - call %o1 + b got_prop nop -1: - ba 1b ! Cannot exit into KMON +#else + sethi %hi(SUN4_PROM_VECTOR+0x84), %o1 + ld [%o1 + %lo(SUN4_PROM_VECTOR+0x84)], %o1 + set sun4_notsup, %o0 + call %o1 /* printf */ nop - + sethi %hi(SUN4_PROM_VECTOR+0xc4), %o1 + ld [%o1 + %lo(SUN4_PROM_VECTOR+0xc4)], %o1 + call %o1 /* exittomon */ + nop +1: ba 1b ! Cannot exit into KMON + nop +#endif no_sun4e_here: ld [%g7 + 0x68], %o1 set sun4e_notsup, %o0 diff --git a/arch/sparc/kernel/init_task.c b/arch/sparc/kernel/init_task.c index 1829daeea..506a98622 100644 --- a/arch/sparc/kernel/init_task.c +++ b/arch/sparc/kernel/init_task.c @@ -11,9 +11,9 @@ static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; -/* .text section in head.S is aligned at 2 page boundry and this gets linked +/* .text section in head.S is aligned at 8k boundry and this gets linked * right after that so that the init_task_union is aligned properly as well. - * We really don't need this special alignment like the Intel does, but - * I do it anyways for completeness. + * If this is not aligned on a 8k boundry, then you should change code + * in etrap.S which assumes it. */ union task_union init_task_union __attribute__((__section__(".text"))) = { INIT_TASK }; diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c index 08c0be5c6..b29eca496 100644 --- a/arch/sparc/kernel/irq.c +++ b/arch/sparc/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.77 1997/11/19 15:33:05 jj Exp $ +/* $Id: irq.c,v 1.85 1998/03/09 14:03:40 jj Exp $ * arch/sparc/kernel/irq.c: Interrupt request handling routines. On the * Sparc the IRQ's are basically 'cast in stone' * and you are supposed to probe the prom's device @@ -67,22 +67,9 @@ static void irq_panic(void) prom_halt(); } -void (*enable_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic; -void (*disable_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic; -void (*enable_pil_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic; -void (*disable_pil_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic; -void (*clear_clock_irq)(void) = irq_panic; -void (*clear_profile_irq)(int) = (void (*)(int)) irq_panic; -void (*load_profile_irq)(int, unsigned int) = (void (*)(int, unsigned int)) irq_panic; void (*init_timers)(void (*)(int, void *,struct pt_regs *)) = (void (*)(void (*)(int, void *,struct pt_regs *))) irq_panic; -#ifdef __SMP__ -void (*set_cpu_int)(int, int); -void (*clear_cpu_int)(int, int); -void (*set_irq_udt)(int); -#endif - /* * Dave Redman (djhr@tadpole.co.uk) * @@ -109,6 +96,9 @@ int get_irq_list(char *buf) { int i, len = 0; struct irqaction * action; +#ifdef __SMP__ + int j; +#endif if (sparc_cpu_model == sun4d) { extern int sun4d_get_irq_list(char *); @@ -119,8 +109,15 @@ int get_irq_list(char *buf) action = *(i + irq_action); if (!action) continue; - len += sprintf(buf+len, "%2d: %8d %c %s", - i, kstat.interrupts[i], + len += sprintf(buf+len, "%3d: ", i); +#ifndef __SMP__ + len += sprintf(buf+len, "%10u ", kstat_irqs(i)); +#else + for (j = 0; j < smp_num_cpus; j++) + len += sprintf(buf+len, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#endif + len += sprintf(buf+len, " %c %s", (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -280,7 +277,7 @@ static inline void get_irqlock(int cpu, unsigned long where) do { STUCK; barrier(); - } while (*((unsigned char *)&global_irq_lock)); + } while (*((volatile unsigned char *)&global_irq_lock)); } while (!spin_trylock(&global_irq_lock)); } /* @@ -352,7 +349,7 @@ void irq_enter(int cpu, int irq, void *_opaque) hardirq_enter(cpu); barrier(); - while (*((unsigned char *)&global_irq_lock)) { + while (*((volatile unsigned char *)&global_irq_lock)) { if ((unsigned char) cpu == global_irq_holder) { struct pt_regs *regs = _opaque; int sbh_cnt = atomic_read(&__sparc_bh_counter); @@ -436,18 +433,20 @@ void handler_irq(int irq, struct pt_regs * regs) struct irqaction * action; int cpu = smp_processor_id(); #ifdef __SMP__ - extern void smp_irq_rotate(int cpu); + extern void smp4m_irq_rotate(int cpu); #endif - + disable_pil_irq(irq); +#if 0 /* FIXME: rotating IRQs halts the machine during SCSI probe. -ecd */ #ifdef __SMP__ /* Only rotate on lower priority IRQ's (scsi, ethernet, etc.). */ if(irq < 10) - smp_irq_rotate(cpu); + smp4m_irq_rotate(cpu); +#endif #endif irq_enter(cpu, irq, regs); action = *(irq + irq_action); - kstat.interrupts[irq]++; + kstat.irqs[cpu][irq]++; do { if (!action || !action->handler) unexpected_irq(irq, 0, regs); @@ -467,6 +466,7 @@ void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs) disable_pil_irq(irq); irq_enter(cpu, irq, regs); + kstat.irqs[cpu][irq]++; floppy_interrupt(irq, dev_id, regs); irq_exit(cpu, irq); enable_pil_irq(irq); @@ -667,6 +667,7 @@ __initfunc(void init_IRQ(void)) switch(sparc_cpu_model) { case sun4c: + case sun4: sun4c_init_IRQ(); break; @@ -688,4 +689,5 @@ __initfunc(void init_IRQ(void)) prom_printf("Cannot initialize IRQ's on this Sun machine..."); break; } + btfixup(); } diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 32feff3de..8d4c29e62 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.102 1997/12/01 03:36:31 davem Exp $ +/* $Id: process.c,v 1.110 1998/04/08 16:15:51 jj Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -40,6 +40,7 @@ #include <asm/elf.h> extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *); +extern void srmmu_check_pgt_cache(void); struct task_struct *current_set[NR_CPUS] = {&init_task, }; @@ -62,7 +63,7 @@ asmlinkage int sys_idle(void) current->priority = -100; current->counter = -100; for (;;) { - if (sparc_cpu_model == sun4c) { + if (ARCH_SUN4C_SUN4) { static int count = HZ; static unsigned long last_jiffies = 0; static unsigned long last_faults = 0; @@ -91,7 +92,9 @@ asmlinkage int sys_idle(void) } } restore_flags(flags); - } + check_pgt_cache(); + } else + srmmu_check_pgt_cache(); schedule(); } ret = 0; @@ -109,6 +112,7 @@ int cpu_idle(void *unused) current->priority = -100; while(1) { + srmmu_check_pgt_cache(); /* * tq_scheduler currently assumes we're running in a process * context (ie that we hold the kernel lock..) @@ -187,12 +191,12 @@ void machine_power_off(void) void show_regwindow(struct reg_window *rw) { - printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n" + printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx " "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n", rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3], rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]); - printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n" - "i4: %08lx i5: %08lx i6: %08lx i7: %08lx\n", + printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx " + "i4: %08lx i5: %08lx fp: %08lx i7: %08lx\n", rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3], rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); } @@ -201,15 +205,13 @@ void show_regwindow(struct reg_window *rw) static spinlock_t sparc_backtrace_lock = SPIN_LOCK_UNLOCKED; #endif -void show_backtrace(void) +void __show_backtrace(unsigned long fp) { struct reg_window *rw; unsigned long flags; - unsigned long fp; int cpu = smp_processor_id(); spin_lock_irqsave(&sparc_backtrace_lock, flags); - __asm__ __volatile__("mov %%i6, %0" : "=r" (fp)); rw = (struct reg_window *) fp; while(rw) { printk("CPU[%d]: ARGS[%08lx,%08lx,%08lx,%08lx,%08lx,%08lx] " @@ -223,6 +225,31 @@ void show_backtrace(void) spin_unlock_irqrestore(&sparc_backtrace_lock, flags); } +void show_backtrace(void) +{ + unsigned long fp; + + __asm__ __volatile__( + "save %%sp, -64, %%sp\n\t" + "save %%sp, -64, %%sp\n\t" + "save %%sp, -64, %%sp\n\t" + "save %%sp, -64, %%sp\n\t" + "save %%sp, -64, %%sp\n\t" + "save %%sp, -64, %%sp\n\t" + "save %%sp, -64, %%sp\n\t" + "save %%sp, -64, %%sp\n\t" + "restore\n\t" + "restore\n\t" + "restore\n\t" + "restore\n\t" + "restore\n\t" + "restore\n\t" + "restore\n\t" + "restore\n\t" + "mov %%i6, %0" : "=r" (fp)); + __show_backtrace(fp); +} + #ifdef __SMP__ void smp_show_backtrace_all_cpus(void) { @@ -236,15 +263,15 @@ void show_stackframe(struct sparc_stackf *sf) unsigned long *stk; int i; - printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n" + printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx " "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n", sf->locals[0], sf->locals[1], sf->locals[2], sf->locals[3], sf->locals[4], sf->locals[5], sf->locals[6], sf->locals[7]); - printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n" - "i4: %08lx i5: %08lx fp: %08lx ret_pc: %08lx\n", + printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx " + "i4: %08lx i5: %08lx fp: %08lx i7: %08lx\n", sf->ins[0], sf->ins[1], sf->ins[2], sf->ins[3], sf->ins[4], sf->ins[5], (unsigned long)sf->fp, sf->callers_pc); - printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx\n" + printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx " "x3: %08lx x4: %08lx x5: %08lx xx: %08lx\n", (unsigned long)sf->structptr, sf->xargs[0], sf->xargs[1], sf->xargs[2], sf->xargs[3], sf->xargs[4], sf->xargs[5], @@ -265,36 +292,32 @@ void show_regs(struct pt_regs * regs) #endif printk("PSR: %08lx PC: %08lx NPC: %08lx Y: %08lx\n", regs->psr, regs->pc, regs->npc, regs->y); - printk("g0: %08lx g1: %08lx g2: %08lx g3: %08lx\n", + printk("g0: %08lx g1: %08lx g2: %08lx g3: %08lx ", regs->u_regs[0], regs->u_regs[1], regs->u_regs[2], regs->u_regs[3]); printk("g4: %08lx g5: %08lx g6: %08lx g7: %08lx\n", regs->u_regs[4], regs->u_regs[5], regs->u_regs[6], regs->u_regs[7]); - printk("o0: %08lx o1: %08lx o2: %08lx o3: %08lx\n", + printk("o0: %08lx o1: %08lx o2: %08lx o3: %08lx ", regs->u_regs[8], regs->u_regs[9], regs->u_regs[10], regs->u_regs[11]); - printk("o4: %08lx o5: %08lx sp: %08lx ret_pc: %08lx\n", + printk("o4: %08lx o5: %08lx sp: %08lx o7: %08lx\n", regs->u_regs[12], regs->u_regs[13], regs->u_regs[14], regs->u_regs[15]); show_regwindow((struct reg_window *)regs->u_regs[14]); } +#if NOTUSED void show_thread(struct thread_struct *tss) { int i; - printk("uwinmask: 0x%08lx\n", tss->uwinmask); - printk("kregs: 0x%08lx\n", (unsigned long)tss->kregs); + printk("uwinmask: 0x%08lx kregs: 0x%08lx\n", tss->uwinmask, (unsigned long)tss->kregs); show_regs(tss->kregs); - printk("sig_address: 0x%08lx\n", tss->sig_address); - printk("sig_desc: 0x%08lx\n", tss->sig_desc); - printk("ksp: 0x%08lx\n", tss->ksp); - printk("kpc: 0x%08lx\n", tss->kpc); - printk("kpsr: 0x%08lx\n", tss->kpsr); - printk("kwim: 0x%08lx\n", tss->kwim); - printk("fork_kpsr: 0x%08lx\n", tss->fork_kpsr); - printk("fork_kwim: 0x%08lx\n", tss->fork_kwim); + printk("sig_address: 0x%08lx sig_desc: 0x%08lx\n", tss->sig_address, tss->sig_desc); + printk("ksp: 0x%08lx kpc: 0x%08lx\n", tss->ksp, tss->kpc); + printk("kpsr: 0x%08lx kwim: 0x%08lx\n", tss->kpsr, tss->kwim); + printk("fork_kpsr: 0x%08lx fork_kwim: 0x%08lx\n", tss->fork_kpsr, tss->fork_kwim); for (i = 0; i < NSWINS; i++) { if (!tss->rwbuf_stkptrs[i]) @@ -306,19 +329,19 @@ void show_thread(struct thread_struct *tss) printk("w_saved: 0x%08lx\n", tss->w_saved); /* XXX missing: float_regs */ - printk("fsr: 0x%08lx\n", tss->fsr); - printk("fpqdepth: 0x%08lx\n", tss->fpqdepth); + printk("fsr: 0x%08lx fpqdepth: 0x%08lx\n", tss->fsr, tss->fpqdepth); /* XXX missing: fpqueue */ - printk("sstk_info.stack: 0x%08lx\n", - (unsigned long)tss->sstk_info.the_stack); - printk("sstk_info.status: 0x%08lx\n", - (unsigned long)tss->sstk_info.cur_status); - printk("flags: 0x%08lx\n", tss->flags); - printk("current_ds: 0x%08x\n", tss->current_ds); + printk("sstk_info.stack: 0x%08lx sstk_info.status: 0x%08lx\n", + (unsigned long)tss->sstk_info.the_stack, + (unsigned long)tss->sstk_info.cur_status); + printk("flags: 0x%08lx current_ds: 0x%08lx\n", tss->flags, tss->current_ds.seg); + + show_regwindow((struct reg_window *)tss->ksp); /* XXX missing: core_exec */ } +#endif /* * Free current thread data structures etc.. @@ -367,8 +390,11 @@ void flush_thread(void) } /* Now, this task is no longer a kernel thread. */ - current->tss.flags &= ~SPARC_FLAG_KTHREAD; current->tss.current_ds = USER_DS; + if (current->tss.flags & SPARC_FLAG_KTHREAD) { + current->tss.flags &= ~SPARC_FLAG_KTHREAD; + switch_to_context(current); + } } static __inline__ void copy_regs(struct pt_regs *dst, struct pt_regs *src) @@ -475,7 +501,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, } /* Calculate offset to stack_frame & pt_regs */ - stack_offset = ((PAGE_SIZE<<1) - TRACEREG_SZ); + stack_offset = TASK_UNION_SIZE - TRACEREG_SZ; if(regs->psr & PSR_PS) stack_offset -= REGWIN_SZ; diff --git a/arch/sparc/kernel/rtrap.S b/arch/sparc/kernel/rtrap.S index 68f3dc9af..3c0d311ba 100644 --- a/arch/sparc/kernel/rtrap.S +++ b/arch/sparc/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.49 1997/12/14 23:24:24 ecd Exp $ +/* $Id: rtrap.S,v 1.50 1998/02/05 14:18:43 jj Exp $ * rtrap.S: Return from Sparc trap low-level code. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -296,9 +296,8 @@ sun4c_rett_onepage: .globl C_LABEL(srmmu_rett_stackchk) C_LABEL(srmmu_rett_stackchk): - sethi %hi(C_LABEL(page_offset)), %g1 bne ret_trap_user_stack_is_bolixed - ld [%g1 + %lo(C_LABEL(page_offset))], %g1 + GET_PAGE_OFFSET(g1) cmp %g1, %fp bleu ret_trap_user_stack_is_bolixed mov AC_M_SFSR, %g1 diff --git a/arch/sparc/kernel/sclow.S b/arch/sparc/kernel/sclow.S index 06c028395..4da88bd0e 100644 --- a/arch/sparc/kernel/sclow.S +++ b/arch/sparc/kernel/sclow.S @@ -11,6 +11,7 @@ #include <asm/errno.h> #include <asm/winmacro.h> #include <asm/psr.h> +#include <asm/page.h> #define CC_AND_RETT \ set PSR_C, %l4; \ @@ -94,7 +95,7 @@ LABEL(sunossmask): .globl LABEL(getpagesize) LABEL(getpagesize): - set 4096, %i0 + set PAGE_SIZE, %i0 CC_AND_RETT /* XXX sys_nice() XXX */ diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index e53c343da..b5625cd12 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.87 1997/12/18 02:42:42 ecd Exp $ +/* $Id: setup.c,v 1.93 1998/03/09 14:03:18 jj Exp $ * linux/arch/sparc/kernel/setup.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -43,6 +43,7 @@ #include <asm/spinlock.h> #include <asm/softirq.h> #include <asm/hardirq.h> +#include <asm/machines.h> struct screen_info screen_info = { 0, 0, /* orig-x, orig-y */ @@ -58,11 +59,6 @@ struct screen_info screen_info = { unsigned int phys_bytes_of_ram, end_of_phys_memory; -unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) -{ - return memory_start; -} - /* Typing sync at the prom prompt calls the function pointed to by * romvec->pv_synchook which I set to the following function. * This should sync all filesystems and return, for now it just @@ -127,7 +123,7 @@ unsigned int boot_flags; extern char *console_fb_path; static int console_fb = 0; #endif -static unsigned long memory_size = 0; +static unsigned long memory_size __initdata = 0; void kernel_enter_debugger(void) { @@ -260,7 +256,7 @@ extern void sun4c_probe_vac(void); extern char cputypval; extern unsigned long start, end; extern void panic_setup(char *, int *); -extern unsigned long srmmu_endmem_fixup(unsigned long); +extern void srmmu_end_memory(unsigned long, unsigned long *); extern unsigned long sun_serial_setup(unsigned long); extern unsigned short root_flags; @@ -311,6 +307,13 @@ __initfunc(void setup_arch(char **cmdline_p, if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; } if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; } if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; } + +#ifdef CONFIG_SUN4 + if (sparc_cpu_model != sun4) { + prom_printf("This kernel is for Sun4 architecture only.\n"); + prom_halt(); + } +#endif #if CONFIG_AP1000 sparc_cpu_model=ap1000; strcpy(&cputypval, "ap+"); @@ -320,12 +323,10 @@ __initfunc(void setup_arch(char **cmdline_p, switch(sparc_cpu_model) { case sun4: printk("SUN4\n"); - sun4c_probe_vac(); packed = 0; break; case sun4c: printk("SUN4C\n"); - sun4c_probe_vac(); packed = 0; break; case sun4m: @@ -356,6 +357,8 @@ __initfunc(void setup_arch(char **cmdline_p, boot_flags_init(*cmdline_p); idprom_init(); + if (ARCH_SUN4C_SUN4) + sun4c_probe_vac(); load_mmu(); total = prom_probe_memory(); *memory_start_p = (((unsigned long) &end)); @@ -374,41 +377,37 @@ __initfunc(void setup_arch(char **cmdline_p, } } } - } else { - unsigned int sum = 0; + *memory_end_p = (end_of_phys_memory + KERNBASE); + } else + srmmu_end_memory(memory_size, memory_end_p); - for(i = 0; sp_banks[i].num_bytes != 0; i++) { - sum += sp_banks[i].num_bytes; - if (memory_size) { - if (sum > memory_size) { - sp_banks[i].num_bytes -= - (sum - memory_size); - sum = memory_size; - sp_banks[++i].base_addr = 0xdeadbeef; - sp_banks[i].num_bytes = 0; - break; - } - } + if (!root_flags) + root_mountflags &= ~MS_RDONLY; + ROOT_DEV = to_kdev_t(root_dev); +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0); +#endif +#ifdef CONFIG_BLK_DEV_INITRD + if (ramdisk_image) { + initrd_start = ramdisk_image; + if (initrd_start < KERNBASE) initrd_start += KERNBASE; + initrd_end = initrd_start + ramdisk_size; + if (initrd_end > *memory_end_p) { + printk(KERN_CRIT "initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end,*memory_end_p); + initrd_start = 0; + } + if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) { + initrd_below_start_ok = 1; + *memory_start_p = PAGE_ALIGN (initrd_end); } - end_of_phys_memory = sum; } - +#endif prom_setsync(prom_sync_me); - *memory_end_p = (end_of_phys_memory + KERNBASE); - if((sparc_cpu_model == sun4c) || - (sparc_cpu_model == sun4)) - goto not_relevant; - if(end_of_phys_memory >= 0x0d000000) { - *memory_end_p = 0xfd000000; - } else { - if((sparc_cpu_model == sun4m) || - (sparc_cpu_model == sun4d) || - (sparc_cpu_model == ap1000)) - *memory_end_p = srmmu_endmem_fixup(*memory_end_p); - } -not_relevant: - #ifdef CONFIG_SUN_SERIAL *memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */ #endif @@ -459,31 +458,6 @@ not_relevant: breakpoint(); } - if (!root_flags) - root_mountflags &= ~MS_RDONLY; - ROOT_DEV = to_kdev_t(root_dev); -#ifdef CONFIG_BLK_DEV_RAM - rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK; - rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0); - rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0); -#endif -#ifdef CONFIG_BLK_DEV_INITRD - if (ramdisk_image) { - initrd_start = ramdisk_image; - if (initrd_start < KERNBASE) initrd_start += KERNBASE; - initrd_end = initrd_start + ramdisk_size; - if (initrd_end > *memory_end_p) { - printk(KERN_CRIT "initrd extends beyond end of memory " - "(0x%08lx > 0x%08lx)\ndisabling initrd\n", - initrd_end,*memory_end_p); - initrd_start = 0; - } - if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) { - initrd_below_start_ok = 1; - *memory_start_p = PAGE_ALIGN (initrd_end); - } - } -#endif /* Due to stack alignment restrictions and assumptions... */ init_task.mm->mmap->vm_page_prot = PAGE_SHARED; @@ -504,13 +478,12 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) extern char *sparc_cpu_type[]; extern char *sparc_fpu_type[]; -extern char *smp_info(void); - int get_cpuinfo(char *buffer) { - int cpuid=get_cpuid(); + int cpuid=hard_smp_processor_id(); + int len; - return sprintf(buffer, "cpu\t\t: %s\n" + len = sprintf(buffer, "cpu\t\t: %s\n" "fpu\t\t: %s\n" "promlib\t\t: Version %d Revision %d\n" "prom\t\t: %d.%d\n" @@ -519,34 +492,23 @@ int get_cpuinfo(char *buffer) "ncpus active\t: %d\n" #ifndef __SMP__ "BogoMips\t: %lu.%02lu\n" -#else - "Cpu0Bogo\t: %lu.%02lu\n" - "Cpu1Bogo\t: %lu.%02lu\n" - "Cpu2Bogo\t: %lu.%02lu\n" - "Cpu3Bogo\t: %lu.%02lu\n" -#endif - "%s" -#ifdef __SMP__ - "%s" #endif , - sparc_cpu_type[cpuid], - sparc_fpu_type[cpuid], + sparc_cpu_type[cpuid] ? : "undetermined", + sparc_fpu_type[cpuid] ? : "undetermined", romvec->pv_romvers, prom_rev, romvec->pv_printrev >> 16, (short)romvec->pv_printrev, &cputypval, - linux_num_cpus, smp_num_cpus, + linux_num_cpus, smp_num_cpus #ifndef __SMP__ - loops_per_sec/500000, (loops_per_sec/5000) % 100, -#else - cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100, - cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100, - cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100, - cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100, + , loops_per_sec/500000, (loops_per_sec/5000) % 100 #endif - mmu_info() + ); #ifdef __SMP__ - , smp_info() + len += smp_bogo_info(buffer + len); #endif - ); - + len += mmu_info(buffer + len); +#ifdef __SMP__ + len += smp_info(buffer + len); +#endif + return len; } diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c index 5c10faa81..efdb362a4 100644 --- a/arch/sparc/kernel/signal.c +++ b/arch/sparc/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.77 1997/12/22 03:06:32 ecd Exp $ +/* $Id: signal.c,v 1.79 1998/04/04 07:11:41 davem Exp $ * linux/arch/sparc/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -78,9 +78,20 @@ struct new_signal_frame { __siginfo_fpu_t fpu_state; }; +struct rt_signal_frame { + struct sparc_stackf ss; + siginfo_t info; + struct pt_regs regs; + sigset_t mask; + __siginfo_fpu_t *fpu_save; + unsigned int insns [2]; + __siginfo_fpu_t fpu_state; +}; + /* Align macros */ #define SF_ALIGNEDSZ (((sizeof(struct signal_sframe) + 7) & (~7))) #define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame) + 7) & (~7))) +#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7))) /* * atomically swap in the new signal mask, and wait for a signal. @@ -318,7 +329,60 @@ segv_and_exit: asmlinkage void do_rt_sigreturn(struct pt_regs *regs) { - printk("XXX: FIXME: write do_rt_sigreturn\n"); + struct rt_signal_frame *sf; + unsigned int psr, pc, npc; + __siginfo_fpu_t *fpu_save; + sigset_t set; + + synchronize_user_stack(); + sf = (struct rt_signal_frame *) regs->u_regs[UREG_FP]; + if(verify_area(VERIFY_READ, sf, sizeof(*sf)) || + (((unsigned long) sf) & 0x03)) + goto segv; + + get_user(pc, &sf->regs.pc); + __get_user(npc, &sf->regs.npc); + if((pc | npc) & 0x03) + goto segv; + + regs->pc = pc; + regs->npc = npc; + + __get_user(regs->y, &sf->regs.y); + __get_user(psr, &sf->regs.psr); + + __get_user(regs->u_regs[UREG_G1], &sf->regs.u_regs[UREG_G1]); + __get_user(regs->u_regs[UREG_G2], &sf->regs.u_regs[UREG_G2]); + __get_user(regs->u_regs[UREG_G3], &sf->regs.u_regs[UREG_G3]); + __get_user(regs->u_regs[UREG_G4], &sf->regs.u_regs[UREG_G4]); + __get_user(regs->u_regs[UREG_G5], &sf->regs.u_regs[UREG_G5]); + __get_user(regs->u_regs[UREG_G6], &sf->regs.u_regs[UREG_G6]); + __get_user(regs->u_regs[UREG_G7], &sf->regs.u_regs[UREG_G7]); + __get_user(regs->u_regs[UREG_I0], &sf->regs.u_regs[UREG_I0]); + __get_user(regs->u_regs[UREG_I1], &sf->regs.u_regs[UREG_I1]); + __get_user(regs->u_regs[UREG_I2], &sf->regs.u_regs[UREG_I2]); + __get_user(regs->u_regs[UREG_I3], &sf->regs.u_regs[UREG_I3]); + __get_user(regs->u_regs[UREG_I4], &sf->regs.u_regs[UREG_I4]); + __get_user(regs->u_regs[UREG_I5], &sf->regs.u_regs[UREG_I5]); + __get_user(regs->u_regs[UREG_I6], &sf->regs.u_regs[UREG_I6]); + __get_user(regs->u_regs[UREG_I7], &sf->regs.u_regs[UREG_I7]); + + regs->psr = (regs->psr & ~PSR_ICC) | (psr & PSR_ICC); + + __get_user(fpu_save, &sf->fpu_save); + if(fpu_save) + restore_fpu_state(regs, &sf->fpu_state); + if(copy_from_user(&set, &sf->mask, sizeof(sigset_t))) + goto segv; + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + return; +segv: + lock_kernel(); + do_exit(SIGSEGV); } /* Checks if the fp is valid */ @@ -514,7 +578,63 @@ static inline void new_setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, int signo, sigset_t *oldset, siginfo_t *info) { - printk("XXX: FIXME: new_setup_rt_frame unimplemented\n"); + struct rt_signal_frame *sf; + int sigframe_size; + unsigned int psr; + int i; + + synchronize_user_stack(); + sigframe_size = RT_ALIGNEDSZ; + if(!current->used_math) + sigframe_size -= sizeof(__siginfo_fpu_t); + sf = (struct rt_signal_frame *)(regs->u_regs[UREG_FP] - sigframe_size); + if(invalid_frame_pointer(sf, sigframe_size)) + goto sigill; + if(current->tss.w_saved != 0) + goto sigill; + + put_user(regs->pc, &sf->regs.pc); + __put_user(regs->npc, &sf->regs.npc); + __put_user(regs->y, &sf->regs.y); + psr = regs->psr; + if(current->used_math) + psr |= PSR_EF; + __put_user(psr, &sf->regs.psr); + for(i = 0; i < 16; i++) + __put_user(regs->u_regs[i], &sf->regs.u_regs[i]); + if(psr & PSR_EF) { + save_fpu_state(regs, &sf->fpu_state); + __put_user(&sf->fpu_state, &sf->fpu_save); + } else { + __put_user(0, &sf->fpu_save); + } + __copy_to_user(&sf->mask, &oldset->sig[0], sizeof(sigset_t)); + copy_to_user(sf, (char *) regs->u_regs [UREG_FP], + sizeof (struct reg_window)); + + regs->u_regs[UREG_FP] = (unsigned long) sf; + regs->u_regs[UREG_I0] = signo; + regs->u_regs[UREG_I1] = (unsigned long) &sf->info; + + regs->pc = (unsigned long) ka->sa.sa_handler; + regs->npc = (regs->pc + 4); + + if(ka->ka_restorer) + regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + else { + regs->u_regs[UREG_I7] = (unsigned long)(&(sf->insns[0]) - 2); + + __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ + __put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */ + + /* Flush instruction space. */ + flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); + } + return; + +sigill: + lock_kernel(); + do_exit(SIGILL); } /* Setup a Solaris stack frame */ @@ -783,6 +903,7 @@ handle_signal(unsigned long signr, struct k_sigaction *ka, spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked, signr); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); } } diff --git a/arch/sparc/kernel/smp.c b/arch/sparc/kernel/smp.c index 15364d44f..c6d86d36c 100644 --- a/arch/sparc/kernel/smp.c +++ b/arch/sparc/kernel/smp.c @@ -1,9 +1,9 @@ /* smp.c: Sparc SMP support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include <linux/config.h> /* for CONFIG_PROFILE */ #include <asm/head.h> #include <linux/kernel.h> @@ -13,6 +13,7 @@ #include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/kernel_stat.h> +#include <linux/init.h> #include <asm/ptrace.h> #include <asm/atomic.h> @@ -34,34 +35,29 @@ #define IRQ_STOP_CPU 14 #define IRQ_CROSS_CALL 15 -extern ctxd_t *srmmu_ctx_table_phys; -extern int linux_num_cpus; - -extern void calibrate_delay(void); - -/* XXX Let's get rid of this thing if we can... */ -extern struct task_struct *current_set[NR_CPUS]; - volatile int smp_processors_ready = 0; - unsigned long cpu_present_map = 0; int smp_num_cpus = 1; int smp_threads_ready=0; unsigned char mid_xlate[NR_CPUS] = { 0, 0, 0, 0, }; -volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; +volatile unsigned long cpu_callin_map[NR_CPUS] __initdata = {0,}; +#ifdef NOTUSED volatile unsigned long smp_spinning[NR_CPUS] = { 0, }; +#endif unsigned long smp_proc_in_lock[NR_CPUS] = { 0, }; struct cpuinfo_sparc cpu_data[NR_CPUS]; -static unsigned char boot_cpu_id = 0; -static int smp_activated = 0; +unsigned long cpu_offset[NR_CPUS]; +unsigned char boot_cpu_id = 0; +unsigned char boot_cpu_id4 = 0; /* boot_cpu_id << 2 */ +int smp_activated = 0; volatile int cpu_number_map[NR_CPUS]; -volatile int cpu_logical_map[NR_CPUS]; +volatile int __cpu_logical_map[NR_CPUS]; /* The only guaranteed locking primitive available on all Sparc * processors is 'ldstub [%reg + immediate], %dest_reg' which atomically * places the current byte at the effective address into dest_reg and * places 0xff there afterwards. Pretty lame locking primitive - * compared to the Alpha and the intel no? Most Sparcs have 'swap' + * compared to the Alpha and the Intel no? Most Sparcs have 'swap' * instruction which is much better... */ struct klock_info klock_info = { KLOCK_CLEAR, 0 }; @@ -69,42 +65,11 @@ struct klock_info klock_info = { KLOCK_CLEAR, 0 }; volatile unsigned long ipi_count; volatile int smp_process_available=0; - -/*#define SMP_DEBUG*/ - -#ifdef SMP_DEBUG -#define SMP_PRINTK(x) printk x -#else -#define SMP_PRINTK(x) -#endif - volatile int smp_commenced = 0; -static char smp_buf[512]; - /* Not supported on Sparc yet. */ -void smp_setup(char *str, int *ints) -{ -} - -char *smp_info(void) -{ - sprintf(smp_buf, -" CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n" -"State: %s\t\t%s\t\t%s\t\t%s\n", -(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline", -(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline", -(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline", -(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline"); - return smp_buf; -} - -static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val) +__initfunc(void smp_setup(char *str, int *ints)) { - __asm__ __volatile__("swap [%1], %0\n\t" : - "=&r" (val), "=&r" (ptr) : - "0" (val), "1" (ptr)); - return val; } /* @@ -112,12 +77,12 @@ static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val) * a given CPU */ -void smp_store_cpu_info(int id) +__initfunc(void smp_store_cpu_info(int id)) { cpu_data[id].udelay_val = loops_per_sec; /* this is it on sparc. */ } -void smp_commence(void) +__initfunc(void smp_commence(void)) { /* * Lets the callin's below out of their loop. @@ -129,65 +94,19 @@ void smp_commence(void) local_flush_tlb_all(); } -static void smp_setup_percpu_timer(void); - -void smp_callin(void) -{ - int cpuid = hard_smp_processor_id(); - - local_flush_cache_all(); - local_flush_tlb_all(); - set_irq_udt(mid_xlate[boot_cpu_id]); - - /* Get our local ticker going. */ - smp_setup_percpu_timer(); - - calibrate_delay(); - smp_store_cpu_info(cpuid); - local_flush_cache_all(); - local_flush_tlb_all(); - - /* Allow master to continue. */ - swap((unsigned long *)&cpu_callin_map[cpuid], 1); - local_flush_cache_all(); - local_flush_tlb_all(); - - while(!task[cpuid] || current_set[cpuid] != task[cpuid]) - barrier(); - - /* Fix idle thread fields. */ - __asm__ __volatile__("ld [%0], %%g6\n\t" - : : "r" (¤t_set[cpuid]) - : "memory" /* paranoid */); - current->mm->mmap->vm_page_prot = PAGE_SHARED; - current->mm->mmap->vm_start = PAGE_OFFSET; - current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; - - while(!smp_commenced) - barrier(); - - local_flush_cache_all(); - local_flush_tlb_all(); - - __sti(); -} - -extern int cpu_idle(void *unused); -extern void init_IRQ(void); - /* Only broken Intel needs this, thus it should not even be referenced * globally... */ -void initialize_secondary(void) +__initfunc(void initialize_secondary(void)) { } +extern int cpu_idle(void *unused); + /* Activate a secondary processor. */ int start_secondary(void *unused) { - trap_init(); - init_IRQ(); - smp_callin(); + prom_printf("Start secondary called. Should not happen\n"); return cpu_idle(NULL); } @@ -201,255 +120,25 @@ void cpu_panic(void) * Cycle through the processors asking the PROM to start each one. */ -extern struct prom_cpuinfo linux_cpus[NCPUS]; -static struct linux_prom_registers penguin_ctable; +extern struct prom_cpuinfo linux_cpus[NR_CPUS]; +struct linux_prom_registers smp_penguin_ctable __initdata = { 0 }; -void smp_boot_cpus(void) +__initfunc(void smp_boot_cpus(void)) { - int cpucount = 0; - int i = 0; - int first, prev; - - printk("Entering SMP Mode...\n"); - - penguin_ctable.which_io = 0; - penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys; - penguin_ctable.reg_size = 0; - - __sti(); - cpu_present_map = 0; - for(i=0; i < linux_num_cpus; i++) - cpu_present_map |= (1<<i); - for(i=0; i < NR_CPUS; i++) - cpu_number_map[i] = -1; - for(i=0; i < NR_CPUS; i++) - cpu_logical_map[i] = -1; - mid_xlate[boot_cpu_id] = (linux_cpus[boot_cpu_id].mid & ~8); - cpu_number_map[boot_cpu_id] = 0; - cpu_logical_map[0] = boot_cpu_id; - klock_info.akp = boot_cpu_id; - current->processor = boot_cpu_id; - smp_store_cpu_info(boot_cpu_id); - set_irq_udt(mid_xlate[boot_cpu_id]); - smp_setup_percpu_timer(); - local_flush_cache_all(); - if(linux_num_cpus == 1) - return; /* Not an MP box. */ - for(i = 0; i < NR_CPUS; i++) { - if(i == boot_cpu_id) - continue; - - if(cpu_present_map & (1 << i)) { - extern unsigned long sparc_cpu_startup; - unsigned long *entry = &sparc_cpu_startup; - struct task_struct *p; - int timeout; - - /* Cook up an idler for this guy. */ - kernel_thread(start_secondary, NULL, CLONE_PID); - - p = task[++cpucount]; - - p->processor = i; - current_set[i] = p; - - /* See trampoline.S for details... */ - entry += ((i-1) * 3); - - /* whirrr, whirrr, whirrrrrrrrr... */ - printk("Starting CPU %d at %p\n", i, entry); - mid_xlate[i] = (linux_cpus[i].mid & ~8); - local_flush_cache_all(); - prom_startcpu(linux_cpus[i].prom_node, - &penguin_ctable, 0, (char *)entry); - - /* wheee... it's going... */ - for(timeout = 0; timeout < 5000000; timeout++) { - if(cpu_callin_map[i]) - break; - udelay(100); - } - if(cpu_callin_map[i]) { - /* Another "Red Snapper". */ - cpu_number_map[i] = i; - cpu_logical_map[i] = i; - } else { - cpucount--; - printk("Processor %d is stuck.\n", i); - } - } - if(!(cpu_callin_map[i])) { - cpu_present_map &= ~(1 << i); - cpu_number_map[i] = -1; - } - } - local_flush_cache_all(); - if(cpucount == 0) { - printk("Error: only one Processor found.\n"); - cpu_present_map = (1 << smp_processor_id()); - } else { - unsigned long bogosum = 0; - for(i = 0; i < NR_CPUS; i++) { - if(cpu_present_map & (1 << i)) - bogosum += cpu_data[i].udelay_val; - } - printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", - cpucount + 1, - (bogosum + 2500)/500000, - ((bogosum + 2500)/5000)%100); - smp_activated = 1; - smp_num_cpus = cpucount + 1; - } - - /* Setup CPU list for IRQ distribution scheme. */ - first = prev = -1; - for(i = 0; i < NR_CPUS; i++) { - if(cpu_present_map & (1 << i)) { - if(first == -1) - first = i; - if(prev != -1) - cpu_data[i].next = i; - cpu_data[i].mid = mid_xlate[i]; - prev = i; - } - } - cpu_data[prev].next = first; - - /* Ok, they are spinning and ready to go. */ - smp_processors_ready = 1; -} - -/* At each hardware IRQ, we get this called to forward IRQ reception - * to the next processor. The caller must disable the IRQ level being - * serviced globally so that there are no double interrupts received. - */ -void smp_irq_rotate(int cpu) -{ - if(smp_processors_ready) - set_irq_udt(cpu_data[cpu_data[cpu].next].mid); -} - -/* Cross calls, in order to work efficiently and atomically do all - * the message passing work themselves, only stopcpu and reschedule - * messages come through here. - */ -void smp_message_pass(int target, int msg, unsigned long data, int wait) -{ - static unsigned long smp_cpu_in_msg[NR_CPUS]; - unsigned long mask; - int me = smp_processor_id(); - int irq, i; - - if(msg == MSG_RESCHEDULE) { - irq = IRQ_RESCHEDULE; - - if(smp_cpu_in_msg[me]) - return; - } else if(msg == MSG_STOP_CPU) { - irq = IRQ_STOP_CPU; - } else { - goto barf; - } - - smp_cpu_in_msg[me]++; - if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) { - mask = cpu_present_map; - if(target == MSG_ALL_BUT_SELF) - mask &= ~(1 << me); - for(i = 0; i < 4; i++) { - if(mask & (1 << i)) - set_cpu_int(mid_xlate[i], irq); - } - } else { - set_cpu_int(mid_xlate[target], irq); - } - smp_cpu_in_msg[me]--; - - return; -barf: - printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me); - panic("Bogon SMP message pass."); -} - -struct smp_funcall { - smpfunc_t func; - unsigned long arg1; - unsigned long arg2; - unsigned long arg3; - unsigned long arg4; - unsigned long arg5; - unsigned long processors_in[NR_CPUS]; /* Set when ipi entered. */ - unsigned long processors_out[NR_CPUS]; /* Set when ipi exited. */ -} ccall_info; - -static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED; - -/* Cross calls must be serialized, at least currently. */ -void smp_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2, - unsigned long arg3, unsigned long arg4, unsigned long arg5) -{ - if(smp_processors_ready) { - register int ncpus = smp_num_cpus; - unsigned long flags; - - spin_lock_irqsave(&cross_call_lock, flags); - - /* Init function glue. */ - ccall_info.func = func; - ccall_info.arg1 = arg1; - ccall_info.arg2 = arg2; - ccall_info.arg3 = arg3; - ccall_info.arg4 = arg4; - ccall_info.arg5 = arg5; - - /* Init receive/complete mapping, plus fire the IPI's off. */ - { - register void (*send_ipi)(int,int) = set_cpu_int; - register unsigned long mask; - register int i; - - mask = (cpu_present_map & ~(1 << smp_processor_id())); - for(i = 0; i < ncpus; i++) { - if(mask & (1 << i)) { - ccall_info.processors_in[i] = 0; - ccall_info.processors_out[i] = 0; - send_ipi(mid_xlate[i], IRQ_CROSS_CALL); - } else { - ccall_info.processors_in[i] = 1; - ccall_info.processors_out[i] = 1; - } - } - } - - /* First, run local copy. */ - func(arg1, arg2, arg3, arg4, arg5); - - { - register int i; - - i = 0; - do { - while(!ccall_info.processors_in[i]) - barrier(); - } while(++i < ncpus); - - i = 0; - do { - while(!ccall_info.processors_out[i]) - barrier(); - } while(++i < ncpus); - } - - spin_unlock_irqrestore(&cross_call_lock, flags); - } else - func(arg1, arg2, arg3, arg4, arg5); /* Just need to run local copy. */ + extern void smp4m_boot_cpus(void); + extern void smp4d_boot_cpus(void); + + if (sparc_cpu_model == sun4m) + smp4m_boot_cpus(); + else + smp4d_boot_cpus(); } void smp_flush_cache_all(void) -{ xc0((smpfunc_t) local_flush_cache_all); } +{ xc0((smpfunc_t) BTFIXUP_CALL(local_flush_cache_all)); } void smp_flush_tlb_all(void) -{ xc0((smpfunc_t) local_flush_tlb_all); } +{ xc0((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_all)); } void smp_flush_cache_mm(struct mm_struct *mm) { @@ -457,7 +146,7 @@ void smp_flush_cache_mm(struct mm_struct *mm) if(mm->cpu_vm_mask == (1 << smp_processor_id())) local_flush_cache_mm(mm); else - xc1((smpfunc_t) local_flush_cache_mm, (unsigned long) mm); + xc1((smpfunc_t) BTFIXUP_CALL(local_flush_cache_mm), (unsigned long) mm); } } @@ -467,7 +156,7 @@ void smp_flush_tlb_mm(struct mm_struct *mm) if(mm->cpu_vm_mask == (1 << smp_processor_id())) { local_flush_tlb_mm(mm); } else { - xc1((smpfunc_t) local_flush_tlb_mm, (unsigned long) mm); + xc1((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_mm), (unsigned long) mm); if(mm->count == 1 && current->mm == mm) mm->cpu_vm_mask = (1 << smp_processor_id()); } @@ -481,7 +170,7 @@ void smp_flush_cache_range(struct mm_struct *mm, unsigned long start, if(mm->cpu_vm_mask == (1 << smp_processor_id())) local_flush_cache_range(mm, start, end); else - xc3((smpfunc_t) local_flush_cache_range, (unsigned long) mm, + xc3((smpfunc_t) BTFIXUP_CALL(local_flush_cache_range), (unsigned long) mm, start, end); } } @@ -493,7 +182,7 @@ void smp_flush_tlb_range(struct mm_struct *mm, unsigned long start, if(mm->cpu_vm_mask == (1 << smp_processor_id())) local_flush_tlb_range(mm, start, end); else - xc3((smpfunc_t) local_flush_tlb_range, (unsigned long) mm, + xc3((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_range), (unsigned long) mm, start, end); } } @@ -506,7 +195,7 @@ void smp_flush_cache_page(struct vm_area_struct *vma, unsigned long page) if(mm->cpu_vm_mask == (1 << smp_processor_id())) local_flush_cache_page(vma, page); else - xc2((smpfunc_t) local_flush_cache_page, + xc2((smpfunc_t) BTFIXUP_CALL(local_flush_cache_page), (unsigned long) vma, page); } } @@ -519,7 +208,7 @@ void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) if(mm->cpu_vm_mask == (1 << smp_processor_id())) local_flush_tlb_page(vma, page); else - xc2((smpfunc_t) local_flush_tlb_page, (unsigned long) vma, page); + xc2((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_page), (unsigned long) vma, page); } } @@ -532,7 +221,7 @@ void smp_flush_page_to_ram(unsigned long page) * XXX This experiment failed, research further... -DaveM */ #if 1 - xc1((smpfunc_t) local_flush_page_to_ram, page); + xc1((smpfunc_t) BTFIXUP_CALL(local_flush_page_to_ram), page); #else local_flush_page_to_ram(page); #endif @@ -543,7 +232,7 @@ void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr) if(mm->cpu_vm_mask == (1 << smp_processor_id())) local_flush_sig_insns(mm, insn_addr); else - xc2((smpfunc_t) local_flush_sig_insns, (unsigned long) mm, insn_addr); + xc2((smpfunc_t) BTFIXUP_CALL(local_flush_sig_insns), (unsigned long) mm, insn_addr); } /* Reschedule call back. */ @@ -552,17 +241,6 @@ void smp_reschedule_irq(void) need_resched = 1; } -/* Running cross calls. */ -void smp_cross_call_irq(void) -{ - int i = smp_processor_id(); - - ccall_info.processors_in[i] = 1; - ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3, - ccall_info.arg4, ccall_info.arg5); - ccall_info.processors_out[i] = 1; -} - /* Stopping processors. */ void smp_stop_cpu_irq(void) { @@ -571,84 +249,10 @@ void smp_stop_cpu_irq(void) barrier(); } -/* Protects counters touched during level14 ticker */ -spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; - -#ifdef CONFIG_PROFILE - -/* 32-bit Sparc specific profiling function. */ -static inline void sparc_do_profile(unsigned long pc) -{ - if(prof_buffer && current->pid) { - extern int _stext; - - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - - spin_lock(&ticker_lock); - if(pc < prof_len) - prof_buffer[pc]++; - else - prof_buffer[prof_len - 1]++; - spin_unlock(&ticker_lock); - } -} - -#endif - unsigned int prof_multiplier[NR_CPUS]; unsigned int prof_counter[NR_CPUS]; - -extern void update_one_process(struct task_struct *p, unsigned long ticks, - unsigned long user, unsigned long system); - -void smp_percpu_timer_interrupt(struct pt_regs *regs) -{ - int cpu = smp_processor_id(); - - clear_profile_irq(mid_xlate[cpu]); -#ifdef CONFIG_PROFILE - if(!user_mode(regs)) - sparc_do_profile(regs->pc); -#endif - if(!--prof_counter[cpu]) { - int user = user_mode(regs); - if(current->pid) { - update_one_process(current, 1, user, !user); - - if(--current->counter < 0) { - current->counter = 0; - need_resched = 1; - } - - spin_lock(&ticker_lock); - if(user) { - if(current->priority < DEF_PRIORITY) - kstat.cpu_nice++; - else - kstat.cpu_user++; - } else { - kstat.cpu_system++; - } - spin_unlock(&ticker_lock); - } - prof_counter[cpu] = prof_multiplier[cpu]; - } -} - extern unsigned int lvl14_resolution; -static void smp_setup_percpu_timer(void) -{ - int cpu = smp_processor_id(); - - prof_counter[cpu] = prof_multiplier[cpu] = 1; - load_profile_irq(mid_xlate[cpu], lvl14_resolution); - - if(cpu == boot_cpu_id) - enable_pil_irq(14); -} - int setup_profiling_timer(unsigned int multiplier) { int i; diff --git a/arch/sparc/kernel/sparc-stub.c b/arch/sparc/kernel/sparc-stub.c index e259ffade..9426ec0d9 100644 --- a/arch/sparc/kernel/sparc-stub.c +++ b/arch/sparc/kernel/sparc-stub.c @@ -1,4 +1,4 @@ -/* $Id: sparc-stub.c,v 1.22 1998/01/07 06:33:48 baccala Exp $ +/* $Id: sparc-stub.c,v 1.24 1998/02/08 07:58:44 ecd Exp $ * sparc-stub.c: KGDB support for the Linux kernel. * * Modifications to run under Linux @@ -165,9 +165,10 @@ unsigned long get_sun4csegmap(unsigned long addr) return entry; } -static void flush_cache_all_nop(void) -{ -} +#if 0 +/* Have to sort this out. This cannot be done after initialization. */ +static void flush_cache_all_nop(void) {} +#endif /* Place where we save old trap entries for restoration */ struct tt_entry kgdb_savettable[256]; @@ -398,10 +399,12 @@ set_debug_traps(void) { struct hard_trap_info *ht; unsigned long flags; - unsigned char c; save_and_cli(flags); - flush_cache_all = flush_cache_all_nop; +#if 0 +/* Have to sort this out. This cannot be done after initialization. */ + BTFIXUPSET_CALL(flush_cache_all, flush_cache_all_nop, BTFIXUPCALL_NOP); +#endif /* Initialize our copy of the Linux Sparc trap table */ eh_init(); diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 92be6f74d..1690a7c69 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc_ksyms.c,v 1.61 1997/11/19 07:57:44 jj Exp $ +/* $Id: sparc_ksyms.c,v 1.64 1998/03/19 15:36:43 jj Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -15,7 +15,6 @@ #include <linux/string.h> #include <linux/interrupt.h> #include <linux/in6.h> -#include <linux/pci.h> #include <asm/oplib.h> #include <asm/delay.h> @@ -138,7 +137,6 @@ EXPORT_SYMBOL(page_offset); EXPORT_SYMBOL(stack_top); /* Atomic operations. */ -EXPORT_SYMBOL_PRIVATE(_xchg32); EXPORT_SYMBOL_PRIVATE(_atomic_add); EXPORT_SYMBOL_PRIVATE(_atomic_sub); @@ -168,13 +166,23 @@ EXPORT_SYMBOL(request_fast_irq); EXPORT_SYMBOL(sparc_alloc_io); EXPORT_SYMBOL(sparc_free_io); EXPORT_SYMBOL(io_remap_page_range); -EXPORT_SYMBOL(mmu_v2p); -EXPORT_SYMBOL(mmu_unlockarea); -EXPORT_SYMBOL(mmu_lockarea); -EXPORT_SYMBOL(mmu_get_scsi_sgl); -EXPORT_SYMBOL(mmu_get_scsi_one); -EXPORT_SYMBOL(mmu_release_scsi_sgl); -EXPORT_SYMBOL(mmu_release_scsi_one); + +/* Btfixup stuff cannot have versions, it would be complicated too much */ +#ifndef __SMP__ +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(___xchg32)); +#else +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(__smp_processor_id)); +#endif +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(enable_irq)); +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(disable_irq)); +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_v2p)); +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_unlockarea)); +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_lockarea)); +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_get_scsi_sgl)); +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_get_scsi_one)); +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_release_scsi_sgl)); +EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_release_scsi_one)); + EXPORT_SYMBOL(_sparc_dvma_malloc); EXPORT_SYMBOL(sun4c_unmapioaddr); EXPORT_SYMBOL(srmmu_unmapioaddr); @@ -272,7 +280,3 @@ EXPORT_SYMBOL_DOT(mul); EXPORT_SYMBOL_DOT(umul); EXPORT_SYMBOL_DOT(div); EXPORT_SYMBOL_DOT(udiv); - -#if CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c index bc9569688..cef6370ce 100644 --- a/arch/sparc/kernel/sun4c_irq.c +++ b/arch/sparc/kernel/sun4c_irq.c @@ -30,6 +30,9 @@ #include <asm/traps.h> #include <asm/irq.h> #include <asm/io.h> +#include <asm/sun4paddr.h> +#include <asm/idprom.h> +#include <asm/machines.h> /* Pointer to the interrupt enable byte * @@ -128,7 +131,7 @@ __initfunc(static void sun4c_init_timers(void (*counter_fn)(int, void *, struct /* Map the Timer chip, this is implemented in hardware inside * the cache chip on the sun4c. */ - sun4c_timers = sparc_alloc_io (SUN4C_TIMER_PHYSADDR, 0, + sun4c_timers = sparc_alloc_io (SUN_TIMER_PHYSADDR, 0, sizeof(struct sun4c_timer_info), "timer", 0x0, 0x0); @@ -160,30 +163,41 @@ __initfunc(void sun4c_init_IRQ(void)) { struct linux_prom_registers int_regs[2]; int ie_node; + + if (ARCH_SUN4) { + interrupt_enable = + (char *) sparc_alloc_io(SUN4_IE_PHYSADDR, 0, + PAGE_SIZE, + "sun4c_interrupts", + 0x0, 0x0); + } else { - ie_node = prom_searchsiblings (prom_getchild(prom_root_node), - "interrupt-enable"); - if(ie_node == 0) - panic("Cannot find /interrupt-enable node"); - - /* Depending on the "address" property is bad news... */ - prom_getproperty(ie_node, "reg", (char *) int_regs, sizeof(int_regs)); - interrupt_enable = (char *) sparc_alloc_io(int_regs[0].phys_addr, 0, - int_regs[0].reg_size, - "sun4c_interrupts", - int_regs[0].which_io, 0x0); - enable_irq = sun4c_enable_irq; - disable_irq = sun4c_disable_irq; - enable_pil_irq = sun4c_enable_irq; - disable_pil_irq = sun4c_disable_irq; - clear_clock_irq = sun4c_clear_clock_irq; - clear_profile_irq = sun4c_clear_profile_irq; - load_profile_irq = sun4c_load_profile_irq; + ie_node = prom_searchsiblings (prom_getchild(prom_root_node), + "interrupt-enable"); + if(ie_node == 0) + panic("Cannot find /interrupt-enable node"); + + /* Depending on the "address" property is bad news... */ + prom_getproperty(ie_node, "reg", (char *) int_regs, sizeof(int_regs)); + interrupt_enable = + (char *) sparc_alloc_io(int_regs[0].phys_addr, 0, + int_regs[0].reg_size, + "sun4c_interrupts", + int_regs[0].which_io, 0x0); + } + + BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(clear_profile_irq, sun4c_clear_profile_irq, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP); init_timers = sun4c_init_timers; #ifdef __SMP__ - set_cpu_int = (void (*) (int, int))sun4c_nop; - clear_cpu_int = (void (*) (int, int))sun4c_nop; - set_irq_udt = (void (*) (int))sun4c_nop; + BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP); #endif *interrupt_enable = (SUN4C_INT_ENABLE); /* Cannot enable interrupts until OBP ticker is disabled. */ diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index f22fe1495..302df86f4 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -1,8 +1,8 @@ -/* $Id: sun4d_irq.c,v 1.3 1997/12/22 16:09:15 jj Exp $ - * arch/sparc/kernel/sun4d_irq.c: +/* $Id: sun4d_irq.c,v 1.12 1998/03/19 15:36:36 jj Exp $ + * arch/sparc/kernel/sun4d_irq.c: * SS1000/SC2000 interrupt handling. * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Heavily based on arch/sparc/kernel/irq.c. */ @@ -36,32 +36,47 @@ #include <asm/sbus.h> #include <asm/sbi.h> +/* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */ +/* #define DISTRIBUTE_IRQS */ + struct sun4d_timer_regs *sun4d_timers; #define TIMER_IRQ 10 #define MAX_STATIC_ALLOC 4 extern struct irqaction static_irqaction[MAX_STATIC_ALLOC]; extern int static_irq_count; +unsigned char cpu_leds[32]; +#ifdef __SMP__ +unsigned char sbus_tid[32]; +#endif extern struct irqaction *irq_action[]; struct sbus_action { struct irqaction *action; - unsigned char lock; - unsigned char active; - unsigned char disabled; + /* For SMP this needs to be extended */ } *sbus_actions; static int pil_to_sbus[] = { 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0, }; +static int sbus_to_pil[] = { + 0, 2, 3, 5, 7, 9, 11, 13, +}; + static int nsbi; +#ifdef __SMP__ +spinlock_t sun4d_imsk_lock = SPIN_LOCK_UNLOCKED; +#endif int sun4d_get_irq_list(char *buf) { int i, j = 0, k = 0, len = 0, sbusl; struct irqaction * action; +#ifdef __SMP__ + int x; +#endif for (i = 0 ; i < NR_IRQS ; i++) { sbusl = pil_to_sbus[i]; @@ -77,8 +92,15 @@ int sun4d_get_irq_list(char *buf) } continue; } -found_it: len += sprintf(buf+len, "%2d: %8d %c %s", - i, kstat.interrupts[i], +found_it: len += sprintf(buf+len, "%3d: ", i); +#ifndef __SMP__ + len += sprintf(buf+len, "%10u ", kstat_irqs(i)); +#else + for (x = 0; x < smp_num_cpus; x++) + len += sprintf(buf+len, "%10u ", + kstat.irqs[cpu_logical_map(x)][i]); +#endif + len += sprintf(buf+len, "%c %s", (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); action = action->next; @@ -172,7 +194,7 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs) cc_set_iclr(1 << irq); irq_enter(cpu, irq, regs); - kstat.interrupts[irq]++; + kstat.irqs[cpu][irq]++; if (!sbusl) { action = *(irq + irq_action); if (!action) @@ -183,7 +205,6 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs) } while (action); } else { int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff; - int lock; int sbino; struct sbus_action *actionp; unsigned mask, slot; @@ -202,19 +223,13 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs) if (mask & slot) { mask &= ~slot; action = actionp->action; - __asm__ __volatile__ ("ldstub [%1 + 4], %0" - : "=r" (lock) : "r" (actionp)); - if (!lock) { - if (!action) - unexpected_irq(irq, 0, regs); - do { - action->handler(irq, action->dev_id, regs); - action = action->next; - } while (action); - actionp->lock = 0; - } else - actionp->active = 1; + if (!action) + unexpected_irq(irq, 0, regs); + do { + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); release_sbi(SBI2DEVID(sbino), slot); } } @@ -305,79 +320,49 @@ int sun4d_request_irq(unsigned int irq, else *actionp = action; - if (ret) irq = *ret; - - if (irq > NR_IRQS) { - struct sbus_action *s = sbus_actions + irq - (1 << 5); - - if (s->disabled) { - s->disabled = 0; - s->active = 0; - s->lock = 0; - } - } - + enable_irq(irq); restore_flags(flags); return 0; } static void sun4d_disable_irq(unsigned int irq) { - struct sbus_action *s; - - if (irq < NR_IRQS) { - /* FIXME */ - printk ("Unable to disable IRQ %d\n", irq); - return; - } - s = sbus_actions + irq - (1 << 5); +#ifdef __SMP__ + int tid = sbus_tid[(irq >> 5) - 1]; + unsigned long flags; +#endif - if (s->disabled) return; - s->disabled = 1; - __asm__ __volatile__ (" -1: ldstub [%0 + 4], %%g1 - orcc %%g1, 0, %%g0 - bne 1b" - : : "r" (s) : "g1", "cc"); + if (irq < NR_IRQS) return; +#ifdef __SMP__ + spin_lock_irqsave(&sun4d_imsk_lock, flags); + cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7])); + spin_unlock_irqrestore(&sun4d_imsk_lock, flags); +#else + cc_set_imsk(cc_get_imsk() | (1 << sbus_to_pil[(irq >> 2) & 7])); +#endif } static void sun4d_enable_irq(unsigned int irq) { - struct sbus_action *s; - struct irqaction *action; - - if (irq < NR_IRQS) - /* FIXME */ - return; - s = sbus_actions + irq - (1 << 5); +#ifdef __SMP__ + int tid = sbus_tid[(irq >> 5) - 1]; + unsigned long flags; +#endif - if (!s->disabled) return; - action = s->action; - s->disabled = 0; - while (s->active) { - s->active = 0; - while (action) { - /* FIXME: Hope no sbus intr handler uses regs */ - action->handler(irq, action->dev_id, NULL); - action = action->next; - } - } - s->lock = 0; + if (irq < NR_IRQS) return; +#ifdef __SMP__ + spin_lock_irqsave(&sun4d_imsk_lock, flags); + cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7])); + spin_unlock_irqrestore(&sun4d_imsk_lock, flags); +#else + cc_set_imsk(cc_get_imsk() & ~(1 << sbus_to_pil[(irq >> 2) & 7])); +#endif } #ifdef __SMP__ - -/* +-------+-------------+-----------+------------------------------------+ - * | bcast | devid | sid | levels mask | - * +-------+-------------+-----------+------------------------------------+ - * 31 30 23 22 15 14 0 - */ -#define IGEN_MESSAGE(bcast, devid, sid, levels) \ - (((bcast) << 31) | ((devid) << 23) | ((sid) << 15) | (levels)) - -static void sun4d_send_ipi(int cpu, int level) +static void sun4d_set_cpu_int(int cpu, int level) { - cc_set_igen(IGEN_MESSAGE(0, cpu << 3, 6 + ((level >> 1) & 7), 1 << (level - 1))); + sun4d_send_ipi(cpu, level); } static void sun4d_clear_ipi(int cpu, int level) @@ -387,6 +372,55 @@ static void sun4d_clear_ipi(int cpu, int level) static void sun4d_set_udt(int cpu) { } + +/* Setup IRQ distribution scheme. */ +__initfunc(void sun4d_distribute_irqs(void)) +{ +#ifdef DISTRIBUTE_IRQS + struct linux_sbus *sbus; + unsigned long sbus_serving_map; + + sbus_serving_map = cpu_present_map; + for_each_sbus(sbus) { + if ((sbus->board * 2) == boot_cpu_id && (cpu_present_map & (1 << (sbus->board * 2 + 1)))) + sbus_tid[sbus->board] = (sbus->board * 2 + 1); + else if (cpu_present_map & (1 << (sbus->board * 2))) + sbus_tid[sbus->board] = (sbus->board * 2); + else if (cpu_present_map & (1 << (sbus->board * 2 + 1))) + sbus_tid[sbus->board] = (sbus->board * 2 + 1); + else + sbus_tid[sbus->board] = 0xff; + if (sbus_tid[sbus->board] != 0xff) + sbus_serving_map &= ~(1 << sbus_tid[sbus->board]); + } + for_each_sbus(sbus) + if (sbus_tid[sbus->board] == 0xff) { + int i = 31; + + if (!sbus_serving_map) + sbus_serving_map = cpu_present_map; + while (!(sbus_serving_map & (1 << i))) + i--; + sbus_tid[sbus->board] = i; + sbus_serving_map &= ~(1 << i); + } + for_each_sbus(sbus) { + printk("sbus%d IRQs directed to CPU%d\n", sbus->board, sbus_tid[sbus->board]); + set_sbi_tid(sbus->devid, sbus_tid[sbus->board] << 3); + } +#else + struct linux_sbus *sbus; + int cpuid = cpu_logical_map(1); + + if (cpuid == -1) + cpuid = cpu_logical_map(0); + for_each_sbus(sbus) { + sbus_tid[sbus->board] = cpuid; + set_sbi_tid(sbus->devid, cpuid << 3); + } + printk("All sbus IRQs directed to CPU%d\n", cpuid); +#endif +} #endif static void sun4d_clear_clock_irq(void) @@ -408,7 +442,7 @@ static void sun4d_load_profile_irq(int cpu, unsigned int limit) __initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct pt_regs *))) { int irq; - extern struct prom_cpuinfo linux_cpus[NCPUS]; + extern struct prom_cpuinfo linux_cpus[NR_CPUS]; int cpu; /* Map the User Timer registers. */ @@ -431,15 +465,39 @@ __initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct /* Enable user timer free run for CPU 0 in BW */ /* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */ - for(cpu = 0; cpu < NCPUS; cpu++) - sun4d_load_profile_irq(linux_cpus[cpu].mid, 0); + for(cpu = 0; cpu < linux_num_cpus; cpu++) + sun4d_load_profile_irq((linux_cpus[cpu].mid >> 3), 0); + +#ifdef __SMP__ + { + unsigned long flags; + extern unsigned long lvl14_save[4]; + struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)]; + extern unsigned int real_irq_entry[], smp4d_ticker[]; + extern unsigned int patchme_maybe_smp_msg[]; + + /* Adjust so that we jump directly to smp4d_ticker */ + lvl14_save[2] += smp4d_ticker - real_irq_entry; + + /* For SMP we use the level 14 ticker, however the bootup code + * has copied the firmwares level 14 vector into boot cpu's + * trap table, we must fix this now or we get squashed. + */ + __save_and_cli(flags); + patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */ + trap_table->inst_one = lvl14_save[0]; + trap_table->inst_two = lvl14_save[1]; + trap_table->inst_three = lvl14_save[2]; + trap_table->inst_four = lvl14_save[3]; + local_flush_cache_all(); + __restore_flags(flags); + } +#endif } __initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start)) { struct linux_sbus *sbus; - struct sbus_action *s; - int i; unsigned mask; nsbi = 0; @@ -449,11 +507,13 @@ __initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start)) sbus_actions = (struct sbus_action *)memory_start; memory_start += (nsbi * 8 * 4 * sizeof(struct sbus_action)); memset (sbus_actions, 0, (nsbi * 8 * 4 * sizeof(struct sbus_action))); - for (i = 0, s = sbus_actions; i < nsbi * 8 * 4; i++, s++) { - s->lock = 0xff; - s->disabled = 1; - } for_each_sbus(sbus) { +#ifdef __SMP__ + extern unsigned char boot_cpu_id; + + set_sbi_tid(sbus->devid, boot_cpu_id << 3); + sbus_tid[sbus->board] = boot_cpu_id; +#endif /* Get rid of pending irqs from PROM */ mask = acquire_sbi(sbus->devid, 0xffffffff); if (mask) { @@ -468,16 +528,16 @@ __initfunc(void sun4d_init_IRQ(void)) { __cli(); - enable_irq = sun4d_enable_irq; - disable_irq = sun4d_disable_irq; - clear_clock_irq = sun4d_clear_clock_irq; - clear_profile_irq = sun4d_clear_profile_irq; - load_profile_irq = sun4d_load_profile_irq; + BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(clear_profile_irq, sun4d_clear_profile_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM); init_timers = sun4d_init_timers; #ifdef __SMP__ - set_cpu_int = (void (*) (int, int))sun4d_send_ipi; - clear_cpu_int = (void (*) (int, int))sun4d_clear_ipi; - set_irq_udt = (void (*) (int))sun4d_set_udt; + BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(set_irq_udt, sun4d_set_udt, BTFIXUPCALL_NOP); #endif /* Cannot enable interrupts until OBP ticker is disabled. */ } diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c new file mode 100644 index 000000000..46ce7a83f --- /dev/null +++ b/arch/sparc/kernel/sun4d_smp.c @@ -0,0 +1,576 @@ +/* sun4d_smp.c: Sparc SS1000/SC2000 SMP support. + * + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * Based on sun4m's smp.c, which is: + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/config.h> /* for CONFIG_PROFILE */ +#include <asm/head.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/tasks.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/init.h> + +#include <asm/ptrace.h> +#include <asm/atomic.h> + +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/oplib.h> +#include <asm/atops.h> +#include <asm/spinlock.h> +#include <asm/hardirq.h> +#include <asm/softirq.h> +#include <asm/sbus.h> +#include <asm/sbi.h> + +#define __KERNEL_SYSCALLS__ +#include <linux/unistd.h> + +#define IRQ_CROSS_CALL 15 + +extern ctxd_t *srmmu_ctx_table_phys; +extern int linux_num_cpus; + +extern void calibrate_delay(void); + +extern struct task_struct *current_set[NR_CPUS]; +extern volatile int smp_processors_ready; +extern unsigned long cpu_present_map; +extern int smp_num_cpus; +static int smp_highest_cpu = 0; +extern int smp_threads_ready; +extern unsigned char mid_xlate[NR_CPUS]; +extern volatile unsigned long cpu_callin_map[NR_CPUS]; +extern unsigned long smp_proc_in_lock[NR_CPUS]; +extern struct cpuinfo_sparc cpu_data[NR_CPUS]; +extern unsigned long cpu_offset[NR_CPUS]; +extern unsigned char boot_cpu_id; +extern int smp_activated; +extern volatile int cpu_number_map[NR_CPUS]; +extern volatile int __cpu_logical_map[NR_CPUS]; +extern struct klock_info klock_info; +extern volatile unsigned long ipi_count; +extern volatile int smp_process_available; +extern volatile int smp_commenced; +extern int __smp4d_processor_id(void); + +/* #define SMP_DEBUG */ + +#ifdef SMP_DEBUG +#define SMP_PRINTK(x) printk x +#else +#define SMP_PRINTK(x) +#endif + +int smp4d_bogo_info(char *buf) +{ + int len = 0, i; + + for (i = 0; i < NR_CPUS; i++) + if (cpu_present_map & (1 << i)) + len += sprintf(buf + len, "Cpu%dBogo\t: %lu.%02lu\n", + i, + cpu_data[i].udelay_val/500000, + (cpu_data[i].udelay_val/5000)%100); + return len; +} + +int smp4d_info(char *buf) +{ + int len = 0, i; + + for (i = 0; i < NR_CPUS; i++) + if (cpu_present_map & (1 << i)) + len += sprintf(buf + len, "CPU%d\t\t: %s\n", + i, + (klock_info.akp == i) ? "akp" : "online"); + return len; +} + +static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val) +{ + __asm__ __volatile__("swap [%1], %0\n\t" : + "=&r" (val), "=&r" (ptr) : + "0" (val), "1" (ptr)); + return val; +} + +static void smp_setup_percpu_timer(void); +extern void cpu_probe(void); +extern void sun4d_distribute_irqs(void); + +__initfunc(void smp4d_callin(void)) +{ + int cpuid = hard_smp4d_processor_id(); + extern spinlock_t sun4d_imsk_lock; + unsigned long flags; + + /* Show we are alive */ + cpu_leds[cpuid] = 0x6; + show_leds(cpuid); + + /* Enable level15 interrupt, disable level14 interrupt for now */ + cc_set_imsk((cc_get_imsk() & ~0x8000) | 0x4000); + + local_flush_cache_all(); + local_flush_tlb_all(); + + /* Get our local ticker going. */ + smp_setup_percpu_timer(); + + calibrate_delay(); + smp_store_cpu_info(cpuid); + local_flush_cache_all(); + local_flush_tlb_all(); + + /* Allow master to continue. */ + swap((unsigned long *)&cpu_callin_map[cpuid], 1); + local_flush_cache_all(); + local_flush_tlb_all(); + + cpu_probe(); + + while((unsigned long)current_set[cpuid] < PAGE_OFFSET) + barrier(); + + while(current_set[cpuid]->processor != cpuid) + barrier(); + + /* Fix idle thread fields. */ + __asm__ __volatile__("ld [%0], %%g6\n\t" + "sta %%g6, [%%g0] %1\n\t" + : : "r" (¤t_set[cpuid]), "i" (ASI_M_VIKING_TMP2) + : "memory" /* paranoid */); + + cpu_leds[cpuid] = 0x9; + show_leds(cpuid); + + current->mm->mmap->vm_page_prot = PAGE_SHARED; + current->mm->mmap->vm_start = PAGE_OFFSET; + current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; + + local_flush_cache_all(); + local_flush_tlb_all(); + + __sti(); /* We don't allow PIL 14 yet */ + + while(!smp_commenced) + barrier(); + + spin_lock_irqsave(&sun4d_imsk_lock, flags); + cc_set_imsk(cc_get_imsk() & ~0x4000); /* Allow PIL 14 as well */ + spin_unlock_irqrestore(&sun4d_imsk_lock, flags); +} + +extern int cpu_idle(void *unused); +extern void init_IRQ(void); +extern void cpu_panic(void); +extern int start_secondary(void *unused); + +/* + * Cycle through the processors asking the PROM to start each one. + */ + +extern struct prom_cpuinfo linux_cpus[NR_CPUS]; +extern struct linux_prom_registers smp_penguin_ctable; +extern unsigned long trapbase_cpu1[]; +extern unsigned long trapbase_cpu2[]; +extern unsigned long trapbase_cpu3[]; + +__initfunc(void smp4d_boot_cpus(void)) +{ + int cpucount = 0; + int i = 0; + + printk("Entering SMP Mode...\n"); + + smp_penguin_ctable.which_io = 0; + smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys; + smp_penguin_ctable.reg_size = 0; + + for (i = 0; i < NR_CPUS; i++) + cpu_offset[i] = (char *)&cpu_data[i] - (char *)&cpu_data; + + if (boot_cpu_id) + current_set[0] = NULL; + + __sti(); + cpu_present_map = 0; + for(i=0; i < linux_num_cpus; i++) + cpu_present_map |= (1<<linux_cpus[i].mid); + SMP_PRINTK(("cpu_present_map %08lx\n", cpu_present_map)); + for(i=0; i < NR_CPUS; i++) + cpu_number_map[i] = -1; + for(i=0; i < NR_CPUS; i++) + __cpu_logical_map[i] = -1; + for(i=0; i < NR_CPUS; i++) + mid_xlate[i] = i; + cpu_number_map[boot_cpu_id] = 0; + __cpu_logical_map[0] = boot_cpu_id; + klock_info.akp = boot_cpu_id; + current->processor = boot_cpu_id; + smp_store_cpu_info(boot_cpu_id); + smp_setup_percpu_timer(); + local_flush_cache_all(); + if(linux_num_cpus == 1) + return; /* Not an MP box. */ + SMP_PRINTK(("Iterating over CPUs\n")); + for(i = 0; i < NR_CPUS; i++) { + if(i == boot_cpu_id) + continue; + + if(cpu_present_map & (1 << i)) { + extern unsigned long sun4d_cpu_startup; + unsigned long *entry = &sun4d_cpu_startup; + struct task_struct *p; + int timeout; + int no; + + /* Cook up an idler for this guy. */ + kernel_thread(start_secondary, NULL, CLONE_PID); + + p = task[++cpucount]; + + p->processor = i; + current_set[i] = p; + + for (no = 0; no < linux_num_cpus; no++) + if (linux_cpus[no].mid == i) + break; + + /* whirrr, whirrr, whirrrrrrrrr... */ + SMP_PRINTK(("Starting CPU %d at %p task %d node %08x\n", i, entry, cpucount, linux_cpus[no].prom_node)); + local_flush_cache_all(); + prom_startcpu(linux_cpus[no].prom_node, + &smp_penguin_ctable, 0, (char *)entry); + + SMP_PRINTK(("prom_startcpu returned :)\n")); + + /* wheee... it's going... */ + for(timeout = 0; timeout < 5000000; timeout++) { + if(cpu_callin_map[i]) + break; + udelay(100); + } + + if(cpu_callin_map[i]) { + /* Another "Red Snapper". */ + cpu_number_map[i] = cpucount; + __cpu_logical_map[cpucount] = i; + } else { + cpucount--; + printk("Processor %d is stuck.\n", i); + } + } + if(!(cpu_callin_map[i])) { + cpu_present_map &= ~(1 << i); + cpu_number_map[i] = -1; + } + } + local_flush_cache_all(); + if(cpucount == 0) { + printk("Error: only one Processor found.\n"); + cpu_present_map = (1 << hard_smp4d_processor_id()); + } else { + unsigned long bogosum = 0; + + for(i = 0; i < NR_CPUS; i++) { + if(cpu_present_map & (1 << i)) { + bogosum += cpu_data[i].udelay_val; + smp_highest_cpu = i; + } + } + SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, (bogosum + 2500)/500000, ((bogosum + 2500)/5000)%100)); + printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", + cpucount + 1, + (bogosum + 2500)/500000, + ((bogosum + 2500)/5000)%100); + smp_activated = 1; + smp_num_cpus = cpucount + 1; + } + + /* Free unneeded trap tables */ + + mem_map[MAP_NR((unsigned long)trapbase_cpu1)].flags &= ~(1 << PG_reserved); + free_page((unsigned long)trapbase_cpu1); + mem_map[MAP_NR((unsigned long)trapbase_cpu2)].flags &= ~(1 << PG_reserved); + free_page((unsigned long)trapbase_cpu2); + mem_map[MAP_NR((unsigned long)trapbase_cpu3)].flags &= ~(1 << PG_reserved); + free_page((unsigned long)trapbase_cpu3); + + /* Ok, they are spinning and ready to go. */ + smp_processors_ready = 1; + sun4d_distribute_irqs(); +} + +static struct smp_funcall { + smpfunc_t func; + unsigned long arg1; + unsigned long arg2; + unsigned long arg3; + unsigned long arg4; + unsigned long arg5; + unsigned char processors_in[NR_CPUS]; /* Set when ipi entered. */ + unsigned char processors_out[NR_CPUS]; /* Set when ipi exited. */ +} ccall_info __attribute__((aligned(8))); + +static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED; + +/* Cross calls must be serialized, at least currently. */ +void smp4d_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, unsigned long arg5) +{ + if(smp_processors_ready) { + register int high = smp_highest_cpu; + unsigned long flags; + + spin_lock_irqsave(&cross_call_lock, flags); + + { + /* If you make changes here, make sure gcc generates proper code... */ + smpfunc_t f asm("i0") = func; + unsigned long a1 asm("i1") = arg1; + unsigned long a2 asm("i2") = arg2; + unsigned long a3 asm("i3") = arg3; + unsigned long a4 asm("i4") = arg4; + unsigned long a5 asm("i5") = arg5; + + __asm__ __volatile__(" + std %0, [%6] + std %2, [%6 + 8] + std %4, [%6 + 16]" : : + "r"(f), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), + "r" (&ccall_info.func)); + } + + /* Init receive/complete mapping, plus fire the IPI's off. */ + { + register unsigned long mask; + register int i; + + mask = (cpu_present_map & ~(1 << hard_smp4d_processor_id())); + for(i = 0; i <= high; i++) { + if(mask & (1 << i)) { + ccall_info.processors_in[i] = 0; + ccall_info.processors_out[i] = 0; + sun4d_send_ipi(i, IRQ_CROSS_CALL); + } + } + } + + /* First, run local copy. */ + func(arg1, arg2, arg3, arg4, arg5); + + { + register int i; + + i = 0; + do { + while(!ccall_info.processors_in[i]) + barrier(); + } while(++i <= high); + + i = 0; + do { + while(!ccall_info.processors_out[i]) + barrier(); + } while(++i <= high); + } + + spin_unlock_irqrestore(&cross_call_lock, flags); + } else + func(arg1, arg2, arg3, arg4, arg5); /* Just need to run local copy. */ +} + +/* Running cross calls. */ +void smp4d_cross_call_irq(void) +{ + int i = hard_smp4d_processor_id(); + + ccall_info.processors_in[i] = 1; + ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3, + ccall_info.arg4, ccall_info.arg5); + ccall_info.processors_out[i] = 1; +} + +static int smp4d_stop_cpu_sender; + +static void smp4d_stop_cpu(void) +{ + int me = hard_smp4d_processor_id(); + + if (me != smp4d_stop_cpu_sender) + while(1) barrier(); +} + +/* Cross calls, in order to work efficiently and atomically do all + * the message passing work themselves, only stopcpu and reschedule + * messages come through here. + */ +void smp4d_message_pass(int target, int msg, unsigned long data, int wait) +{ + int me = hard_smp4d_processor_id(); + + SMP_PRINTK(("smp4d_message_pass %d %d %08lx %d\n", target, msg, data, wait)); + if (msg == MSG_STOP_CPU && target == MSG_ALL_BUT_SELF) { + unsigned long flags; + static spinlock_t stop_cpu_lock = SPIN_LOCK_UNLOCKED; + spin_lock_irqsave(&stop_cpu_lock, flags); + smp4d_stop_cpu_sender = me; + smp4d_cross_call((smpfunc_t)smp4d_stop_cpu, 0, 0, 0, 0, 0); + spin_unlock_irqrestore(&stop_cpu_lock, flags); + } + printk("Yeeee, trying to send SMP msg(%d) to %d on cpu %d\n", msg, target, me); + panic("Bogon SMP message pass."); +} + +/* Protects counters touched during level14 ticker */ +static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; + +#ifdef CONFIG_PROFILE + +/* 32-bit Sparc specific profiling function. */ +static inline void sparc_do_profile(unsigned long pc) +{ + if(prof_buffer && current->pid) { + extern int _stext; + + pc -= (unsigned long) &_stext; + pc >>= prof_shift; + + spin_lock(&ticker_lock); + if(pc < prof_len) + prof_buffer[pc]++; + else + prof_buffer[prof_len - 1]++; + spin_unlock(&ticker_lock); + } +} + +#endif + +extern unsigned int prof_multiplier[NR_CPUS]; +extern unsigned int prof_counter[NR_CPUS]; + +extern void update_one_process(struct task_struct *p, unsigned long ticks, + unsigned long user, unsigned long system, + int cpu); + + +void smp4d_percpu_timer_interrupt(struct pt_regs *regs) +{ + int cpu = hard_smp4d_processor_id(); + static int cpu_tick[NR_CPUS]; + static char led_mask[] = { 0xe, 0xd, 0xb, 0x7, 0xb, 0xd }; + + bw_get_prof_limit(cpu); + bw_clear_intr_mask(0, 1); /* INTR_TABLE[0] & 1 is Profile IRQ */ + + cpu_tick[cpu]++; + if (!(cpu_tick[cpu] & 15)) { + if (cpu_tick[cpu] == 0x60) + cpu_tick[cpu] = 0; + cpu_leds[cpu] = led_mask[cpu_tick[cpu] >> 4]; + show_leds(cpu); + } + +#ifdef CONFIG_PROFILE + if(!user_mode(regs)) + sparc_do_profile(regs->pc); +#endif + if(!--prof_counter[cpu]) { + int user = user_mode(regs); + if(current->pid) { + update_one_process(current, 1, user, !user, cpu); + + if(--current->counter < 0) { + current->counter = 0; + need_resched = 1; + } + + spin_lock(&ticker_lock); + if(user) { + if(current->priority < DEF_PRIORITY) { + kstat.cpu_nice++; + kstat.per_cpu_nice[cpu]++; + } else { + kstat.cpu_user++; + kstat.per_cpu_user[cpu]++; + } + } else { + kstat.cpu_system++; + kstat.per_cpu_system[cpu]++; + } + spin_unlock(&ticker_lock); + } + prof_counter[cpu] = prof_multiplier[cpu]; + } +} + +extern unsigned int lvl14_resolution; + +__initfunc(static void smp_setup_percpu_timer(void)) +{ + int cpu = hard_smp4d_processor_id(); + + prof_counter[cpu] = prof_multiplier[cpu] = 1; + load_profile_irq(cpu, lvl14_resolution); +} + +__initfunc(void smp4d_blackbox_id(unsigned *addr)) +{ + int rd = *addr & 0x3e000000; + + addr[0] = 0xc0800800 | rd; /* lda [%g0] ASI_M_VIKING_TMP1, reg */ + addr[1] = 0x01000000; /* nop */ + addr[2] = 0x01000000; /* nop */ +} + +__initfunc(void smp4d_blackbox_current(unsigned *addr)) +{ + /* We have a nice Linux current register :) */ + int rd = addr[1] & 0x3e000000; + + addr[0] = 0x10800006; /* b .+24 */ + addr[1] = 0xc0800820 | rd; /* lda [%g0] ASI_M_VIKING_TMP2, reg */ +} + +__initfunc(void sun4d_init_smp(void)) +{ + int i; + extern unsigned int patchme_store_new_current[]; + extern unsigned int t_nmi[], linux_trap_ipi15_sun4d[], linux_trap_ipi15_sun4m[]; + + /* Store current into Linux current register :) */ + __asm__ __volatile__("sta %%g6, [%%g0] %0" : : "i"(ASI_M_VIKING_TMP2)); + + /* Patch switch_to */ + patchme_store_new_current[0] = (patchme_store_new_current[0] & 0x3e000000) | 0xc0a00820; + + /* Patch ipi15 trap table */ + t_nmi[1] = t_nmi[1] + (linux_trap_ipi15_sun4d - linux_trap_ipi15_sun4m); + + /* And set btfixup... */ + BTFIXUPSET_BLACKBOX(smp_processor_id, smp4d_blackbox_id); + BTFIXUPSET_BLACKBOX(load_current, smp4d_blackbox_current); + BTFIXUPSET_CALL(smp_cross_call, smp4d_cross_call, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(smp_message_pass, smp4d_message_pass, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(smp_bogo_info, smp4d_bogo_info, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(smp_info, smp4d_info, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(__smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM); + + for (i = 0; i < NR_CPUS; i++) { + ccall_info.processors_in[i] = 1; + ccall_info.processors_out[i] = 1; + } +} diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 81db1a4ce..e55839a7a 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c @@ -83,9 +83,9 @@ inline unsigned long sun4m_get_irqmask(unsigned int irq) if (!mask) printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq); } else { - /* Soft Interrupts will come here - * Currently there is no way to trigger them but I'm sure something - * could be cooked up. + /* Soft Interrupts will come here. + * Currently there is no way to trigger them but I'm sure + * something could be cooked up. */ irq &= 0xf; mask = SUN4M_SOFT_INT(irq); @@ -349,18 +349,18 @@ __initfunc(void sun4m_init_IRQ(void)) &sun4m_interrupts->undirected_target; sun4m_interrupts->undirected_target = 0; } - enable_irq = sun4m_enable_irq; - disable_irq = sun4m_disable_irq; - enable_pil_irq = sun4m_enable_pil_irq; - disable_pil_irq = sun4m_disable_pil_irq; - clear_clock_irq = sun4m_clear_clock_irq; - clear_profile_irq = sun4m_clear_profile_irq; - load_profile_irq = sun4m_load_profile_irq; + BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(clear_profile_irq, sun4m_clear_profile_irq, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM); init_timers = sun4m_init_timers; #ifdef __SMP__ - set_cpu_int = (void (*) (int, int))sun4m_send_ipi; - clear_cpu_int = (void (*) (int, int))sun4m_clear_ipi; - set_irq_udt = (void (*) (int))sun4m_set_udt; + BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM); #endif /* Cannot enable interrupts until OBP ticker is disabled. */ } diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c new file mode 100644 index 000000000..ec1ef424b --- /dev/null +++ b/arch/sparc/kernel/sun4m_smp.c @@ -0,0 +1,545 @@ +/* sun4m_smp.c: Sparc SUN4M SMP support. + * + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/config.h> /* for CONFIG_PROFILE */ +#include <asm/head.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/tasks.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/init.h> + +#include <asm/ptrace.h> +#include <asm/atomic.h> + +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/oplib.h> +#include <asm/atops.h> +#include <asm/spinlock.h> +#include <asm/hardirq.h> +#include <asm/softirq.h> + +#define __KERNEL_SYSCALLS__ +#include <linux/unistd.h> + +#define IRQ_RESCHEDULE 13 +#define IRQ_STOP_CPU 14 +#define IRQ_CROSS_CALL 15 + +extern ctxd_t *srmmu_ctx_table_phys; +extern int linux_num_cpus; + +extern void calibrate_delay(void); + +extern struct task_struct *current_set[NR_CPUS]; +extern volatile int smp_processors_ready; +extern unsigned long cpu_present_map; +extern int smp_num_cpus; +extern int smp_threads_ready; +extern unsigned char mid_xlate[NR_CPUS]; +extern volatile unsigned long cpu_callin_map[NR_CPUS]; +extern unsigned long smp_proc_in_lock[NR_CPUS]; +extern struct cpuinfo_sparc cpu_data[NR_CPUS]; +extern unsigned long cpu_offset[NR_CPUS]; +extern unsigned char boot_cpu_id; +extern int smp_activated; +extern volatile int cpu_number_map[NR_CPUS]; +extern volatile int __cpu_logical_map[NR_CPUS]; +extern struct klock_info klock_info; +extern volatile unsigned long ipi_count; +extern volatile int smp_process_available; +extern volatile int smp_commenced; +extern int __smp4m_processor_id(void); + +/*#define SMP_DEBUG*/ + +#ifdef SMP_DEBUG +#define SMP_PRINTK(x) printk x +#else +#define SMP_PRINTK(x) +#endif + +int smp4m_bogo_info(char *buf) +{ + return sprintf(buf, + "Cpu0Bogo\t: %lu.%02lu\n" + "Cpu1Bogo\t: %lu.%02lu\n" + "Cpu2Bogo\t: %lu.%02lu\n" + "Cpu3Bogo\t: %lu.%02lu\n", + cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100, + cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100, + cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100, + cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100); +} + +int smp4m_info(char *buf) +{ + return sprintf(buf, +" CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n" +"State: %s\t\t%s\t\t%s\t\t%s\n", +(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline", +(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline", +(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline", +(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline"); +} + +static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val) +{ + __asm__ __volatile__("swap [%1], %0\n\t" : + "=&r" (val), "=&r" (ptr) : + "0" (val), "1" (ptr)); + return val; +} + +static void smp_setup_percpu_timer(void); +extern void cpu_probe(void); + +__initfunc(void smp4m_callin(void)) +{ + int cpuid = hard_smp_processor_id(); + + local_flush_cache_all(); + local_flush_tlb_all(); + set_irq_udt(mid_xlate[boot_cpu_id]); + + /* Get our local ticker going. */ + smp_setup_percpu_timer(); + + calibrate_delay(); + smp_store_cpu_info(cpuid); + local_flush_cache_all(); + local_flush_tlb_all(); + + /* Allow master to continue. */ + swap((unsigned long *)&cpu_callin_map[cpuid], 1); + local_flush_cache_all(); + local_flush_tlb_all(); + + cpu_probe(); + + while(!task[cpuid] || current_set[cpuid] != task[cpuid]) + barrier(); + + /* Fix idle thread fields. */ + __asm__ __volatile__("ld [%0], %%g6\n\t" + : : "r" (¤t_set[cpuid]) + : "memory" /* paranoid */); + current->mm->mmap->vm_page_prot = PAGE_SHARED; + current->mm->mmap->vm_start = PAGE_OFFSET; + current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; + + while(!smp_commenced) + barrier(); + + local_flush_cache_all(); + local_flush_tlb_all(); + + __sti(); +} + +extern int cpu_idle(void *unused); +extern void init_IRQ(void); +extern void cpu_panic(void); +extern int start_secondary(void *unused); + +/* + * Cycle through the processors asking the PROM to start each one. + */ + +extern struct prom_cpuinfo linux_cpus[NR_CPUS]; +extern struct linux_prom_registers smp_penguin_ctable; +extern unsigned long trapbase_cpu1[]; +extern unsigned long trapbase_cpu2[]; +extern unsigned long trapbase_cpu3[]; + +__initfunc(void smp4m_boot_cpus(void)) +{ + int cpucount = 0; + int i = 0; + int first, prev; + + printk("Entering SMP Mode...\n"); + + smp_penguin_ctable.which_io = 0; + smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys; + smp_penguin_ctable.reg_size = 0; + + for (i = 0; i < NR_CPUS; i++) + cpu_offset[i] = (char *)&cpu_data[i] - (char *)&cpu_data; + + __sti(); + cpu_present_map = 0; + for(i=0; i < linux_num_cpus; i++) + cpu_present_map |= (1<<i); + for(i=0; i < NR_CPUS; i++) + cpu_number_map[i] = -1; + for(i=0; i < NR_CPUS; i++) + __cpu_logical_map[i] = -1; + mid_xlate[boot_cpu_id] = (linux_cpus[boot_cpu_id].mid & ~8); + cpu_number_map[boot_cpu_id] = 0; + __cpu_logical_map[0] = boot_cpu_id; + klock_info.akp = boot_cpu_id; + current->processor = boot_cpu_id; + smp_store_cpu_info(boot_cpu_id); + set_irq_udt(mid_xlate[boot_cpu_id]); + smp_setup_percpu_timer(); + local_flush_cache_all(); + if(linux_num_cpus == 1) + return; /* Not an MP box. */ + for(i = 0; i < NR_CPUS; i++) { + if(i == boot_cpu_id) + continue; + + if(cpu_present_map & (1 << i)) { + extern unsigned long sun4m_cpu_startup; + unsigned long *entry = &sun4m_cpu_startup; + struct task_struct *p; + int timeout; + + /* Cook up an idler for this guy. */ + kernel_thread(start_secondary, NULL, CLONE_PID); + + p = task[++cpucount]; + + p->processor = i; + current_set[i] = p; + + /* See trampoline.S for details... */ + entry += ((i-1) * 3); + + /* whirrr, whirrr, whirrrrrrrrr... */ + printk("Starting CPU %d at %p\n", i, entry); + mid_xlate[i] = (linux_cpus[i].mid & ~8); + local_flush_cache_all(); + prom_startcpu(linux_cpus[i].prom_node, + &smp_penguin_ctable, 0, (char *)entry); + + /* wheee... it's going... */ + for(timeout = 0; timeout < 5000000; timeout++) { + if(cpu_callin_map[i]) + break; + udelay(100); + } + if(cpu_callin_map[i]) { + /* Another "Red Snapper". */ + cpu_number_map[i] = i; + __cpu_logical_map[i] = i; + } else { + cpucount--; + printk("Processor %d is stuck.\n", i); + } + } + if(!(cpu_callin_map[i])) { + cpu_present_map &= ~(1 << i); + cpu_number_map[i] = -1; + } + } + local_flush_cache_all(); + if(cpucount == 0) { + printk("Error: only one Processor found.\n"); + cpu_present_map = (1 << smp_processor_id()); + } else { + unsigned long bogosum = 0; + for(i = 0; i < NR_CPUS; i++) { + if(cpu_present_map & (1 << i)) + bogosum += cpu_data[i].udelay_val; + } + printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", + cpucount + 1, + (bogosum + 2500)/500000, + ((bogosum + 2500)/5000)%100); + smp_activated = 1; + smp_num_cpus = cpucount + 1; + } + + /* Setup CPU list for IRQ distribution scheme. */ + first = prev = -1; + for(i = 0; i < NR_CPUS; i++) { + if(cpu_present_map & (1 << i)) { + if(first == -1) + first = i; + if(prev != -1) + cpu_data[prev].next = i; + cpu_data[i].mid = mid_xlate[i]; + prev = i; + } + } + cpu_data[prev].next = first; + + /* Free unneeded trap tables */ + + if (!(cpu_present_map & (1 << 1))) { + mem_map[MAP_NR((unsigned long)trapbase_cpu1)].flags &= ~(1 << PG_reserved); + free_page((unsigned long)trapbase_cpu1); + } + if (!(cpu_present_map & (1 << 2))) { + mem_map[MAP_NR((unsigned long)trapbase_cpu2)].flags &= ~(1 << PG_reserved); + free_page((unsigned long)trapbase_cpu2); + } + if (!(cpu_present_map & (1 << 3))) { + mem_map[MAP_NR((unsigned long)trapbase_cpu3)].flags &= ~(1 << PG_reserved); + free_page((unsigned long)trapbase_cpu3); + } + + /* Ok, they are spinning and ready to go. */ + smp_processors_ready = 1; +} + +/* At each hardware IRQ, we get this called to forward IRQ reception + * to the next processor. The caller must disable the IRQ level being + * serviced globally so that there are no double interrupts received. + */ +void smp4m_irq_rotate(int cpu) +{ + if(smp_processors_ready) + set_irq_udt(cpu_data[cpu_data[cpu].next].mid); +} + +/* Cross calls, in order to work efficiently and atomically do all + * the message passing work themselves, only stopcpu and reschedule + * messages come through here. + */ +void smp4m_message_pass(int target, int msg, unsigned long data, int wait) +{ + static unsigned long smp_cpu_in_msg[NR_CPUS]; + unsigned long mask; + int me = smp_processor_id(); + int irq, i; + + if(msg == MSG_RESCHEDULE) { + irq = IRQ_RESCHEDULE; + + if(smp_cpu_in_msg[me]) + return; + } else if(msg == MSG_STOP_CPU) { + irq = IRQ_STOP_CPU; + } else { + goto barf; + } + + smp_cpu_in_msg[me]++; + if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) { + mask = cpu_present_map; + if(target == MSG_ALL_BUT_SELF) + mask &= ~(1 << me); + for(i = 0; i < 4; i++) { + if(mask & (1 << i)) + set_cpu_int(mid_xlate[i], irq); + } + } else { + set_cpu_int(mid_xlate[target], irq); + } + smp_cpu_in_msg[me]--; + + return; +barf: + printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me); + panic("Bogon SMP message pass."); +} + +static struct smp_funcall { + smpfunc_t func; + unsigned long arg1; + unsigned long arg2; + unsigned long arg3; + unsigned long arg4; + unsigned long arg5; + unsigned long processors_in[NR_CPUS]; /* Set when ipi entered. */ + unsigned long processors_out[NR_CPUS]; /* Set when ipi exited. */ +} ccall_info; + +static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED; + +/* Cross calls must be serialized, at least currently. */ +void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, unsigned long arg5) +{ + if(smp_processors_ready) { + register int ncpus = smp_num_cpus; + unsigned long flags; + + spin_lock_irqsave(&cross_call_lock, flags); + + /* Init function glue. */ + ccall_info.func = func; + ccall_info.arg1 = arg1; + ccall_info.arg2 = arg2; + ccall_info.arg3 = arg3; + ccall_info.arg4 = arg4; + ccall_info.arg5 = arg5; + + /* Init receive/complete mapping, plus fire the IPI's off. */ + { + register unsigned long mask; + register int i; + + mask = (cpu_present_map & ~(1 << smp_processor_id())); + for(i = 0; i < ncpus; i++) { + if(mask & (1 << i)) { + ccall_info.processors_in[i] = 0; + ccall_info.processors_out[i] = 0; + set_cpu_int(mid_xlate[i], IRQ_CROSS_CALL); + } else { + ccall_info.processors_in[i] = 1; + ccall_info.processors_out[i] = 1; + } + } + } + + /* First, run local copy. */ + func(arg1, arg2, arg3, arg4, arg5); + + { + register int i; + + i = 0; + do { + while(!ccall_info.processors_in[i]) + barrier(); + } while(++i < ncpus); + + i = 0; + do { + while(!ccall_info.processors_out[i]) + barrier(); + } while(++i < ncpus); + } + + spin_unlock_irqrestore(&cross_call_lock, flags); + } else + func(arg1, arg2, arg3, arg4, arg5); /* Just need to run local copy. */ +} + +/* Running cross calls. */ +void smp4m_cross_call_irq(void) +{ + int i = smp_processor_id(); + + ccall_info.processors_in[i] = 1; + ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3, + ccall_info.arg4, ccall_info.arg5); + ccall_info.processors_out[i] = 1; +} + +/* Protects counters touched during level14 ticker */ +static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; + +#ifdef CONFIG_PROFILE + +/* 32-bit Sparc specific profiling function. */ +static inline void sparc_do_profile(unsigned long pc) +{ + if(prof_buffer && current->pid) { + extern int _stext; + + pc -= (unsigned long) &_stext; + pc >>= prof_shift; + + spin_lock(&ticker_lock); + if(pc < prof_len) + prof_buffer[pc]++; + else + prof_buffer[prof_len - 1]++; + spin_unlock(&ticker_lock); + } +} + +#endif + +extern unsigned int prof_multiplier[NR_CPUS]; +extern unsigned int prof_counter[NR_CPUS]; + +extern void update_one_process(struct task_struct *p, unsigned long ticks, + unsigned long user, unsigned long system, + int cpu); + +void smp4m_percpu_timer_interrupt(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + + clear_profile_irq(mid_xlate[cpu]); +#ifdef CONFIG_PROFILE + if(!user_mode(regs)) + sparc_do_profile(regs->pc); +#endif + if(!--prof_counter[cpu]) { + int user = user_mode(regs); + if(current->pid) { + update_one_process(current, 1, user, !user, cpu); + + if(--current->counter < 0) { + current->counter = 0; + need_resched = 1; + } + + spin_lock(&ticker_lock); + if(user) { + if(current->priority < DEF_PRIORITY) { + kstat.cpu_nice++; + kstat.per_cpu_nice[cpu]++; + } else { + kstat.cpu_user++; + kstat.per_cpu_user[cpu]++; + } + } else { + kstat.cpu_system++; + kstat.per_cpu_system[cpu]++; + } + spin_unlock(&ticker_lock); + } + prof_counter[cpu] = prof_multiplier[cpu]; + } +} + +extern unsigned int lvl14_resolution; + +__initfunc(static void smp_setup_percpu_timer(void)) +{ + int cpu = smp_processor_id(); + + prof_counter[cpu] = prof_multiplier[cpu] = 1; + load_profile_irq(mid_xlate[cpu], lvl14_resolution); + + if(cpu == boot_cpu_id) + enable_pil_irq(14); +} + +__initfunc(void smp4m_blackbox_id(unsigned *addr)) +{ + int rd = *addr & 0x3e000000; + int rs1 = rd >> 11; + + addr[0] = 0x81580000 | rd; /* rd %tbr, reg */ + addr[1] = 0x8130200c | rd | rs1; /* srl reg, 0xc, reg */ + addr[2] = 0x80082003 | rd | rs1; /* and reg, 3, reg */ +} + +__initfunc(void smp4m_blackbox_current(unsigned *addr)) +{ + int rd = *addr & 0x3e000000; + int rs1 = rd >> 11; + + addr[0] = 0x81580000 | rd; /* rd %tbr, reg */ + addr[2] = 0x8130200a | rd | rs1; /* srl reg, 0xa, reg */ + addr[4] = 0x8008200c | rd | rs1; /* and reg, 3, reg */ +} + +__initfunc(void sun4m_init_smp(void)) +{ + BTFIXUPSET_BLACKBOX(smp_processor_id, smp4m_blackbox_id); + BTFIXUPSET_BLACKBOX(load_current, smp4m_blackbox_current); + BTFIXUPSET_CALL(smp_cross_call, smp4m_cross_call, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(smp_message_pass, smp4m_message_pass, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(smp_bogo_info, smp4m_bogo_info, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(smp_info, smp4m_info, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(__smp_processor_id, __smp4m_processor_id, BTFIXUPCALL_NORM); +} diff --git a/arch/sparc/kernel/sunos_ioctl.c b/arch/sparc/kernel/sunos_ioctl.c index 4f6b2accc..deb1aa79e 100644 --- a/arch/sparc/kernel/sunos_ioctl.c +++ b/arch/sparc/kernel/sunos_ioctl.c @@ -1,4 +1,4 @@ -/* $Id: sunos_ioctl.c,v 1.29 1997/09/18 10:37:31 rth Exp $ +/* $Id: sunos_ioctl.c,v 1.30 1998/01/21 06:17:32 ecd Exp $ * sunos_ioctl.c: The Linux Operating system: SunOS ioctl compatibility. * * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -114,7 +114,7 @@ asmlinkage int sunos_ioctl (int fd, unsigned long cmd, unsigned long arg) ret = sys_ioctl(fd, SIOCGIFBRDADDR, arg); goto out; case _IOW('i', 24, struct ifreq): - ret = sys_ioctl(fd, SIOCGIFBRDADDR, arg); + ret = sys_ioctl(fd, SIOCSIFBRDADDR, arg); goto out; case _IOWR('i', 25, struct ifreq): ret = sys_ioctl(fd, SIOCGIFNETMASK, arg); diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index 5b82aa8eb..2fe56b344 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.38 1998/01/09 16:42:48 jj Exp $ +/* $Id: sys_sparc.c,v 1.40 1998/03/28 08:29:26 davem Exp $ * linux/arch/sparc/kernel/sys_sparc.c * * This file contains various random system calls that @@ -10,8 +10,9 @@ #include <linux/types.h> #include <linux/sched.h> #include <linux/config.h> -#include <linux/fs.h> #include <linux/mm.h> +#include <linux/fs.h> +#include <linux/file.h> #include <linux/sem.h> #include <linux/msg.h> #include <linux/shm.h> @@ -39,7 +40,7 @@ asmlinkage unsigned long sparc_brk(unsigned long brk) unsigned long ret; lock_kernel(); - if(sparc_cpu_model == sun4c) { + if(ARCH_SUN4C_SUN4) { if(brk >= 0x20000000 && brk < 0xe0000000) { ret = current->mm->brk; goto out; @@ -192,31 +193,34 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, lock_kernel(); if (!(flags & MAP_ANONYMOUS)) { - if (fd >= NR_OPEN || !(file = current->files->fd[fd])){ + file = fget(fd); + if (!file) goto out; - } } retval = -ENOMEM; if(!(flags & MAP_FIXED) && !addr) { addr = get_unmapped_area(addr, len); - if(!addr){ - goto out; - } + if(!addr) + goto out_putf; } /* See asm-sparc/uaccess.h */ retval = -EINVAL; if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE))) - goto out; + goto out_putf; - if(sparc_cpu_model == sun4c) { + if(ARCH_SUN4C_SUN4) { if(((addr >= 0x20000000) && (addr < 0xe0000000))) { retval = current->mm->brk; - goto out; + goto out_putf; } } retval = do_mmap(file, addr, len, prot, flags, off); + +out_putf: + if (file) + fput(file); out: unlock_kernel(); return retval; diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c index 29f00f6b8..bd7bf5d77 100644 --- a/arch/sparc/kernel/sys_sunos.c +++ b/arch/sparc/kernel/sys_sunos.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.83 1997/12/14 23:24:28 ecd Exp $ +/* $Id: sys_sunos.c,v 1.87 1998/03/29 03:48:16 shadow Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -17,6 +17,7 @@ #include <linux/mm.h> #include <linux/swap.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/resource.h> #include <linux/ipc.h> #include <linux/shm.h> @@ -77,14 +78,19 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len, flags &= ~MAP_NORESERVE; } retval = -EBADF; - if(!(flags & MAP_ANONYMOUS)) - if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd])) + if(!(flags & MAP_ANONYMOUS)) { + if (fd >= SUNOS_NR_OPEN) goto out; + file = fget(fd); + if (!file) + goto out; + } + retval = -ENOMEM; if(!(flags & MAP_FIXED) && !addr) { addr = get_unmapped_area(addr, len); if(!addr) - goto out; + goto out_putf; } /* If this is ld.so or a shared library doing an mmap * of /dev/zero, transform it into an anonymous mapping. @@ -105,18 +111,22 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len, /* See asm-sparc/uaccess.h */ retval = -EINVAL; if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE))) - goto out; + goto out_putf; - if(sparc_cpu_model == sun4c) { + if(ARCH_SUN4C_SUN4) { if(((addr >= 0x20000000) && (addr < 0xe0000000))) { retval = current->mm->brk; - goto out; + goto out_putf; } } retval = do_mmap(file, addr, len, prot, flags, off); if(!ret_type) retval = ((retval < PAGE_OFFSET) ? 0 : retval); + +out_putf: + if (file) + fput(file); out: unlock_kernel(); return retval; @@ -139,7 +149,7 @@ asmlinkage int sunos_brk(unsigned long brk) unsigned long newbrk, oldbrk; lock_kernel(); - if(sparc_cpu_model == sun4c) { + if(ARCH_SUN4C_SUN4) { if(brk >= 0x20000000 && brk < 0xe0000000) { goto out; } @@ -423,39 +433,48 @@ static int sunos_filldir(void * __buf, const char * name, int namlen, asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt) { struct file * file; + struct inode * inode; struct sunos_dirent * lastdirent; struct sunos_dirent_callback buf; int error = -EBADF; lock_kernel(); - if(fd >= SUNOS_NR_OPEN) + if (fd >= SUNOS_NR_OPEN) goto out; - file = current->files->fd[fd]; - if(!file) + file = fget(fd); + if (!file) goto out; error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; + goto out_putf; error = -EINVAL; - if(cnt < (sizeof(struct sunos_dirent) + 255)) - goto out; + if (cnt < (sizeof(struct sunos_dirent) + 255)) + goto out_putf; buf.curr = (struct sunos_dirent *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; + + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, sunos_filldir); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; + lastdirent = buf.previous; error = buf.error; if (lastdirent) { put_user(file->f_pos, &lastdirent->d_off); error = cnt - buf.count; } + +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -503,39 +522,48 @@ static int sunos_filldirentry(void * __buf, const char * name, int namlen, asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsigned int *basep) { struct file * file; + struct inode * inode; struct sunos_direntry * lastdirent; struct sunos_direntry_callback buf; int error = -EBADF; lock_kernel(); - if(fd >= SUNOS_NR_OPEN) + if (fd >= SUNOS_NR_OPEN) goto out; - file = current->files->fd[fd]; - if(!file) + file = fget(fd); + if (!file) goto out; error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; + goto out_putf; error = -EINVAL; if(cnt < (sizeof(struct sunos_direntry) + 255)) - goto out; + goto out_putf; buf.curr = (struct sunos_direntry *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; + + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, sunos_filldirentry); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; + lastdirent = buf.previous; error = buf.error; if (lastdirent) { put_user(file->f_pos, basep); error = cnt - buf.count; } + +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -669,6 +697,15 @@ asmlinkage int sunos_select(int width, fd_set *inp, fd_set *outp, fd_set *exp, s lock_kernel(); current->personality |= STICKY_TIMEOUTS; ret = sys_select (width, inp, outp, exp, tvp); + if (ret == -EINTR && tvp) { + time_t sec, usec; + + __get_user(sec, &tvp->tv_sec); + __get_user(usec, &tvp->tv_usec); + + if (sec == 0 && usec == 0) + ret = 0; + } unlock_kernel(); return ret; } @@ -720,7 +757,7 @@ extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); /* Bind the socket on a local reserved port and connect it to the * remote server. This on Linux/i386 is done by the mount program, - * not by the kernel. + * not by the kernel. */ static int sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr) @@ -728,16 +765,16 @@ sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr) struct sockaddr_in local; struct sockaddr_in server; int try_port; - int ret; struct socket *socket; - struct dentry *dentry; struct inode *inode; struct file *file; + int ret, result = 0; - file = current->files->fd [fd]; - dentry = file->f_dentry; - if(!dentry || !(inode = dentry->d_inode)) - return 0; + file = fget(fd); + if (!file) + goto out; + if (!file->f_dentry || !(inode = file->f_dentry->d_inode)) + goto out_putf; socket = &inode->u.socket_i; local.sin_family = AF_INET; @@ -752,7 +789,7 @@ sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr) } while (ret && try_port > (1024 / 2)); if (ret) - return 0; + goto out_putf; server.sin_family = AF_INET; server.sin_addr = addr->sin_addr; @@ -761,9 +798,13 @@ sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr) /* Call sys_connect */ ret = socket->ops->connect (socket, (struct sockaddr *) &server, sizeof (server), file->f_flags); - if (ret < 0) - return 0; - return 1; + if (ret >= 0) + result = 1; + +out_putf: + fput(file); +out: + return result; } static int get_default (int value, int def_value) @@ -1139,10 +1180,13 @@ asmlinkage int sunos_open(const char *filename, int flags, int mode) file descriptors that have been set non-blocking using 4.2BSD style calls. (tridge) */ -static inline int check_nonblock(int ret,int fd) +static inline int check_nonblock(int ret, int fd) { - if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY)) - return -SUNOS_EWOULDBLOCK; + if (ret == -EAGAIN) { + struct file * file = fcheck(fd); + if (file && (file->f_flags & O_NDELAY)) + ret = -SUNOS_EWOULDBLOCK; + } return ret; } @@ -1215,12 +1259,41 @@ asmlinkage int sunos_send(int fd, void * buff, int len, unsigned flags) return ret; } +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); + +asmlinkage int sunos_socket(int family, int type, int protocol) +{ + int ret, one = 1; + + lock_kernel(); + ret = sys_socket(family, type, protocol); + if (ret < 0) + goto out; + + sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT, + (char *)&one, sizeof(one)); +out: + unlock_kernel(); + return ret; +} + asmlinkage int sunos_accept(int fd, struct sockaddr *sa, int *addrlen) { - int ret; + int ret, one = 1; lock_kernel(); - ret = check_nonblock(sys_accept(fd,sa,addrlen),fd); + while (1) { + ret = check_nonblock(sys_accept(fd,sa,addrlen),fd); + if (ret != -ENETUNREACH && ret != -EHOSTUNREACH) + break; + } + if (ret < 0) + goto out; + + sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT, + (char *)&one, sizeof(one)); +out: unlock_kernel(); return ret; } diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S index 5ea64d2a4..320264255 100644 --- a/arch/sparc/kernel/systbls.S +++ b/arch/sparc/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.68 1997/12/24 17:26:38 ecd Exp $ +/* $Id: systbls.S,v 1.71 1998/03/24 06:25:06 ecd Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -23,9 +23,9 @@ C_LABEL(sys_call_table): /*5*/ .long C_LABEL(sys_open), C_LABEL(sys_close), C_LABEL(sys_wait4) .long C_LABEL(sys_creat), C_LABEL(sys_link) /*10*/ .long C_LABEL(sys_unlink), C_LABEL(sunos_execv), C_LABEL(sys_chdir) - .long C_LABEL(sys_nis_syscall), C_LABEL(sys_mknod) -/*15*/ .long C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sparc_brk) - .long C_LABEL(sys_nis_syscall), C_LABEL(sys_lseek) + .long C_LABEL(sys_xstat), C_LABEL(sys_mknod) +/*15*/ .long C_LABEL(sys_chmod), C_LABEL(sys_lchown), C_LABEL(sparc_brk) + .long C_LABEL(sys_xmknod), C_LABEL(sys_lseek) /*20*/ .long C_LABEL(sys_getpid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_setuid), C_LABEL(sys_getuid) /*25*/ .long C_LABEL(sys_time), C_LABEL(sys_ptrace), C_LABEL(sys_alarm) @@ -137,7 +137,7 @@ C_LABEL(sunos_sys_table): .long C_LABEL(sys_close), C_LABEL(sunos_wait4), C_LABEL(sys_creat) .long C_LABEL(sys_link), C_LABEL(sys_unlink), C_LABEL(sunos_execv) .long C_LABEL(sys_chdir), C_LABEL(sunos_nosys), C_LABEL(sys_mknod) - .long C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sunos_brk) + .long C_LABEL(sys_chmod), C_LABEL(sys_lchown), C_LABEL(sunos_brk) .long C_LABEL(sunos_nosys), C_LABEL(sys_lseek), C_LABEL(sunos_getpid) .long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys) .long C_LABEL(sunos_getuid), C_LABEL(sunos_nosys), C_LABEL(sys_ptrace) @@ -164,7 +164,7 @@ C_LABEL(sunos_sys_table): .long C_LABEL(sys_getitimer), C_LABEL(sys_gethostname), C_LABEL(sys_sethostname) .long C_LABEL(sunos_getdtablesize), C_LABEL(sys_dup2), C_LABEL(sunos_nop) .long C_LABEL(sys_fcntl), C_LABEL(sunos_select), C_LABEL(sunos_nop) - .long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_socket) + .long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sunos_socket) .long C_LABEL(sys_connect), C_LABEL(sunos_accept) /*100*/ .long C_LABEL(sys_getpriority), C_LABEL(sunos_send), C_LABEL(sunos_recv) .long C_LABEL(sunos_nosys), C_LABEL(sys_bind), C_LABEL(sunos_setsockopt) diff --git a/arch/sparc/kernel/tadpole.c b/arch/sparc/kernel/tadpole.c index 03fe3cff9..f618bf53b 100644 --- a/arch/sparc/kernel/tadpole.c +++ b/arch/sparc/kernel/tadpole.c @@ -6,6 +6,7 @@ #include <linux/string.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/init.h> #include <asm/asi.h> #include <asm/oplib.h> @@ -94,7 +95,7 @@ static void swift_clockstop(void) clk_ctrl[0] = 0; } -void clock_stop_probe(void) +__initfunc(void clock_stop_probe(void)) { unsigned int node, clk_nd; char name[20]; diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index 77401391e..5eb49e22c 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.29 1997/04/18 09:48:44 davem Exp $ +/* $Id: time.c,v 1.32 1998/03/23 08:41:13 jj Exp $ * linux/arch/sparc/kernel/time.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -24,6 +24,10 @@ #include <asm/system.h> #include <asm/irq.h> #include <asm/io.h> +#include <asm/idprom.h> +#include <asm/machines.h> +#include <asm/sun4paddr.h> +#include <asm/page.h> enum sparc_clock_type sp_clock_typ; struct mostek48t02 *mstk48t02_regs = 0; @@ -88,7 +92,7 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon, } /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ -static void kick_start_clock(void) +__initfunc(static void kick_start_clock(void)) { register struct mostek48t02 *regs = mstk48t02_regs; unsigned char sec; @@ -137,7 +141,7 @@ static void kick_start_clock(void) } /* Return nonzero if the clock chip battery is low. */ -static int has_low_battery(void) +static __inline__ int has_low_battery(void) { register struct mostek48t02 *regs = mstk48t02_regs; unsigned char data1, data2; @@ -150,8 +154,24 @@ static int has_low_battery(void) return (data1 == data2); /* Was the write blocked? */ } -/* Probe for the real time clock chip. */ -__initfunc(static void clock_probe(void)) +/* Probe for the real time clock chip on Sun4/300. */ +static __inline__ void sun4_clock_probe(void) +{ + sp_clock_typ = MSTK48T02; + mstk48t02_regs = (struct mostek48t02 *) + sparc_alloc_io(SUN4_300_MOSTEK_PHYSADDR, 0, + sizeof(*mstk48t02_regs), + "clock", 0x0, 0x0); + mstk48t08_regs = 0; /* To catch weirdness */ + /* Kick start the clock if it is completely stopped. */ + if (mstk48t02_regs->sec & MSTK_STOP) { + kick_start_clock(); + } + +} + +/* Probe for the mostek real time clock chip. */ +static __inline__ void clock_probe(void) { struct linux_prom_registers clk_reg[2]; char model[128]; @@ -247,7 +267,11 @@ __initfunc(void time_init(void)) return; #endif - clock_probe(); + if (ARCH_SUN4) + sun4_clock_probe(); + else + clock_probe(); + init_timers(timer_interrupt); mregs = mstk48t02_regs; diff --git a/arch/sparc/kernel/trampoline.S b/arch/sparc/kernel/trampoline.S index 9ee5bd14a..3515e265d 100644 --- a/arch/sparc/kernel/trampoline.S +++ b/arch/sparc/kernel/trampoline.S @@ -1,7 +1,8 @@ -/* $Id: trampoline.S,v 1.9 1997/05/01 08:53:34 davem Exp $ +/* $Id: trampoline.S,v 1.12 1998/03/19 15:36:38 jj Exp $ * trampoline.S: SMP cpu boot-up trampoline code. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <asm/cprefix.h> @@ -12,9 +13,12 @@ #include <asm/ptrace.h> #include <asm/vaddrs.h> #include <asm/contregs.h> +#include <asm/init.h> + .globl C_LABEL(sun4m_cpu_startup), C_LABEL(__smp4m_processor_id) + .globl C_LABEL(sun4d_cpu_startup), C_LABEL(__smp4d_processor_id) - .text + __INIT .align 4 /* When we start up a cpu for the first time it enters this routine. @@ -22,8 +26,7 @@ * in and sets PIL in %psr to 15, no irqs. */ - .globl C_LABEL(sparc_cpu_startup) -C_LABEL(sparc_cpu_startup): +C_LABEL(sun4m_cpu_startup): cpu1_startup: sethi %hi(C_LABEL(trapbase_cpu1)), %g3 b 1f @@ -60,9 +63,8 @@ cpu3_startup: and %g4, 0xc, %g4 ld [%g5 + %g4], %g6 - mov 1, %sp - sll %sp, (PAGE_SHIFT + 1), %sp - sub %sp, REGWIN_SZ, %sp + sethi %hi(TASK_UNION_SIZE - REGWIN_SZ), %sp + or %sp, %lo(TASK_UNION_SIZE - REGWIN_SZ), %sp add %g6, %sp, %sp /* Turn on traps (PSR_ET). */ @@ -77,11 +79,84 @@ cpu3_startup: nop /* Start this processor. */ - call C_LABEL(smp_callin) + call C_LABEL(smp4m_callin) nop + b,a smp_do_cpu_idle + + .text + .align 4 + +smp_do_cpu_idle: call C_LABEL(cpu_idle) mov 0, %o0 call C_LABEL(cpu_panic) nop + +C_LABEL(__smp4m_processor_id): + rd %tbr, %g2 + srl %g2, 12, %g2 + and %g2, 3, %g2 + retl + mov %g1, %o7 + +C_LABEL(__smp4d_processor_id): + lda [%g0] ASI_M_VIKING_TMP1, %g2 + retl + mov %g1, %o7 + +/* CPUID in bootbus can be found at PA 0xff0140000 */ +#define SUN4D_BOOTBUS_CPUID 0xf0140000 + + __INIT + .align 4 + +C_LABEL(sun4d_cpu_startup): + /* Set up a sane %psr -- PIL<0xf> S<0x1> PS<0x1> CWP<0x0> */ + set (PSR_PIL | PSR_S | PSR_PS), %g1 + wr %g1, 0x0, %psr ! traps off though + WRITE_PAUSE + + /* Our %wim is one behind CWP */ + mov 2, %g1 + wr %g1, 0x0, %wim + WRITE_PAUSE + + /* Set tbr - we use just one trap table. */ + set C_LABEL(trapbase), %g1 + wr %g1, 0x0, %tbr + WRITE_PAUSE + + /* Get our CPU id out of bootbus */ + set SUN4D_BOOTBUS_CPUID, %g3 + lduba [%g3] ASI_M_CTL, %g3 + and %g3, 0xf8, %g3 + srl %g3, 3, %g1 + sta %g1, [%g0] ASI_M_VIKING_TMP1 + + /* Give ourselves a stack and curptr. */ + set C_LABEL(current_set), %g5 + srl %g3, 1, %g4 + ld [%g5 + %g4], %g6 + + sethi %hi(TASK_UNION_SIZE - REGWIN_SZ), %sp + or %sp, %lo(TASK_UNION_SIZE - REGWIN_SZ), %sp + add %g6, %sp, %sp + + /* Turn on traps (PSR_ET). */ + rd %psr, %g1 + wr %g1, PSR_ET, %psr ! traps on + WRITE_PAUSE + + /* Init our caches, etc. */ + set C_LABEL(poke_srmmu), %g5 + ld [%g5], %g5 + call %g5 + nop + + /* Start this processor. */ + call C_LABEL(smp4d_callin) + nop + + b,a smp_do_cpu_idle diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c index 19a3afbd0..015d05357 100644 --- a/arch/sparc/kernel/traps.c +++ b/arch/sparc/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.53 1997/01/25 02:43:05 miguel Exp $ +/* $Id: traps.c,v 1.56 1998/04/06 16:08:32 jj Exp $ * arch/sparc/kernel/traps.c * * Copyright 1995 David S. Miller (davem@caip.rutgers.edu) @@ -62,6 +62,14 @@ void sun4m_nmi(struct pt_regs *regs) prom_halt(); } +void sun4d_nmi(struct pt_regs *regs) +{ + printk("Aieee: sun4d NMI received!\n"); + printk("you lose buddy boy...\n"); + show_regs(regs); + prom_halt(); +} + void instruction_dump (unsigned long *pc) { int i; @@ -229,10 +237,13 @@ static unsigned long fake_fsr; static unsigned long fake_queue[32] __attribute__ ((aligned (8))); static unsigned long fake_depth; +extern int do_mathemu(struct pt_regs *, struct task_struct *); + void do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc, unsigned long psr) { static calls = 0; + int ret; #ifndef __SMP__ struct task_struct *fpt = last_task_used_math; #else @@ -255,6 +266,40 @@ void do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc, } fpsave(&fpt->tss.float_regs[0], &fpt->tss.fsr, &fpt->tss.fpqueue[0], &fpt->tss.fpqdepth); +#ifdef DEBUG_FPU + printk("Hmm, FP exception, fsr was %016lx\n", fpt->tss.fsr); +#endif + + switch ((fpt->tss.fsr & 0x1c000)) { + /* switch on the contents of the ftt [floating point trap type] field */ +#ifdef DEBUG_FPU + case (1 << 14): + printk("IEEE_754_exception\n"); + break; +#endif + case (2 << 14): /* unfinished_FPop (underflow & co) */ + case (3 << 14): /* unimplemented_FPop (quad stuff, maybe sqrt) */ + ret = do_mathemu(regs, fpt); + break; +#ifdef DEBUG_FPU + case (4 << 14): + printk("sequence_error (OS bug...)\n"); + break; + case (5 << 14): + printk("hardware_error (uhoh!)\n"); + break; + case (6 << 14): + printk("invalid_fp_register (user error)\n"); + break; +#endif /* DEBUG_FPU */ + } + /* If we successfully emulated the FPop, we pretend the trap never happened :-> */ + if (ret) { + fpload(¤t->tss.float_regs[0], ¤t->tss.fsr); + return; + } + /* nope, better SIGFPE the offending process... */ + fpt->tss.sig_address = pc; fpt->tss.sig_desc = SUBSIG_FPERROR; /* as good as any */ #ifdef __SMP__ @@ -331,19 +376,6 @@ void handle_cp_disabled(struct pt_regs *regs, unsigned long pc, unsigned long np unlock_kernel(); } -void handle_bad_flush(struct pt_regs *regs, unsigned long pc, unsigned long npc, - unsigned long psr) -{ - lock_kernel(); -#ifdef TRAP_DEBUG - printk("Unimplemented FLUSH Exception at PC %08lx NPC %08lx PSR %08lx\n", - pc, npc, psr); -#endif - printk("INSTRUCTION=%08lx\n", *((unsigned long *) regs->pc)); - send_sig(SIGILL, current, 1); - unlock_kernel(); -} - void handle_cp_exception(struct pt_regs *regs, unsigned long pc, unsigned long npc, unsigned long psr) { diff --git a/arch/sparc/kernel/wof.S b/arch/sparc/kernel/wof.S index 890676bfb..1d321d521 100644 --- a/arch/sparc/kernel/wof.S +++ b/arch/sparc/kernel/wof.S @@ -1,4 +1,4 @@ -/* $Id: wof.S,v 1.36 1997/05/01 08:53:35 davem Exp $ +/* $Id: wof.S,v 1.38 1998/02/06 14:14:22 jj Exp $ * wof.S: Sparc window overflow handler. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -234,9 +234,10 @@ spwin_user_stack_is_bolixed: spnwin_patch3: and %twin_tmp, 0xff, %twin_tmp ! patched on 7win Sparcs st %twin_tmp, [%curptr + AOFF_task_tss + AOFF_thread_uwinmask] - mov 1, %sp - sll %sp, (PAGE_SHIFT + 1), %sp - sub %sp, (TRACEREG_SZ + REGWIN_SZ), %sp +#define STACK_OFFSET (TASK_UNION_SIZE - TRACEREG_SZ - REGWIN_SZ) + + sethi %hi(STACK_OFFSET), %sp + or %sp, %lo(STACK_OFFSET), %sp add %curptr, %sp, %sp /* Restore the saved globals and build a pt_regs frame. */ @@ -244,9 +245,8 @@ spnwin_patch3: and %twin_tmp, 0xff, %twin_tmp ! patched on 7win Sparcs mov %saved_g6, %g6 STORE_PT_ALL(sp, t_psr, t_pc, t_npc, g1) - mov 1, %g6 - sll %g6, (PAGE_SHIFT + 1), %g6 - sub %g6, (TRACEREG_SZ + REGWIN_SZ), %g6 + sethi %hi(STACK_OFFSET), %g6 + or %g6, %lo(STACK_OFFSET), %g6 sub %sp, %g6, %g6 /* Turn on traps and call c-code to deal with it. */ @@ -394,9 +394,8 @@ C_LABEL(spwin_srmmu_stackchk): * kernel is page aligned, which should always be the case. */ /* Check results of callers andcc %sp, 0x7, %g0 */ - sethi %hi(C_LABEL(page_offset)), %glob_tmp bne spwin_user_stack_is_bolixed - ld [%glob_tmp + %lo(C_LABEL(page_offset))], %glob_tmp + GET_PAGE_OFFSET(glob_tmp) cmp %glob_tmp, %sp bleu spwin_user_stack_is_bolixed mov AC_M_SFSR, %glob_tmp diff --git a/arch/sparc/kernel/wuf.S b/arch/sparc/kernel/wuf.S index cb407aa69..bfa5b5191 100644 --- a/arch/sparc/kernel/wuf.S +++ b/arch/sparc/kernel/wuf.S @@ -1,4 +1,4 @@ -/* $Id: wuf.S,v 1.34 1997/05/01 08:53:36 davem Exp $ +/* $Id: wuf.S,v 1.37 1998/02/19 21:25:50 ecd Exp $ * wuf.S: Window underflow trap handler for the Sparc. * * Copyright (C) 1995 David S. Miller @@ -138,6 +138,8 @@ fwin_from_user: C_LABEL(fwin_mmu_patchme): b C_LABEL(sun4c_fwin_stackchk) andcc %sp, 0x7, %g0 +#define STACK_OFFSET (TASK_UNION_SIZE - TRACEREG_SZ - REGWIN_SZ) + fwin_user_stack_is_bolixed: /* LOCATION: Window 'W' */ @@ -146,9 +148,8 @@ fwin_user_stack_is_bolixed: */ LOAD_CURRENT(l4, l5) - mov 1, %l5 - sll %l5, (PAGE_SHIFT + 1), %l5 - sub %l5, (TRACEREG_SZ + REGWIN_SZ), %l5 + sethi %hi(STACK_OFFSET), %l5 + or %l5, %lo(STACK_OFFSET), %l5 add %l4, %l5, %l5 /* Store globals into pt_regs frame. */ @@ -169,10 +170,9 @@ fwin_user_stack_is_bolixed: /* LOCATION: Window 'T' */ - mov 1, %sp - sll %sp, (PAGE_SHIFT + 1), %sp - sub %sp, (TRACEREG_SZ + REGWIN_SZ), %sp - add %curptr, %sp, %sp + sethi %hi(STACK_OFFSET), %l5 + or %l5, %lo(STACK_OFFSET), %l5 + add %curptr, %l5, %sp /* Build rest of pt_regs. */ STORE_PT_INS(sp) @@ -299,9 +299,8 @@ C_LABEL(srmmu_fwin_stackchk): /* LOCATION: Window 'W' */ /* Caller did 'andcc %sp, 0x7, %g0' */ - sethi %hi(C_LABEL(page_offset)), %l5 bne fwin_user_stack_is_bolixed - ld [%l5 + %lo(C_LABEL(page_offset))], %l5 + GET_PAGE_OFFSET(l5) /* Check if the users stack is in kernel vma, then our * trial and error technique below would succeed for diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile index cefe7a851..6ec986c86 100644 --- a/arch/sparc/lib/Makefile +++ b/arch/sparc/lib/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.24 1997/05/08 17:45:26 davem Exp $ +# $Id: Makefile,v 1.25 1998/01/30 10:58:43 jj Exp $ # Makefile for Sparc library files.. # @@ -16,19 +16,19 @@ lib.a: $(OBJS) sync checksum.o: checksum.S - $(CC) -ansi -c -o checksum.o checksum.S + $(CC) -D__ASSEMBLY__ -ansi -c -o checksum.o checksum.S memcpy.o: memcpy.S $(CC) -D__ASSEMBLY__ -ansi -c -o memcpy.o memcpy.S memcmp.o: memcmp.S - $(CC) -ansi -c -o memcmp.o memcmp.S + $(CC) -D__ASSEMBLY__ -ansi -c -o memcmp.o memcmp.S memscan.o: memscan.S - $(CC) -ansi -c -o memscan.o memscan.S + $(CC) -D__ASSEMBLY__ -ansi -c -o memscan.o memscan.S strncmp.o: strncmp.S - $(CC) -ansi -c -o strncmp.o strncmp.S + $(CC) -D__ASSEMBLY__ -ansi -c -o strncmp.o strncmp.S strncpy_from_user.o: strncpy_from_user.S $(CC) -D__ASSEMBLY__ -ansi -c -o strncpy_from_user.o strncpy_from_user.S @@ -40,7 +40,7 @@ copy_user.o: copy_user.S $(CC) -D__ASSEMBLY__ -ansi -c -o copy_user.o copy_user.S blockops.o: blockops.S - $(CC) -ansi -c -o blockops.o blockops.S + $(CC) -D__ASSEMBLY__ -ansi -c -o blockops.o blockops.S memset.o: memset.S $(CC) -D__ASSEMBLY__ -ansi -c -o memset.o memset.S @@ -73,34 +73,34 @@ bitops.o: bitops.S endif strlen.o: strlen.S - $(CC) -ansi -c -o strlen.o strlen.S + $(CC) -D__ASSEMBLY__ -ansi -c -o strlen.o strlen.S divdi3.o: divdi3.S - $(CC) -ansi -c -o divdi3.o divdi3.S + $(CC) -D__ASSEMBLY__ -ansi -c -o divdi3.o divdi3.S udivdi3.o: udivdi3.S - $(CC) -ansi -c -o udivdi3.o udivdi3.S + $(CC) -D__ASSEMBLY__ -ansi -c -o udivdi3.o udivdi3.S mul.o: mul.S - $(CC) -c -o mul.o mul.S + $(CC) -D__ASSEMBLY__ -c -o mul.o mul.S rem.o: rem.S - $(CC) -DST_DIV0=0x2 -c -o rem.o rem.S + $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o rem.o rem.S sdiv.o: sdiv.S - $(CC) -DST_DIV0=0x2 -c -o sdiv.o sdiv.S + $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o sdiv.o sdiv.S udiv.o: udiv.S - $(CC) -DST_DIV0=0x2 -c -o udiv.o udiv.S + $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o udiv.o udiv.S umul.o: umul.S - $(CC) -c -o umul.o umul.S + $(CC) -D__ASSEMBLY__ -c -o umul.o umul.S urem.o: urem.S - $(CC) -DST_DIV0=0x2 -c -o urem.o urem.S + $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o urem.o urem.S ashrdi3.o: ashrdi3.S - $(CC) -c -o ashrdi3.o ashrdi3.S + $(CC) -D__ASSEMBLY__ -c -o ashrdi3.o ashrdi3.S dep: diff --git a/arch/sparc/lib/atomic.S b/arch/sparc/lib/atomic.S index 89bf2c5fb..4e4aa0646 100644 --- a/arch/sparc/lib/atomic.S +++ b/arch/sparc/lib/atomic.S @@ -10,8 +10,9 @@ .text .align 4 - .globl ___xchg32 -___xchg32: +#ifndef __SMP__ + .globl ___xchg32_sun4c +___xchg32_sun4c: rd %psr, %g3 andcc %g3, PSR_PIL, %g0 bne 1f @@ -27,9 +28,16 @@ ___xchg32: nop; nop; nop 1: mov %g7, %g2 - jmpl %o7, %g0 /* Note, not + 0x8, see call in system.h */ + jmpl %o7 + 8, %g0 mov %g4, %o7 + .globl ___xchg32_sun4md +___xchg32_sun4md: + swap [%g1], %g2 + jmpl %o7 + 8, %g0 + mov %g4, %o7 +#endif + /* Read asm-sparc/atomic.h carefully to understand how this works for SMP. * Really, some things here for SMP are overly clever, go read the header. */ diff --git a/arch/sparc/lib/blockops.S b/arch/sparc/lib/blockops.S index a5a4bffad..3f09ec1db 100644 --- a/arch/sparc/lib/blockops.S +++ b/arch/sparc/lib/blockops.S @@ -1,10 +1,11 @@ -/* $Id: blockops.S,v 1.7 1997/05/20 07:58:28 jj Exp $ +/* $Id: blockops.S,v 1.8 1998/01/30 10:58:44 jj Exp $ * blockops.S: Common block zero optimized routines. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ #include <asm/cprefix.h> +#include <asm/page.h> /* Zero out 64 bytes of memory at (buf + offset). * Assumes %g1 contains zero. @@ -53,7 +54,7 @@ C_LABEL(bzero_1page): /* %o0 = buf */ or %g0, %g0, %g1 or %o0, %g0, %o1 - or %g0, 0x10, %g2 + or %g0, (PAGE_SIZE >> 8), %g2 1: BLAST_BLOCK(%o0, 0x00) BLAST_BLOCK(%o0, 0x40) @@ -70,7 +71,7 @@ C_LABEL(__copy_1page): /* NOTE: If you change the number of insns of this routine, please check * arch/sparc/mm/hypersparc.S */ /* %o0 = dst, %o1 = src */ - or %g0, 0x10, %g1 + or %g0, (PAGE_SIZE >> 8), %g1 1: MIRROR_BLOCK(%o0, %o1, 0x00, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5) MIRROR_BLOCK(%o0, %o1, 0x20, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5) diff --git a/arch/sparc/math-emu/.cvsignore b/arch/sparc/math-emu/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/arch/sparc/math-emu/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/arch/sparc/math-emu/Makefile b/arch/sparc/math-emu/Makefile new file mode 100644 index 000000000..d7642b2e9 --- /dev/null +++ b/arch/sparc/math-emu/Makefile @@ -0,0 +1,37 @@ +# +# Makefile for the FPU instruction emulation. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := math-emu.o +O_OBJS := math.o ashldi3.o fabss.o faddd.o faddq.o fadds.o \ + fcmpd.o fcmped.o fcmpeq.o fcmpes.o fcmpq.o fcmps.o \ + fdivd.o fdivq.o fdivs.o fdmulq.o fdtoi.o fdtoq.o \ + fdtos.o fitoq.o fmovs.o fmuld.o fmulq.o fmuls.o \ + fnegs.o fqtod.o fqtoi.o fqtos.o fsmuld.o fsqrtd.o \ + fsqrtq.o fsqrts.o fstod.o fstoi.o fstoq.o fsubd.o \ + fsubq.o fsubs.o udivmodti4.o + +LINKS := double.h faddd.c faddq.c fadds.c fdivd.c fdivq.c fdivs.c \ + fdtoi.c fitoq.c fmuld.c fmulq.c fmuls.c fqtoi.c \ + fsqrtd.c fsqrtq.c fsqrts.c fstoi.c fsubd.c \ + fsubq.c fsubs.c op-1.h op-2.h op-4.h op-common.h quad.h \ + single.h soft-fp.h udivmodti4.c + +.S.s: + $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s + +.S.o: + $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + +include $(TOPDIR)/Rules.make + +symlinks: + ln -sf $(patsubst %,../../sparc64/math-emu/%,$(LINKS)) . + +cleansymlinks: + rm -f $(LINKS) diff --git a/arch/sparc/math-emu/ashldi3.S b/arch/sparc/math-emu/ashldi3.S new file mode 100644 index 000000000..eab1d0972 --- /dev/null +++ b/arch/sparc/math-emu/ashldi3.S @@ -0,0 +1,36 @@ +/* $Id: ashldi3.S,v 1.1 1998/04/06 16:09:28 jj Exp $ + * ashldi3.S: Math-emu code creates all kinds of references to + * this little routine on the sparc with gcc. + * + * Copyright (C) 1998 Jakub Jelinek(jj@ultra.linux.cz) + */ + +#include <asm/cprefix.h> + + .globl C_LABEL(__ashldi3) +C_LABEL(__ashldi3): + tst %o2 + be 3f + mov 32, %g2 + + sub %g2, %o2, %g2 + + tst %g2 + bg 1f + srl %o1, %g2, %g3 + + clr %o5 + neg %g2 + ba 2f + sll %o1, %g2, %o4 + +1: + sll %o1, %o2, %o5 + srl %o0, %o2, %g2 + or %g2, %g3, %o4 +2: + mov %o4, %o0 + mov %o5, %o1 +3: + jmpl %o7 + 8, %g0 + nop diff --git a/arch/sparc/math-emu/fabss.c b/arch/sparc/math-emu/fabss.c new file mode 100644 index 000000000..accfd4f59 --- /dev/null +++ b/arch/sparc/math-emu/fabss.c @@ -0,0 +1,6 @@ +int FABSS(unsigned long *rd, unsigned long *rs2) +{ + /* Clear the sign bit (high bit of word 0) */ + rd[0] = rs2[0] & 0x7fffffffUL; + return 1; +} diff --git a/arch/sparc/math-emu/fcmpd.c b/arch/sparc/math-emu/fcmpd.c new file mode 100644 index 000000000..3a9926575 --- /dev/null +++ b/arch/sparc/math-emu/fcmpd.c @@ -0,0 +1,18 @@ +#include "soft-fp.h" +#include "double.h" + +int FCMPD(void *rd, void *rs2, void *rs1) +{ + FP_DECL_D(A); FP_DECL_D(B); + long ret; + unsigned long *fsr = rd; + + __FP_UNPACK_D(A, rs1); + __FP_UNPACK_D(B, rs2); + FP_CMP_D(ret, B, A, 2); + if (ret == -1) + ret = 2; + + *fsr = (*fsr & ~0xc00) | (ret << 10); + return 1; +} diff --git a/arch/sparc/math-emu/fcmped.c b/arch/sparc/math-emu/fcmped.c new file mode 100644 index 000000000..a8c188042 --- /dev/null +++ b/arch/sparc/math-emu/fcmped.c @@ -0,0 +1,18 @@ +#include "soft-fp.h" +#include "double.h" + +int FCMPED(void *rd, void *rs2, void *rs1) +{ + FP_DECL_D(A); FP_DECL_D(B); + long ret; + unsigned long *fsr = rd; + + __FP_UNPACK_D(A, rs1); + __FP_UNPACK_D(B, rs2); + FP_CMP_D(ret, B, A, 2); + if (ret == -1) + ret = 2; + + *fsr = (*fsr & ~0xc00) | (ret << 10); + return 1; +} diff --git a/arch/sparc/math-emu/fcmpeq.c b/arch/sparc/math-emu/fcmpeq.c new file mode 100644 index 000000000..c109c51ce --- /dev/null +++ b/arch/sparc/math-emu/fcmpeq.c @@ -0,0 +1,18 @@ +#include "soft-fp.h" +#include "quad.h" + +int FCMPEQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_Q(A); FP_DECL_Q(B); + long ret; + unsigned long fsr; + + __FP_UNPACK_Q(A, rs1); + __FP_UNPACK_Q(B, rs2); + FP_CMP_Q(ret, B, A, 3); + if (ret == -1) ret = 2; + fsr = *(unsigned long *)rd; + fsr &= ~0xc00; fsr |= (ret << 10); + *(unsigned long *)rd = fsr; + return 1; +} diff --git a/arch/sparc/math-emu/fcmpes.c b/arch/sparc/math-emu/fcmpes.c new file mode 100644 index 000000000..e20884cfd --- /dev/null +++ b/arch/sparc/math-emu/fcmpes.c @@ -0,0 +1,18 @@ +#include "soft-fp.h" +#include "single.h" + +int FCMPES(void *rd, void *rs2, void *rs1) +{ + FP_DECL_S(A); FP_DECL_S(B); + long ret; + unsigned long *fsr = rd; + + __FP_UNPACK_S(A, rs1); + __FP_UNPACK_S(B, rs2); + FP_CMP_S(ret, B, A, 1); + if (ret == -1) + ret = 2; + + *fsr = (*fsr & ~0xc00) | (ret << 10); + return 1; +} diff --git a/arch/sparc/math-emu/fcmpq.c b/arch/sparc/math-emu/fcmpq.c new file mode 100644 index 000000000..549f02cae --- /dev/null +++ b/arch/sparc/math-emu/fcmpq.c @@ -0,0 +1,18 @@ +#include "soft-fp.h" +#include "quad.h" + +int FCMPQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_Q(A); FP_DECL_Q(B); + long ret; + unsigned long fsr; + + __FP_UNPACK_Q(A, rs1); + __FP_UNPACK_Q(B, rs2); + FP_CMP_Q(ret, B, A, 3); + if (ret == -1) ret = 2; + fsr = *(unsigned long *)rd; + fsr &= ~0xc00; fsr |= (ret << 10); + *(unsigned long *)rd = fsr; + return 1; +} diff --git a/arch/sparc/math-emu/fcmps.c b/arch/sparc/math-emu/fcmps.c new file mode 100644 index 000000000..1b53312ae --- /dev/null +++ b/arch/sparc/math-emu/fcmps.c @@ -0,0 +1,18 @@ +#include "soft-fp.h" +#include "single.h" + +int FCMPS(void *rd, void *rs2, void *rs1) +{ + FP_DECL_S(A); FP_DECL_S(B); + long ret; + unsigned long *fsr = rd; + + __FP_UNPACK_S(A, rs1); + __FP_UNPACK_S(B, rs2); + FP_CMP_S(ret, B, A, 1); + if (ret == -1) + ret = 2; + + *fsr = (*fsr & ~0xc00) | (ret << 10); + return 1; +} diff --git a/arch/sparc/math-emu/fdmulq.c b/arch/sparc/math-emu/fdmulq.c new file mode 100644 index 000000000..1d5bc5053 --- /dev/null +++ b/arch/sparc/math-emu/fdmulq.c @@ -0,0 +1,16 @@ +#include "soft-fp.h" +#include "quad.h" +#include "double.h" + +int FDMULQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_D(IN); FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); + + __FP_UNPACK_D(IN, rs1); + FP_CONV(Q,D,4,2,A,IN); + __FP_UNPACK_D(IN, rs2); + FP_CONV(Q,D,4,2,B,IN); + FP_MUL_Q(R, A, B); + __FP_PACK_Q(rd, R); + return 1; +} diff --git a/arch/sparc/math-emu/fdtoq.c b/arch/sparc/math-emu/fdtoq.c new file mode 100644 index 000000000..84ebcf4a2 --- /dev/null +++ b/arch/sparc/math-emu/fdtoq.c @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" +#include "double.h" + +int FDTOQ(void *rd, void *rs2) +{ + FP_DECL_D(A); FP_DECL_Q(R); + + __FP_UNPACK_D(A, rs2); + FP_CONV(Q,D,4,2,R,A); + __FP_PACK_Q(rd, R); + return 1; +} diff --git a/arch/sparc/math-emu/fdtos.c b/arch/sparc/math-emu/fdtos.c new file mode 100644 index 000000000..83b8a14ed --- /dev/null +++ b/arch/sparc/math-emu/fdtos.c @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int FDTOS(void *rd, void *rs2) +{ + FP_DECL_D(A); FP_DECL_S(R); + + __FP_UNPACK_D(A, rs2); + FP_CONV(S,D,1,2,R,A); + __FP_PACK_S(rd, R); + return 1; +} diff --git a/arch/sparc/math-emu/fmovs.c b/arch/sparc/math-emu/fmovs.c new file mode 100644 index 000000000..f113c0bb1 --- /dev/null +++ b/arch/sparc/math-emu/fmovs.c @@ -0,0 +1,5 @@ +int FMOVS(unsigned long *rd, unsigned long *rs2) +{ + rd[0] = rs2[0]; + return 0; +} diff --git a/arch/sparc/math-emu/fnegs.c b/arch/sparc/math-emu/fnegs.c new file mode 100644 index 000000000..39188eea6 --- /dev/null +++ b/arch/sparc/math-emu/fnegs.c @@ -0,0 +1,7 @@ +int FNEGS(unsigned long *rd, unsigned long *rs2) +{ + /* just change the sign bit */ + rd[0] = rs2[0] ^ 0x80000000UL; + return 1; +} + diff --git a/arch/sparc/math-emu/fqtod.c b/arch/sparc/math-emu/fqtod.c new file mode 100644 index 000000000..dc5b6f9aa --- /dev/null +++ b/arch/sparc/math-emu/fqtod.c @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" +#include "double.h" + +int FQTOD(void *rd, void *rs2) +{ + FP_DECL_Q(A); FP_DECL_D(R); + + __FP_UNPACK_Q(A, rs2); + FP_CONV(D,Q,2,4,R,A); + __FP_PACK_D(rd, R); + return 1; +} diff --git a/arch/sparc/math-emu/fqtos.c b/arch/sparc/math-emu/fqtos.c new file mode 100644 index 000000000..608f57be0 --- /dev/null +++ b/arch/sparc/math-emu/fqtos.c @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" +#include "single.h" + +int FQTOS(void *rd, void *rs2) +{ + FP_DECL_Q(A); FP_DECL_S(R); + + __FP_UNPACK_Q(A, rs2); + FP_CONV(S,Q,1,4,R,A); + __FP_PACK_S(rd, R); + return 1; +} diff --git a/arch/sparc/math-emu/fsmuld.c b/arch/sparc/math-emu/fsmuld.c new file mode 100644 index 000000000..dead5a042 --- /dev/null +++ b/arch/sparc/math-emu/fsmuld.c @@ -0,0 +1,16 @@ +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int FSMULD(void *rd, void *rs2, void *rs1) +{ + FP_DECL_S(IN); FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); + + __FP_UNPACK_S(IN, rs1); + FP_CONV(D,S,2,1,A,IN); + __FP_UNPACK_S(IN, rs2); + FP_CONV(D,S,2,1,B,IN); + FP_MUL_D(R, A, B); + __FP_PACK_D(rd, R); + return 1; +} diff --git a/arch/sparc/math-emu/fstod.c b/arch/sparc/math-emu/fstod.c new file mode 100644 index 000000000..cb34329c9 --- /dev/null +++ b/arch/sparc/math-emu/fstod.c @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int FSTOD(void *rd, void *rs2) +{ + FP_DECL_S(A); FP_DECL_D(R); + + __FP_UNPACK_S(A, rs2); + FP_CONV(D,S,2,1,R,A); + __FP_PACK_D(rd, R); + return 1; +} diff --git a/arch/sparc/math-emu/fstoq.c b/arch/sparc/math-emu/fstoq.c new file mode 100644 index 000000000..081c4d4d0 --- /dev/null +++ b/arch/sparc/math-emu/fstoq.c @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" +#include "single.h" + +int FSTOQ(void *rd, void *rs2) +{ + FP_DECL_S(A); FP_DECL_Q(R); + + __FP_UNPACK_S(A, rs2); + FP_CONV(Q,S,4,1,R,A); + __FP_PACK_Q(rd, R); + return 1; +} diff --git a/arch/sparc/math-emu/math.c b/arch/sparc/math-emu/math.c new file mode 100644 index 000000000..df5c879c5 --- /dev/null +++ b/arch/sparc/math-emu/math.c @@ -0,0 +1,416 @@ +/* + * arch/sparc/math-emu/math.c + * + * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk) + * Based on the sparc64 code by Jakub Jelinek. + * + * This is a good place to start if you're trying to understand the + * emulation code, because it's pretty simple. What we do is + * essentially analyse the instruction to work out what the operation + * is and which registers are involved. We then execute the appropriate + * FXXXX function. [The floating point queue introduces a minor wrinkle; + * see below...] + * The fxxxxx.c files each emulate a single insn. They look relatively + * simple because the complexity is hidden away in an unholy tangle + * of preprocessor macros. + * + * WARNING : don't look at the macro definitions unless you + * absolutely have to! They're extremely ugly, rather complicated + * and a single line in an fxxxx.c file can expand to the equivalent + * of 30 lines or more of C. Of course, any error in those 30 lines + * is reported by the compiler as an error in the single line with the + * macro usage... + * Question: should we replace them with inline functions? + * + * The first layer of macros is single.h, double.h, quad.h. Generally + * these files define macros for working with floating point numbers + * of the three IEEE formats. FP_ADD_D(R,A,B) is for adding doubles, + * for instance. These macros are usually defined as calls to more + * generic macros (in this case _FP_ADD(D,2,R,X,Y) where the number + * of machine words required to store the given IEEE format is passed + * as a parameter. [double.h and co check the number of bits in a word + * and define FP_ADD_D & co appropriately]. + * The generic macros are defined in op-common.h. This is where all + * the grotty stuff like handling NaNs is coded. To handle the possible + * word sizes macros in op-common.h use macros like _FP_FRAC_SLL_##wc() + * where wc is the 'number of machine words' parameter (here 2). + * These are defined in the third layer of macros: op-1.h, op-2.h + * and op-4.h. These handle operations on floating point numbers composed + * of 1,2 and 4 machine words respectively. [For example, on sparc64 + * doubles are one machine word so macros in double.h eventually use + * constructs in op-1.h, but on sparc32 they use op-2.h definitions.] + * soft-fp.h is on the same level as op-common.h, and defines some + * macros which are independent of both word size and FP format. + * Finally, sfp-machine.h is the machine dependent part of the + * code: it defines the word size and what type a word is. It also + * defines how _FP_MUL_MEAT_t() maps to _FP_MUL_MEAT_n_* : op-n.h + * provide several possible flavours of multiply algorithm, most + * of which require that you supply some form of asm or C primitive to + * do the actual multiply. (such asm primitives should be defined + * in sfp-machine.h too). udivmodti4.c is the same sort of thing. + * + * There may be some errors here because I'm working from a + * SPARC architecture manual V9, and what I really want is V8... + * Also, the insns which can generate exceptions seem to be a + * greater subset of the FPops than for V9 (for example, FCMPED + * has to be emulated on V8). So I think I'm going to have + * to emulate them all just to be on the safe side... + * + * Emulation routines originate from soft-fp package, which is + * part of glibc and has appropriate copyrights in it (allegedly). + * + * NB: on sparc int == long == 4 bytes, long long == 8 bytes. + * Most bits of the kernel seem to go for long rather than int, + * so we follow that practice... + */ + +/* WISHLIST: + * + * + Replace all the macros with inline functions. These should + * have the same effect but be much easier to work with. + * + * + Emulate the IEEE exception flags. We don't currently do this + * because a) it would require significant alterations to + * the emulation macros [see the comments about _FP_NEG() + * in op-common.c and note that we'd need to invent a convention + * for passing in the flags to FXXXX fns and returning them] and + * b) SPARClinux doesn't let users access the flags anyway + * [contrast Solaris, which allows you to examine, clear or set + * the flags, and request that exceptions cause SIGFPE + * [which you then set up a signal handler for, obviously...]]. + * Erm, (b) may quite possibly be garbage. %fsr is user-writable + * so you don't need a syscall. There may or may not be library + * support. + * + * + Emulation of FMULQ, FDIVQ, FSQRTQ, FDMULQ needs to be + * written! + * + * + reindent code to conform to Linux kernel standard :-> + * + * + work out whether all the compile-time warnings are bogus + * + * + check that conversion to/from integers works + * + * + check with the SPARC architecture manual to see if we resolve + * the implementation-dependent bits of the IEEE spec in the + * same manner as the hardware. + * + * + more test cases for the test script always welcome! + * + * + illegal opcodes currently cause SIGFPEs. We should arrange + * to tell the traps.c code to SIGILL instead. Currently, + * everywhere that we return 0 should cause SIGILL, I think. + * SIGFPE should only be caused if we set an IEEE exception bit + * and the relevant trap bit is also set. (this means that + * traps.c should do this; also it should handle the case of + * IEEE exception generated directly by the hardware.) + * Should illegal_fp_register (which is a flavour of fp exception) + * cause SIGFPE or SIGILL? + * + * + the test script needs to be extended to handle the quadword + * and comparison insns. + * + * + _FP_DIV_MEAT_2_udiv_64() appears to work but it should be + * checked by somebody who understands the algorithm :-> + * + * + fpsave() saves the FP queue but fpload() doesn't reload it. + * Therefore when we context switch or change FPU ownership + * we have to check to see if the queue had anything in it and + * emulate it if it did. This is going to be a pain. + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <asm/uaccess.h> + + +#define FLOATFUNC(x) extern int x(void *,void *,void *) + +/* Current status: we don't properly emulate the difficult quadword + * insns (MUL, DIV, SQRT). + * There are also some ops involving the FP registers which we don't + * emulate: the branch on FP condition flags and the load/store to + * FP regs or FSR. I'm assuming that these will never generate traps + * (not unreasonable if there's an FPU at all; comments in the NetBSD + * kernel source agree on this point). If we wanted to allow + * purely software-emulation of the FPU with FPU totally disabled + * or non-existent, we'd have to emulate these as well. We'd also + * need to alter the fp_disabled trap handler to call the math-emu + * code appropriately. The structure of do_one_mathemu() is also + * inappropriate for these ops (as it has no way to alter the pc, + * for a start) and it might be better to special-case them in do_mathemu(). + * Oh, and you'd need to alter the traps.c code so it didn't try to + * fpsave() and fpload(). If there's genuinely no FPU then there's + * probably bits of kernel stuff that just won't work anyway... + */ + +/* The Vn labels indicate what version of the SPARC architecture gas thinks + * each insn is. This is from the binutils source :-> + */ +/* quadword instructions */ +FLOATFUNC(FSQRTQ); /* v8 NYI */ +FLOATFUNC(FADDQ); /* v8 */ +FLOATFUNC(FSUBQ); /* v8 */ +FLOATFUNC(FMULQ); /* v8 NYI */ +FLOATFUNC(FDIVQ); /* v8 NYI */ +FLOATFUNC(FDMULQ); /* v8 NYI */ +FLOATFUNC(FQTOS); /* v8 */ +FLOATFUNC(FQTOD); /* v8 */ +FLOATFUNC(FITOQ); /* v8 */ +FLOATFUNC(FSTOQ); /* v8 */ +FLOATFUNC(FDTOQ); /* v8 */ +FLOATFUNC(FQTOI); /* v8 */ +FLOATFUNC(FCMPQ); /* v8 */ +FLOATFUNC(FCMPEQ); /* v8 */ +/* single/double instructions (subnormal): should all work */ +FLOATFUNC(FSQRTS); /* v7 */ +FLOATFUNC(FSQRTD); /* v7 */ +FLOATFUNC(FADDS); /* v6 */ +FLOATFUNC(FADDD); /* v6 */ +FLOATFUNC(FSUBS); /* v6 */ +FLOATFUNC(FSUBD); /* v6 */ +FLOATFUNC(FMULS); /* v6 */ +FLOATFUNC(FMULD); /* v6 */ +FLOATFUNC(FDIVS); /* v6 */ +FLOATFUNC(FDIVD); /* v6 */ +FLOATFUNC(FSMULD); /* v8 */ +FLOATFUNC(FDTOS); /* v6 */ +FLOATFUNC(FSTOD); /* v6 */ +FLOATFUNC(FSTOI); /* v6 */ +FLOATFUNC(FDTOI); /* v6 */ +FLOATFUNC(FABSS); /* v6 */ +FLOATFUNC(FCMPS); /* v6 */ +FLOATFUNC(FCMPES); /* v6 */ +FLOATFUNC(FCMPD); /* v6 */ +FLOATFUNC(FCMPED); /* v6 */ +FLOATFUNC(FMOVS); /* v6 */ +FLOATFUNC(FNEGS); /* v6 */ +FLOATFUNC(FITOS); /* v6 */ +FLOATFUNC(FITOD); /* v6 */ + +static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs); + +/* Unlike the Sparc64 version (which has a struct fpustate), we + * pass the taskstruct corresponding to the task which currently owns the + * FPU. This is partly because we don't have the fpustate struct and + * partly because the task owning the FPU isn't always current (as is + * the case for the Sparc64 port). This is probably SMP-related... + * This function returns 1 if all queued insns were emulated successfully. + * The test for unimplemented FPop in kernel mode has been moved into + * kernel/traps.c for simplicity. + */ +int do_mathemu(struct pt_regs *regs, struct task_struct *fpt) +{ + /* regs->pc isn't necessarily the PC at which the offending insn is sitting. + * The FPU maintains a queue of FPops which cause traps. + * When it hits an instruction that requires that the trapped op succeeded + * (usually because it reads a reg. that the trapped op wrote) then it + * causes this exception. We need to emulate all the insns on the queue + * and then allow the op to proceed. + * This code should also handle the case where the trap was precise, + * in which case the queue length is zero and regs->pc points at the + * single FPop to be emulated. (this case is untested, though :->) + * You'll need this case if you want to be able to emulate all FPops + * because the FPU either doesn't exist or has been software-disabled. + * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it + * might sound because the Ultra does funky things with a superscalar + * architecture.] + */ + + /* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */ + + int i; + int retcode = 0; /* assume all succeed */ + unsigned long insn; + +#ifdef DEBUG_MATHEMU + printk("In do_mathemu()... pc is %08lx\n", regs->pc); + printk("fpqdepth is %ld\n",fpt->tss.fpqdepth); + for (i = 0; i < fpt->tss.fpqdepth; i++) + printk("%d: %08lx at %08lx\n",i,fpt->tss.fpqueue[i].insn, (unsigned long)fpt->tss.fpqueue[i].insn_addr); +#endif + + if (fpt->tss.fpqdepth == 0) { /* no queue, guilty insn is at regs->pc */ +#ifdef DEBUG_MATHEMU + printk("precise trap at %08lx\n", regs->pc); +#endif + if (!get_user(insn, (u32 *)regs->pc)) { + retcode = do_one_mathemu(insn, &fpt->tss.fsr, fpt->tss.float_regs); + if (retcode) { + /* in this case we need to fix up PC & nPC */ + regs->pc = regs->npc; + regs->npc += 4; + } + } + return retcode; + } + + /* Normal case: need to empty the queue... */ + for (i = 0; i < fpt->tss.fpqdepth; i++) + { + retcode = do_one_mathemu(fpt->tss.fpqueue[i].insn, &(fpt->tss.fsr), fpt->tss.float_regs); + if (!retcode) /* insn failed, no point doing any more */ + break; + } + /* Now empty the queue and clear the queue_not_empty flag */ + fpt->tss.fsr &= ~0x3000; + fpt->tss.fpqdepth = 0; + + return retcode; +} + +static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs) +{ + /* Emulate the given insn, updating fsr and fregs appropriately. */ + int type = 0; + /* 01 is single, 10 is double, 11 is quad, + * 000011 is rs1, 001100 is rs2, 110000 is rd (00 in rd is fcc) + * 111100000000 tells which ftt that may happen in + * (this field not used on sparc32 code, as we can't + * extract trap type info for ops on the FP queue) + */ + int freg; + int (*func)(void *,void *,void *) = NULL; + void *rs1 = NULL, *rs2 = NULL, *rd = NULL; + +#ifdef DEBUG_MATHEMU + printk("In do_mathemu(), emulating %08lx\n", insn); +#endif + + if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ { + switch ((insn >> 5) & 0x1ff) { + /* QUAD - ftt == 3 */ + case 0x001: type = 0x314; func = FMOVS; break; + case 0x005: type = 0x314; func = FNEGS; break; + case 0x009: type = 0x314; func = FABSS; break; + case 0x02b: type = 0x33c; func = FSQRTQ; break; + case 0x043: type = 0x33f; func = FADDQ; break; + case 0x047: type = 0x33f; func = FSUBQ; break; + case 0x04b: type = 0x33f; func = FMULQ; break; + case 0x04f: type = 0x33f; func = FDIVQ; break; + case 0x06e: type = 0x33a; func = FDMULQ; break; + case 0x0c7: type = 0x31c; func = FQTOS; break; + case 0x0cb: type = 0x32c; func = FQTOD; break; + case 0x0cc: type = 0x334; func = FITOQ; break; + case 0x0cd: type = 0x334; func = FSTOQ; break; + case 0x0ce: type = 0x338; func = FDTOQ; break; + case 0x0d3: type = 0x31c; func = FQTOI; break; + /* SUBNORMAL - ftt == 2 */ + case 0x029: type = 0x214; func = FSQRTS; break; + case 0x02a: type = 0x228; func = FSQRTD; break; + case 0x041: type = 0x215; func = FADDS; break; + case 0x042: type = 0x22a; func = FADDD; break; + case 0x045: type = 0x215; func = FSUBS; break; + case 0x046: type = 0x22a; func = FSUBD; break; + case 0x049: type = 0x215; func = FMULS; break; + case 0x04a: type = 0x22a; func = FMULD; break; + case 0x04d: type = 0x215; func = FDIVS; break; + case 0x04e: type = 0x22a; func = FDIVD; break; + case 0x069: type = 0x225; func = FSMULD; break; + case 0x0c6: type = 0x218; func = FDTOS; break; + case 0x0c9: type = 0x224; func = FSTOD; break; + case 0x0d1: type = 0x214; func = FSTOI; break; + case 0x0d2: type = 0x218; func = FDTOI; break; + default: +#ifdef DEBUG_MATHEMU + printk("unknown FPop1: %03lx\n",(insn>>5)&0x1ff); +#endif + } + } + else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ { + switch ((insn >> 5) & 0x1ff) { + case 0x051: type = 0x305; func = FCMPS; break; + case 0x052: type = 0x30a; func = FCMPD; break; + case 0x053: type = 0x30f; func = FCMPQ; break; + case 0x055: type = 0x305; func = FCMPES; break; + case 0x056: type = 0x30a; func = FCMPED; break; + case 0x057: type = 0x30f; func = FCMPEQ; break; + default: +#ifdef DEBUG_MATHEMU + printk("unknown FPop2: %03lx\n",(insn>>5)&0x1ff); +#endif + } + } + + if (!type) { /* oops, didn't recognise that FPop */ + printk("attempt to emulate unrecognised FPop!\n"); + return 0; + } + + /* Decode the registers to be used */ + freg = (*fsr >> 14) & 0xf; + + *fsr &= ~0x1c000; /* clear the traptype bits */ + + freg = ((insn >> 14) & 0x1f); + switch (type & 0x3) /* is rs1 single, double or quad? */ + { + case 3: + if (freg & 3) /* quadwords must have bits 4&5 of the */ + { /* encoded reg. number set to zero. */ + *fsr |= (6 << 14); + return 0; /* simulate invalid_fp_register exception */ + } + /* fall through */ + case 2: + if (freg & 1) /* doublewords must have bit 5 zeroed */ + { + *fsr |= (6 << 14); + return 0; + } + } + rs1 = (void *)&fregs[freg]; + freg = (insn & 0x1f); + switch ((type >> 2) & 0x3) + { /* same again for rs2 */ + case 3: + if (freg & 3) /* quadwords must have bits 4&5 of the */ + { /* encoded reg. number set to zero. */ + *fsr |= (6 << 14); + return 0; /* simulate invalid_fp_register exception */ + } + /* fall through */ + case 2: + if (freg & 1) /* doublewords must have bit 5 zeroed */ + { + *fsr |= (6 << 14); + return 0; + } + } + rs2 = (void *)&fregs[freg]; + freg = ((insn >> 25) & 0x1f); + switch ((type >> 4) & 0x3) /* and finally rd. This one's a bit different */ + { + case 0: /* dest is fcc. (this must be FCMPQ or FCMPEQ) */ + if (freg) /* V8 has only one set of condition codes, so */ + { /* anything but 0 in the rd field is an error */ + *fsr |= (6 << 14); /* (should probably flag as invalid opcode */ + return 0; /* but SIGFPE will do :-> ) */ + } + rd = (void *)(fsr); /* FCMPQ and FCMPEQ are special and only */ + break; /* set bits they're supposed to :-> */ + case 3: + if (freg & 3) /* quadwords must have bits 4&5 of the */ + { /* encoded reg. number set to zero. */ + *fsr |= (6 << 14); + return 0; /* simulate invalid_fp_register exception */ + } + /* fall through */ + case 2: + if (freg & 1) /* doublewords must have bit 5 zeroed */ + { + *fsr |= (6 << 14); + return 0; + } + /* fall through */ + case 1: + rd = (void *)&fregs[freg]; + break; + } +#ifdef DEBUG_MATHEMU + printk("executing insn...\n"); +#endif + func(rd, rs2, rs1); /* do the Right Thing */ + return 1; /* success! */ +} diff --git a/arch/sparc/math-emu/sfp-machine.h b/arch/sparc/math-emu/sfp-machine.h new file mode 100644 index 000000000..eafad4273 --- /dev/null +++ b/arch/sparc/math-emu/sfp-machine.h @@ -0,0 +1,363 @@ +/* Machine-dependent software floating-point definitions. Sparc version. + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Actually, this is a sparc (32bit) version, written based on the + i386 and sparc64 versions, by me, + Peter Maydell (pmaydell@chiark.greenend.org.uk). + Comments are by and large also mine, although they may be inaccurate. + + In picking out asm fragments I've gone with the lowest common + denominator, which also happens to be the hardware I have :-> + That is, a SPARC without hardware multiply and divide. + */ + + +/* basic word size definitions */ +#define _FP_W_TYPE_SIZE 32 +#define _FP_W_TYPE unsigned long +#define _FP_WS_TYPE signed long +#define _FP_I_TYPE long + +/* You can optionally code some things like addition in asm. For + * example, i386 defines __FP_FRAC_ADD_2 as asm. If you don't + * then you get a fragment of C code [if you change an #ifdef 0 + * in op-2.h] or a call to add_ssaaaa (see below). + * Good places to look for asm fragments to use are gcc and glibc. + * gcc's longlong.h is useful. + */ + +/* We need to know how to multiply and divide. If the host word size + * is >= 2*fracbits you can use FP_MUL_MEAT_n_imm(t,R,X,Y) which + * codes the multiply with whatever gcc does to 'a * b'. + * _FP_MUL_MEAT_n_wide(t,R,X,Y,f) is used when you have an asm + * function that can multiply two 1W values and get a 2W result. + * Otherwise you're stuck with _FP_MUL_MEAT_n_hard(t,R,X,Y) which + * does bitshifting to avoid overflow. + * For division there is FP_DIV_MEAT_n_imm(t,R,X,Y,f) for word size + * >= 2*fracbits, where f is either _FP_DIV_HELP_imm or + * _FP_DIV_HELP_ldiv (see op-1.h). + * _FP_DIV_MEAT_udiv() is if you have asm to do 2W/1W => (1W, 1W). + * [GCC and glibc have longlong.h which has the asm macro udiv_qrnnd + * to do this.] + * In general, 'n' is the number of words required to hold the type, + * and 't' is either S, D or Q for single/double/quad. + * -- PMM + */ +/* Example: SPARC64: + * #define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_imm(S,R,X,Y) + * #define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm) + * #define _FP_MUL_MEAT_Q(R,X,Y) _FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm) + * + * #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm) + * #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_1_udiv(D,R,X,Y) + * #define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_2_udiv_64(Q,R,X,Y) + * + * Example: i386: + * #define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(S,R,X,Y,_i386_mul_32_64) + * #define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(D,R,X,Y,_i386_mul_32_64) + * + * #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y,_i386_div_64_32) + * #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv_64(D,R,X,Y) + */ +#define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(S,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(D,R,X,Y,umul_ppmm) +/* FIXME: This is not implemented, but should be soon */ +#define _FP_MUL_MEAT_Q(R,X,Y) _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4) +#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y) +#define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv_64(D,R,X,Y) +/* FIXME: This is not implemented, but should be soon */ +#define _FP_DIV_MEAT_Q(R,X,Y) _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4) + +/* These macros define what NaN looks like. They're supposed to expand to + * a comma-separated set of 32bit unsigned ints that encode NaN. + */ +#define _FP_NANFRAC_S _FP_QNANBIT_S +#define _FP_NANFRAC_D _FP_QNANBIT_D, 0 +#define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0, 0, 0 + +#define _FP_KEEPNANFRACP 1 + +/* This macro appears to be called when both X and Y are NaNs, and + * has to choose one and copy it to R. i386 goes for the larger of the + * two, sparc64 just picks Y. I don't understand this at all so I'll + * go with sparc64 because it's shorter :-> -- PMM + */ +#define _FP_CHOOSENAN(fs, wc, R, X, Y) \ + do { \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R,Y); \ + R##_c = FP_CLS_NAN; \ + } while (0) + +#define __FP_UNPACK_RAW_1(fs, X, val) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + X##_f = _flo->bits.frac; \ + X##_e = _flo->bits.exp; \ + X##_s = _flo->bits.sign; \ + } while (0) + +#define __FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac = X##_f; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_UNPACK_RAW_2(fs, X, val) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + X##_f0 = _flo->bits.frac0; \ + X##_f1 = _flo->bits.frac1; \ + X##_e = _flo->bits.exp; \ + X##_s = _flo->bits.sign; \ + } while (0) + +#define __FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f0; \ + _flo->bits.frac1 = X##_f1; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_UNPACK_RAW_4(fs, X, val) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + X##_f[0] = _flo->bits.frac0; \ + X##_f[1] = _flo->bits.frac1; \ + X##_f[2] = _flo->bits.frac2; \ + X##_f[3] = _flo->bits.frac3; \ + X##_e = _flo->bits.exp; \ + X##_s = _flo->bits.sign; \ + } while (0) + +#define __FP_PACK_RAW_4(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f[0]; \ + _flo->bits.frac1 = X##_f[1]; \ + _flo->bits.frac2 = X##_f[2]; \ + _flo->bits.frac3 = X##_f[3]; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_UNPACK_S(X,val) \ + do { \ + __FP_UNPACK_RAW_1(S,X,val); \ + _FP_UNPACK_CANONICAL(S,1,X); \ + } while (0) + +#define __FP_PACK_S(val,X) \ + do { \ + _FP_PACK_CANONICAL(S,1,X); \ + __FP_PACK_RAW_1(S,val,X); \ + } while (0) + +#define __FP_UNPACK_D(X,val) \ + do { \ + __FP_UNPACK_RAW_2(D,X,val); \ + _FP_UNPACK_CANONICAL(D,2,X); \ + } while (0) + +#define __FP_PACK_D(val,X) \ + do { \ + _FP_PACK_CANONICAL(D,2,X); \ + __FP_PACK_RAW_2(D,val,X); \ + } while (0) + +#define __FP_UNPACK_Q(X,val) \ + do { \ + __FP_UNPACK_RAW_4(Q,X,val); \ + _FP_UNPACK_CANONICAL(Q,4,X); \ + } while (0) + +#define __FP_PACK_Q(val,X) \ + do { \ + _FP_PACK_CANONICAL(Q,4,X); \ + __FP_PACK_RAW_4(Q,val,X); \ + } while (0) + +/* the asm fragments go here: all these are taken from glibc-2.0.5's stdlib/longlong.h */ + +#include <linux/types.h> +#include <asm/byteorder.h> + +/* add_ssaaaa is used in op-2.h and should be equivalent to + * #define add_ssaaaa(sh,sl,ah,al,bh,bl) (sh = ah+bh+ (( sl = al+bl) < al)) + * add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, + * high_addend_2, low_addend_2) adds two UWtype integers, composed by + * HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2 + * respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow + * (i.e. carry out) is not stored anywhere, and is lost. + */ +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("addcc %r4,%5,%1 + addx %r2,%3,%0" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "%rJ" ((USItype)(ah)), \ + "rI" ((USItype)(bh)), \ + "%rJ" ((USItype)(al)), \ + "rI" ((USItype)(bl)) \ + : "cc") + + +/* sub_ddmmss is used in op-2.h and udivmodti4.c and should be equivalent to + * #define sub_ddmmss(sh, sl, ah, al, bh, bl) (sh = ah-bh - ((sl = al-bl) > al)) + * sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, + * high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers, + * composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and + * LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE + * and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere, + * and is lost. + */ + +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subcc %r4,%5,%1 + subx %r2,%3,%0" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "rJ" ((USItype)(ah)), \ + "rI" ((USItype)(bh)), \ + "rJ" ((USItype)(al)), \ + "rI" ((USItype)(bl)) \ + : "cc") + + +/* asm fragments for mul and div */ +/* umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two + * UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype + * word product in HIGH_PROD and LOW_PROD. + * These look ugly because the sun4/4c don't have umul/udiv/smul/sdiv in + * hardware. + */ +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("! Inlined umul_ppmm + wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr + sra %3,31,%%g2 ! Don't move this insn + and %2,%%g2,%%g2 ! Don't move this insn + andcc %%g0,0,%%g1 ! Don't move this insn + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,0,%%g1 + add %%g1,%%g2,%0 + rd %%y,%1" \ + : "=r" ((USItype)(w1)), \ + "=r" ((USItype)(w0)) \ + : "%rI" ((USItype)(u)), \ + "r" ((USItype)(v)) \ + : "%g1", "%g2", "cc") + +/* udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, + * denominator) divides a UDWtype, composed by the UWtype integers + * HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient + * in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less + * than DENOMINATOR for correct operation. If, in addition, the most + * significant bit of DENOMINATOR must be 1, then the pre-processor symbol + * UDIV_NEEDS_NORMALIZATION is defined to 1. + */ + +#define udiv_qrnnd(q, r, n1, n0, d) \ + __asm__ ("! Inlined udiv_qrnnd + mov 32,%%g1 + subcc %1,%2,%%g0 +1: bcs 5f + addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb + sub %1,%2,%1 ! this kills msb of n + addx %1,%1,%1 ! so this can't give carry + subcc %%g1,1,%%g1 +2: bne 1b + subcc %1,%2,%%g0 + bcs 3f + addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb + b 3f + sub %1,%2,%1 ! this kills msb of n +4: sub %1,%2,%1 +5: addxcc %1,%1,%1 + bcc 2b + subcc %%g1,1,%%g1 +! Got carry from n. Subtract next step to cancel this carry. + bne 4b + addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb + sub %1,%2,%1 +3: xnor %0,0,%0 + ! End of inline udiv_qrnnd" \ + : "=&r" ((USItype) (q)), \ + "=&r" ((USItype) (r)) \ + : "r" ((USItype) (d)), \ + "1" ((USItype) (n1)), \ + "0" ((USItype) (n0)) : "%g1", "cc") + +#define UDIV_NEEDS_NORMALIZATION 0 + +#define abort() \ + return 0 + +#ifdef __BIG_ENDIAN +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile index bae5d323a..18eeb1f52 100644 --- a/arch/sparc/mm/Makefile +++ b/arch/sparc/mm/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.27 1997/11/07 15:01:27 jj Exp $ +# $Id: Makefile,v 1.30 1998/03/09 14:03:53 jj Exp $ # Makefile for the linux Sparc-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also @@ -8,9 +8,17 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := mm.o -O_OBJS := fault.o init.o sun4c.o srmmu.o hypersparc.o viking.o \ - tsunami.o loadmmu.o generic.o asyncd.o extable.o \ - turbosparc.o iommu.o io-unit.o +O_OBJS := fault.o init.o loadmmu.o generic.o asyncd.o extable.o btfixup.o +ifeq ($(CONFIG_SUN4),y) +O_OBJS += nosrmmu.o +else +O_OBJS += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o turbosparc.o +endif +ifdef SMP +O_OBJS += nosun4c.o +else +O_OBJS += sun4c.o +endif include $(TOPDIR)/Rules.make diff --git a/arch/sparc/mm/btfixup.c b/arch/sparc/mm/btfixup.c new file mode 100644 index 000000000..e61ccc158 --- /dev/null +++ b/arch/sparc/mm/btfixup.c @@ -0,0 +1,334 @@ +/* $Id: btfixup.c,v 1.7 1998/03/09 14:03:56 jj Exp $ + * btfixup.c: Boot time code fixup and relocator, so that + * we can get rid of most indirect calls to achieve single + * image sun4c and srmmu kernel. + * + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/btfixup.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/oplib.h> +#include <asm/system.h> + +#define BTFIXUP_OPTIMIZE_NOP +#define BTFIXUP_OPTIMIZE_OTHER + +extern char *srmmu_name; +static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for "; +#ifdef CONFIG_SUN4 +static char str_sun4c[] __initdata = "sun4\n"; +#else +static char str_sun4c[] __initdata = "sun4c\n"; +#endif +static char str_srmmu[] __initdata = "srmmu[%s]/"; +static char str_iommu[] __initdata = "iommu\n"; +static char str_iounit[] __initdata = "io-unit\n"; + +static int visited __initdata = 0; +extern unsigned int ___btfixup_start[], ___btfixup_end[], __init_begin[], __init_end[], __init_text_end[]; +extern unsigned int _stext[], _end[], __start___ksymtab[], __stop___ksymtab[]; +static char wrong_f[] __initdata = "Trying to set f fixup %p to invalid function %08x\n"; +static char wrong_b[] __initdata = "Trying to set b fixup %p to invalid function %08x\n"; +static char wrong_s[] __initdata = "Trying to set s fixup %p to invalid value %08x\n"; +static char wrong_h[] __initdata = "Trying to set h fixup %p to invalid value %08x\n"; +static char wrong_a[] __initdata = "Trying to set a fixup %p to invalid value %08x\n"; +static char wrong[] __initdata = "Wrong address for %c fixup %p\n"; +static char insn_f[] __initdata = "Fixup f %p refers to weird instructions at %p[%08x,%08x]\n"; +static char insn_b[] __initdata = "Fixup b %p doesn't refer to a SETHI at %p[%08x]\n"; +static char insn_s[] __initdata = "Fixup s %p doesn't refer to an OR at %p[%08x]\n"; +static char insn_h[] __initdata = "Fixup h %p doesn't refer to a SETHI at %p[%08x]\n"; +static char insn_a[] __initdata = "Fixup a %p doesn't refer to a SETHI nor OR at %p[%08x]\n"; +static char insn_i[] __initdata = "Fixup i %p doesn't refer to a valid instruction at %p[%08x]\n"; +static char fca_und[] __initdata = "flush_cache_all undefined in btfixup()\n"; +static char wrong_setaddr[] __initdata = "Garbled CALL/INT patch at %p[%08x,%08x,%08x]=%08x\n"; + +#ifdef BTFIXUP_OPTIMIZE_OTHER +__initfunc(static void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value)) +{ + if (!fmangled) + *addr = value; + else { + unsigned int *q = (unsigned int *)q1; + if (*addr == 0x01000000) { + /* Noped */ + *q = value; + } else if (addr[-1] == *q) { + /* Moved */ + addr[-1] = value; + *q = value; + } else { + prom_printf(wrong_setaddr, addr-1, addr[-1], *addr, *q, value); + prom_halt(); + } + } +} +#else +static __inline__ void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value) +{ + *addr = value; +} +#endif + +__initfunc(void btfixup(void)) +{ + unsigned int *p, *q; + int type, count; + unsigned insn; + unsigned *addr; + int fmangled = 0; + void (*flush_cacheall)(void); + + if (!visited) { + visited++; + printk(version); + if (ARCH_SUN4C_SUN4) + printk(str_sun4c); + else { + printk(str_srmmu, srmmu_name); + if (sparc_cpu_model == sun4d) + printk(str_iounit); + else + printk(str_iommu); + } + } + for (p = ___btfixup_start; p < ___btfixup_end; ) { + count = p[2]; + q = p + 3; + switch (type = *(unsigned char *)p) { + case 'f': + count = p[3]; + q = p + 4; + if (((p[0] & 1) || p[1]) + && ((p[1] & 3) || (unsigned *)(p[1]) < _stext || (unsigned *)(p[1]) >= _end)) { + prom_printf(wrong_f, p, p[1]); + prom_halt(); + } + break; + case 'b': + if (p[1] < (unsigned long)__init_begin || p[1] >= (unsigned long)__init_text_end || (p[1] & 3)) { + prom_printf(wrong_b, p, p[1]); + prom_halt(); + } + break; + case 's': + if (p[1] + 0x1000 >= 0x2000) { + prom_printf(wrong_s, p, p[1]); + prom_halt(); + } + break; + case 'h': + if (p[1] & 0x3ff) { + prom_printf(wrong_h, p, p[1]); + prom_halt(); + } + break; + case 'a': + if (p[1] + 0x1000 >= 0x2000 && (p[1] & 0x3ff)) { + prom_printf(wrong_a, p, p[1]); + prom_halt(); + } + break; + } + if (p[0] & 1) { + p[0] &= ~1; + while (count) { + fmangled = 0; + addr = (unsigned *)*q; + if (addr < _stext || addr >= _end) { + prom_printf(wrong, type, p); + prom_halt(); + } + insn = *addr; +#ifdef BTFIXUP_OPTIMIZE_OTHER + if (type != 'f' && q[1]) { + insn = *(unsigned int *)q[1]; + if (!insn || insn == 1) + insn = *addr; + else + fmangled = 1; + } +#endif + switch (type) { + case 'f': /* CALL */ + if (addr >= __start___ksymtab && addr < __stop___ksymtab) { + *addr = p[1]; + break; + } else if (!q[1]) { + if ((insn & 0xc1c00000) == 0x01000000) { /* SETHI */ + *addr = (insn & 0xffc00000) | (p[1] >> 10); break; + } else if ((insn & 0xc1f82000) == 0x80102000) { /* OR X, %LO(i), Y */ + *addr = (insn & 0xffffe000) | (p[1] & 0x3ff); break; + } else if ((insn & 0xc0000000) != 0x40000000) { /* !CALL */ + bad_f: + prom_printf(insn_f, p, addr, insn, addr[1]); + prom_halt(); + } + } else if (q[1] != 1) + addr[1] = q[1]; + if (p[2] == BTFIXUPCALL_NORM) { + norm_f: + *addr = 0x40000000 | ((p[1] - (unsigned)addr) >> 2); + q[1] = 0; + break; + } +#ifndef BTFIXUP_OPTIMIZE_NOP + goto norm_f; +#else + if (!(addr[1] & 0x80000000)) { + if ((addr[1] & 0xc1c00000) != 0x01000000) /* !SETHI */ + goto bad_f; /* CALL, Bicc, FBfcc, CBccc are weird in delay slot, aren't they? */ + } else { + if ((addr[1] & 0x01800000) == 0x01800000) { + if ((addr[1] & 0x01f80000) == 0x01e80000) { + /* RESTORE */ + goto norm_f; /* It is dangerous to patch that */ + } + goto bad_f; + } + if ((addr[1] & 0xffffe003) == 0x9e03e000) { + /* ADD %O7, XX, %o7 */ + int displac = (addr[1] << 19); + + displac = (displac >> 21) + 2; + *addr = (0x10800000) + (displac & 0x3fffff); + q[1] = addr[1]; + addr[1] = p[2]; + break; + } + if ((addr[1] & 0x201f) == 0x200f || (addr[1] & 0x7c000) == 0x3c000) + goto norm_f; /* Someone is playing bad tricks with us: rs1 or rs2 is o7 */ + if ((addr[1] & 0x3e000000) == 0x1e000000) + goto norm_f; /* rd is %o7. We'd better take care. */ + } + if (p[2] == BTFIXUPCALL_NOP) { + *addr = 0x01000000; + q[1] = 1; + break; + } +#ifndef BTFIXUP_OPTIMIZE_OTHER + goto norm_f; +#else + if (addr[1] == 0x01000000) { /* NOP in the delay slot */ + q[1] = addr[1]; + *addr = p[2]; + break; + } + if ((addr[1] & 0xc0000000) != 0xc0000000) { + /* Not a memory operation */ + if ((addr[1] & 0x30000000) == 0x10000000) { + /* Ok, non-memory op with rd %oX */ + if ((addr[1] & 0x3e000000) == 0x1c000000) + goto bad_f; /* Aiee. Someone is playing strange %sp tricks */ + if ((addr[1] & 0x3e000000) > 0x12000000 || + ((addr[1] & 0x3e000000) == 0x12000000 && + p[2] != BTFIXUPCALL_STO1O0 && p[2] != BTFIXUPCALL_SWAPO0O1) || + ((p[2] & 0xffffe000) == BTFIXUPCALL_RETINT(0))) { + /* Nobody uses the result. We can nop it out. */ + *addr = p[2]; + q[1] = addr[1]; + addr[1] = 0x01000000; + break; + } + if ((addr[1] & 0xf1ffffe0) == 0x90100000) { + /* MOV %reg, %Ox */ + if ((addr[1] & 0x3e000000) == 0x10000000 && + (p[2] & 0x7c000) == 0x20000) { + /* Ok, it is call xx; mov reg, %o0 and call optimizes + to doing something on %o0. Patch the patch. */ + *addr = (p[2] & ~0x7c000) | ((addr[1] & 0x1f) << 14); + q[1] = addr[1]; + addr[1] = 0x01000000; + break; + } + if ((addr[1] & 0x3e000000) == 0x12000000 && + p[2] == BTFIXUPCALL_STO1O0) { + *addr = (p[2] & ~0x3e000000) | ((addr[1] & 0x1f) << 25); + q[1] = addr[1]; + addr[1] = 0x01000000; + break; + } + } + } + } + *addr = addr[1]; + q[1] = addr[1]; + addr[1] = p[2]; + break; +#endif /* BTFIXUP_OPTIMIZE_OTHER */ +#endif /* BTFIXUP_OPTIMIZE_NOP */ + case 'b': /* BLACKBOX */ + /* Has to be sethi i, xx */ + if ((insn & 0xc1c00000) != 0x01000000) { + prom_printf(insn_b, p, addr, insn); + prom_halt(); + } else { + void (*do_fixup)(unsigned *); + + do_fixup = (void (*)(unsigned *))p[1]; + do_fixup(addr); + } + break; + case 's': /* SIMM13 */ + /* Has to be or %g0, i, xx */ + if ((insn & 0xc1ffe000) != 0x80102000) { + prom_printf(insn_s, p, addr, insn); + prom_halt(); + } + set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x1fff)); + break; + case 'h': /* SETHI */ + /* Has to be sethi i, xx */ + if ((insn & 0xc1c00000) != 0x01000000) { + prom_printf(insn_h, p, addr, insn); + prom_halt(); + } + set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10)); + break; + case 'a': /* HALF */ + /* Has to be sethi i, xx or or %g0, i, xx */ + if ((insn & 0xc1c00000) != 0x01000000 && + (insn & 0xc1ffe000) != 0x80102000) { + prom_printf(insn_a, p, addr, insn); + prom_halt(); + } + if (p[1] & 0x3ff) + set_addr(addr, q[1], fmangled, + (insn & 0x3e000000) | 0x80102000 | (p[1] & 0x1fff)); + else + set_addr(addr, q[1], fmangled, + (insn & 0x3e000000) | 0x01000000 | (p[1] >> 10)); + break; + case 'i': /* INT */ + if ((insn & 0xc1c00000) == 0x01000000) /* %HI */ + set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10)); + else if ((insn & 0x80002000) == 0x80002000 && + (insn & 0x01800000) != 0x01800000) /* %LO */ + set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x3ff)); + else { + prom_printf(insn_i, p, addr, insn); + prom_halt(); + } + break; + } + count -= 2; + q += 2; + } + } else + p = q + count; + } +#ifdef __SMP__ + flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(local_flush_cache_all); +#else + flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(flush_cache_all); +#endif + if (!flush_cacheall) { + prom_printf(fca_und); + prom_halt(); + } + (*flush_cacheall)(); +} diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c index 0d6490860..274b9eebf 100644 --- a/arch/sparc/mm/fault.c +++ b/arch/sparc/mm/fault.c @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.92 1997/05/15 21:14:21 davem Exp $ +/* $Id: fault.c,v 1.93 1998/03/25 10:43:16 jj Exp $ * fault.c: Page fault handlers for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -271,7 +271,7 @@ bad_area: #endif tsk->tss.sig_address = address; tsk->tss.sig_desc = SUBSIG_NOMAPPING; - send_sig(SIGSEGV, tsk, 1); + force_sig(SIGSEGV, tsk); goto out; } unhandled_fault (address, tsk, regs); diff --git a/arch/sparc/mm/hypersparc.S b/arch/sparc/mm/hypersparc.S index 2c27bfdab..dcf3fd990 100644 --- a/arch/sparc/mm/hypersparc.S +++ b/arch/sparc/mm/hypersparc.S @@ -1,4 +1,4 @@ -/* $Id: hypersparc.S,v 1.12 1997/11/27 15:42:30 jj Exp $ +/* $Id: hypersparc.S,v 1.13 1998/02/13 15:35:09 jj Exp $ * hypersparc.S: High speed Hypersparc mmu/cache operations. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -301,14 +301,13 @@ hypersparc_flush_tlb_range: cmp %o3, -1 be hypersparc_flush_tlb_range_out #endif - srl %o1, SRMMU_PGDIR_SHIFT, %o1 + sethi %hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4 sta %o3, [%g1] ASI_M_MMUREGS - sll %o1, SRMMU_PGDIR_SHIFT, %o1 - sethi %hi(1 << SRMMU_PGDIR_SHIFT), %o4 + and %o1, %o4, %o1 add %o1, 0x200, %o1 sta %g0, [%o1] ASI_M_FLUSH_PROBE 1: - add %o1, %o4, %o1 + sub %o1, %o4, %o1 cmp %o1, %o2 blu,a 1b sta %g0, [%o1] ASI_M_FLUSH_PROBE diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index aa85666c6..db6559214 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -1,8 +1,9 @@ -/* $Id: init.c,v 1.50 1998/01/10 18:19:42 ecd Exp $ +/* $Id: init.c,v 1.59 1998/03/27 06:59:57 davem Exp $ * linux/arch/sparc/mm/init.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h> @@ -30,11 +31,18 @@ #include <asm/pgtable.h> #include <asm/vaddrs.h> +/* Turn this off if you suspect some place in some physical memory hole + might get into page tables (something would be broken very much). */ + +#define FREE_UNUSED_MEM_MAP + extern void show_net_buffers(void); struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS]; unsigned long sparc_unmapped_base; +struct pgtable_cache_struct pgt_quicklists; + /* References to section boundaries */ extern char __init_begin, __init_end, etext; @@ -65,26 +73,38 @@ pte_t __bad_page(void) void show_mem(void) { - int i,free = 0,total = 0,reserved = 0; - int shared = 0; + int free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + struct page *page, *end; printk("\nMem-info:\n"); show_free_areas(); printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = max_mapnr; - while (i-- > 0) { + for (page = mem_map, end = mem_map + max_mapnr; + page < end; page++) { + if (PageSkip(page)) { + if (page->next_hash < page) + break; + page = page->next_hash; + } total++; - if (PageReserved(mem_map + i)) + if (PageReserved(page)) reserved++; - else if (!atomic_read(&mem_map[i].count)) + else if (PageSwapCache(page)) + cached++; + else if (!atomic_read(&page->count)) free++; else - shared += atomic_read(&mem_map[i].count) - 1; + shared += atomic_read(&page->count) - 1; } printk("%d pages of RAM\n",total); printk("%d free pages\n",free); printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld page tables cached\n",pgtable_cache_size); + if (sparc_cpu_model == sun4m || sparc_cpu_model == sun4d) + printk("%ld page dirs cached\n", pgd_cache_size); show_buffers(); #ifdef CONFIG_NET show_net_buffers(); @@ -128,19 +148,23 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) switch(sparc_cpu_model) { case sun4c: case sun4e: + case sun4: start_mem = sun4c_paging_init(start_mem, end_mem); sparc_unmapped_base = 0xe0000000; + BTFIXUPSET_SETHI(sparc_unmapped_base, 0xe0000000); break; case sun4m: case sun4d: start_mem = srmmu_paging_init(start_mem, end_mem); sparc_unmapped_base = 0x50000000; + BTFIXUPSET_SETHI(sparc_unmapped_base, 0x50000000); break; case ap1000: #if CONFIG_AP1000 start_mem = apmmu_paging_init(start_mem, end_mem); sparc_unmapped_base = 0x50000000; + BTFIXUPSET_SETHI(sparc_unmapped_base, 0x50000000); break; #endif @@ -168,6 +192,7 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) protection_map[13] = PAGE_READONLY; protection_map[14] = PAGE_SHARED; protection_map[15] = PAGE_SHARED; + btfixup(); return device_scan(start_mem); } @@ -175,7 +200,7 @@ struct cache_palias *sparc_aliases; extern void srmmu_frob_mem_map(unsigned long); -int physmem_mapped_contig = 1; +int physmem_mapped_contig __initdata = 1; __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem)) { @@ -210,7 +235,8 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) int codepages = 0; int datapages = 0; int initpages = 0; - unsigned long tmp2, addr; + unsigned long addr; + struct page *page, *end; /* Saves us work later. */ memset((void *) ZERO_PAGE, 0, PAGE_SIZE); @@ -220,33 +246,60 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) high_memory = (void *) end_mem; start_mem = PAGE_ALIGN(start_mem); - num_physpages = (start_mem - KERNBASE) >> PAGE_SHIFT; + num_physpages = 0; addr = KERNBASE; while(addr < start_mem) { #ifdef CONFIG_BLK_DEV_INITRD - if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) { + if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved); - num_physpages--; - } else + else #endif mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved); addr += PAGE_SIZE; } taint_real_pages(start_mem, end_mem); + +#ifdef FREE_UNUSED_MEM_MAP + end = mem_map + max_mapnr; + for (page = mem_map; page < end; page++) { + if (PageSkip(page)) { + unsigned long low, high; + + low = PAGE_ALIGN((unsigned long)(page+1)); + if (page->next_hash < page) + high = ((unsigned long)end) & PAGE_MASK; + else + high = ((unsigned long)page->next_hash) & PAGE_MASK; + while (low < high) { + mem_map[MAP_NR(low)].flags &= ~(1<<PG_reserved); + low += PAGE_SIZE; + } + } + } +#endif + for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { + if (PageSkip(mem_map + MAP_NR(addr))) { + unsigned long next = mem_map[MAP_NR(addr)].next_hash - mem_map; + + next = (next << PAGE_SHIFT) + PAGE_OFFSET; + if (next < addr || next >= end_mem) + break; + addr = next; + } + num_physpages++; if(PageReserved(mem_map + MAP_NR(addr))) { if ((addr < (unsigned long) &etext) && (addr >= KERNBASE)) codepages++; - else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) - initpages++; - else if((addr < start_mem) && (addr >= KERNBASE)) + else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) + initpages++; + else if((addr < start_mem) && (addr >= KERNBASE)) datapages++; continue; } atomic_set(&mem_map[MAP_NR(addr)].count, 1); - num_physpages++; #ifdef CONFIG_BLK_DEV_INITRD if (!initrd_start || (addr < initrd_start || addr >= initrd_end)) @@ -254,14 +307,12 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) free_page(addr); } - tmp2 = nr_free_pages << PAGE_SHIFT; - - printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n", - tmp2 >> 10, + printk("Memory: %dk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n", + nr_free_pages << (PAGE_SHIFT-10), codepages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10), - PAGE_OFFSET, end_mem); + (unsigned long)PAGE_OFFSET, end_mem); freepages.min = nr_free_pages >> 7; if(freepages.min < 16) @@ -284,20 +335,25 @@ void free_initmem (void) void si_meminfo(struct sysinfo *val) { - int i; + struct page *page, *end; - i = MAP_NR(high_memory); val->totalram = 0; val->sharedram = 0; val->freeram = nr_free_pages << PAGE_SHIFT; val->bufferram = buffermem; - while (i-- > 0) { - if (PageReserved(mem_map + i)) + for (page = mem_map, end = mem_map + max_mapnr; + page < end; page++) { + if (PageSkip(page)) { + if (page->next_hash < page) + break; + page = page->next_hash; + } + if (PageReserved(page)) continue; val->totalram++; - if (!atomic_read(&mem_map[i].count)) + if (!atomic_read(&page->count)) continue; - val->sharedram += atomic_read(&mem_map[i].count) - 1; + val->sharedram += atomic_read(&page->count) - 1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c index 519c124c9..d293fc71c 100644 --- a/arch/sparc/mm/io-unit.c +++ b/arch/sparc/mm/io-unit.c @@ -1,7 +1,7 @@ -/* $Id: io-unit.c,v 1.5 1997/12/22 16:09:26 jj Exp $ +/* $Id: io-unit.c,v 1.10 1998/03/03 12:31:14 jj Exp $ * io-unit.c: IO-UNIT specific routines for memory management. * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h> @@ -13,28 +13,41 @@ #include <asm/io.h> #include <asm/io-unit.h> #include <asm/mxcc.h> +#include <asm/spinlock.h> +#include <asm/bitops.h> + +/* #define IOUNIT_DEBUG */ +#ifdef IOUNIT_DEBUG +#define IOD(x) printk(x) +#else +#define IOD(x) do { } while (0) +#endif #define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1)) #define IOPERM (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID) -#define MKIOPTE(phys) ((((phys)>>4) & IOUPTE_PAGE) | IOPERM) +#define MKIOPTE(phys) __iopte((((phys)>>4) & IOUPTE_PAGE) | IOPERM) -unsigned long sun4d_dma_base; -unsigned long sun4d_dma_vbase; -unsigned long sun4d_dma_size; __initfunc(unsigned long iounit_init(int sbi_node, int io_node, unsigned long memory_start, unsigned long memory_end, struct linux_sbus *sbus)) { iopte_t *xpt, *xptend; - unsigned long paddr; struct iounit_struct *iounit; struct linux_prom_registers iommu_promregs[PROMREG_MAX]; memory_start = LONG_ALIGN(memory_start); iounit = (struct iounit_struct *)memory_start; - memory_start += sizeof(struct iounit_struct); - + memory_start = LONG_ALIGN(memory_start + sizeof(struct iounit_struct)); + + memset(iounit, 0, sizeof(*iounit)); + iounit->limit[0] = IOUNIT_BMAP1_START; + iounit->limit[1] = IOUNIT_BMAP2_START; + iounit->limit[2] = IOUNIT_BMAPM_START; + iounit->limit[3] = IOUNIT_BMAPM_END; + iounit->rotor[1] = IOUNIT_BMAP2_START; + iounit->rotor[2] = IOUNIT_BMAPM_START; + prom_getproperty(sbi_node, "reg", (void *) iommu_promregs, sizeof(iommu_promregs)); prom_apply_generic_ranges(io_node, 0, iommu_promregs, 3); @@ -46,11 +59,6 @@ iounit_init(int sbi_node, int io_node, unsigned long memory_start, sbus->iommu = (struct iommu_struct *)iounit; iounit->page_table = xpt; - /* Initialize new table. */ - paddr = IOUNIT_DMA_BASE - sun4d_dma_base; - for (xptend = xpt + (sun4d_dma_size >> PAGE_SHIFT); - xpt < xptend; paddr++) - *xpt++ = MKIOPTE(paddr); for (xptend = iounit->page_table + (16 * PAGE_SIZE) / sizeof(iopte_t); xpt < xptend;) *xpt++ = 0; @@ -58,36 +66,108 @@ iounit_init(int sbi_node, int io_node, unsigned long memory_start, return memory_start; } +/* One has to hold iounit->lock to call this */ +static unsigned long iounit_get_area(struct iounit_struct *iounit, unsigned long vaddr, int size) +{ + int i, j, k, npages; + unsigned long rotor, scan, limit; + iopte_t iopte; + + npages = ((vaddr & ~PAGE_MASK) + size + (PAGE_SIZE-1)) >> PAGE_SHIFT; + + /* A tiny bit of magic ingredience :) */ + switch (npages) { + case 1: i = 0x0231; break; + case 2: i = 0x0132; break; + default: i = 0x0213; break; + } + + IOD(("iounit_get_area(%08lx,%d[%d])=", vaddr, size, npages)); + +next: j = (i & 15); + rotor = iounit->rotor[j - 1]; + limit = iounit->limit[j]; + scan = rotor; +nexti: scan = find_next_zero_bit(iounit->bmap, limit, scan); + if (scan + npages > limit) { + if (limit != rotor) { + limit = rotor; + scan = iounit->limit[j - 1]; + goto nexti; + } + i >>= 4; + if (!(i & 15)) + panic("iounit_get_area: Couldn't find free iopte slots for (%08lx,%d)\n", vaddr, size); + goto next; + } + for (k = 1, scan++; k < npages; k++) + if (test_bit(scan++, iounit->bmap)) + goto nexti; + iounit->rotor[j - 1] = (scan < limit) ? scan : iounit->limit[j - 1]; + scan -= npages; + iopte = MKIOPTE(mmu_v2p(vaddr & PAGE_MASK)); + vaddr = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT) + (vaddr & ~PAGE_MASK); + for (k = 0; k < npages; k++, iopte = __iopte(iopte_val(iopte) + 0x100), scan++) { + set_bit(scan, iounit->bmap); + iounit->page_table[scan] = iopte; + } + IOD(("%08lx\n", vaddr)); + return vaddr; +} + static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus) { - /* Viking MXCC is IO coherent, just need to translate the address to DMA handle */ -#ifdef IOUNIT_DEBUG - if ((((unsigned long) vaddr) & PAGE_MASK) < sun4d_dma_vaddr || - (((unsigned long) vaddr) & PAGE_MASK) + len > sun4d_dma_vbase + sun4d_dma_size) - panic("Using non-DMA memory for iounit_get_scsi_one"); -#endif - return (__u32)(sun4d_dma_base + mmu_v2p((long)vaddr)); + unsigned long ret, flags; + struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu; + + spin_lock_irqsave(&iounit->lock, flags); + ret = iounit_get_area(iounit, (unsigned long)vaddr, len); + spin_unlock_irqrestore(&iounit->lock, flags); + return ret; } static void iounit_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) { - /* Viking MXCC is IO coherent, just need to translate the address to DMA handle */ + unsigned long flags; + struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu; + + /* FIXME: Cache some resolved pages - often several sg entries are to the same page */ + spin_lock_irqsave(&iounit->lock, flags); for (; sz >= 0; sz--) { -#ifdef IOUNIT_DEBUG - unsigned long page = ((unsigned long) sg[sz].addr) & PAGE_MASK; - if (page < sun4d_dma_vbase || page + sg[sz].len > sun4d_dma_vbase + sun4d_dma_size) - panic("Using non-DMA memory for iounit_get_scsi_sgl"); -#endif - sg[sz].dvma_addr = (__u32) (sun4d_dma_base + mmu_v2p((long)sg[sz].addr));; + sg[sz].dvma_addr = iounit_get_area(iounit, (unsigned long)sg[sz].addr, sg[sz].len); } + spin_unlock_irqrestore(&iounit->lock, flags); } static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct linux_sbus *sbus) { + unsigned long flags; + struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu; + + spin_lock_irqsave(&iounit->lock, flags); + len = ((vaddr & ~PAGE_MASK) + len + (PAGE_SIZE-1)) >> PAGE_SHIFT; + vaddr = (vaddr - IOUNIT_DMA_BASE) >> PAGE_SHIFT; + IOD(("iounit_release %08lx-%08lx\n", (long)vaddr, (long)len+vaddr)); + for (len += vaddr; vaddr < len; vaddr++) + clear_bit(vaddr, iounit->bmap); + spin_unlock_irqrestore(&iounit->lock, flags); } static void iounit_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) { + unsigned long flags; + unsigned long vaddr, len; + struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu; + + spin_lock_irqsave(&iounit->lock, flags); + for (; sz >= 0; sz--) { + len = ((sg[sz].dvma_addr & ~PAGE_MASK) + sg[sz].len + (PAGE_SIZE-1)) >> PAGE_SHIFT; + vaddr = (sg[sz].dvma_addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT; + IOD(("iounit_release %08lx-%08lx\n", (long)vaddr, (long)len+vaddr)); + for (len += vaddr; vaddr < len; vaddr++) + clear_bit(vaddr, iounit->bmap); + } + spin_unlock_irqrestore(&iounit->lock, flags); } #ifdef CONFIG_SBUS @@ -135,24 +215,26 @@ static void iounit_map_dma_area(unsigned long addr, int len) static char *iounit_lockarea(char *vaddr, unsigned long len) { +/* FIXME: Write this */ return vaddr; } static void iounit_unlockarea(char *vaddr, unsigned long len) { +/* FIXME: Write this */ } __initfunc(void ld_mmu_iounit(void)) { - mmu_lockarea = iounit_lockarea; - mmu_unlockarea = iounit_unlockarea; + BTFIXUPSET_CALL(mmu_lockarea, iounit_lockarea, BTFIXUPCALL_RETO0); + BTFIXUPSET_CALL(mmu_unlockarea, iounit_unlockarea, BTFIXUPCALL_NOP); - mmu_get_scsi_one = iounit_get_scsi_one; - mmu_get_scsi_sgl = iounit_get_scsi_sgl; - mmu_release_scsi_one = iounit_release_scsi_one; - mmu_release_scsi_sgl = iounit_release_scsi_sgl; + BTFIXUPSET_CALL(mmu_get_scsi_one, iounit_get_scsi_one, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_get_scsi_sgl, iounit_get_scsi_sgl, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_release_scsi_one, iounit_release_scsi_one, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_release_scsi_sgl, iounit_release_scsi_sgl, BTFIXUPCALL_NORM); #ifdef CONFIG_SBUS - mmu_map_dma_area = iounit_map_dma_area; + BTFIXUPSET_CALL(mmu_map_dma_area, iounit_map_dma_area, BTFIXUPCALL_NORM); #endif } diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c index 301946326..e46216233 100644 --- a/arch/sparc/mm/iommu.c +++ b/arch/sparc/mm/iommu.c @@ -1,10 +1,10 @@ -/* $Id: iommu.c,v 1.4 1997/11/21 17:31:31 jj Exp $ +/* $Id: iommu.c,v 1.7 1998/02/22 10:32:26 ecd Exp $ * iommu.c: IOMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h> @@ -18,8 +18,10 @@ /* srmmu.c */ extern int viking_mxcc_present; -extern void (*flush_page_for_dma)(unsigned long page); +BTFIXUPDEF_CALL(void, flush_page_for_dma, unsigned long) +#define flush_page_for_dma(page) BTFIXUP_CALL(flush_page_for_dma)(page) extern int flush_page_for_dma_global; +static int viking_flush = 0; /* viking.S */ extern void viking_flush_page(unsigned long page); extern void viking_mxcc_flush_page(unsigned long page); @@ -113,7 +115,7 @@ iommu_init(int iommund, unsigned long memory_start, viking_mxcc_flush_page(start); start += PAGE_SIZE; } - } else if(flush_page_for_dma == viking_flush_page) { + } else if (viking_flush) { unsigned long start = (unsigned long) iommu->page_table; unsigned long end = (start + ptsize); while(start < end) { @@ -199,7 +201,7 @@ static void iommu_map_dma_area(unsigned long addr, int len) pgprot_t dvma_prot; struct iommu_struct *iommu = SBus_chain->iommu; iopte_t *iopte = iommu->page_table; - iopte_t *iopte_first = iopte; + iopte_t *first; if(viking_mxcc_present) dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV); @@ -207,6 +209,7 @@ static void iommu_map_dma_area(unsigned long addr, int len) dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV); iopte += ((addr - iommu->start) >> PAGE_SHIFT); + first = iopte; end = PAGE_ALIGN((addr + len)); while(addr < end) { page = get_free_page(GFP_KERNEL); @@ -223,21 +226,20 @@ static void iommu_map_dma_area(unsigned long addr, int len) ptep = pte_offset(pmdp, addr); set_pte(ptep, pte_val(mk_pte(page, dvma_prot))); - iopte_val(*iopte++) = MKIOPTE(mmu_v2p(page)); } addr += PAGE_SIZE; } flush_cache_all(); if(viking_mxcc_present) { - unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK; + unsigned long start = ((unsigned long) first) & PAGE_MASK; unsigned long end = PAGE_ALIGN(((unsigned long) iopte)); while(start < end) { viking_mxcc_flush_page(start); start += PAGE_SIZE; } - } else if(flush_page_for_dma == viking_flush_page) { - unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK; + } else if(viking_flush) { + unsigned long start = ((unsigned long) first) & PAGE_MASK; unsigned long end = PAGE_ALIGN(((unsigned long) iopte)); while(start < end) { viking_flush_page(start); @@ -260,25 +262,26 @@ static void iommu_unlockarea(char *vaddr, unsigned long len) __initfunc(void ld_mmu_iommu(void)) { - mmu_lockarea = iommu_lockarea; - mmu_unlockarea = iommu_unlockarea; + viking_flush = (BTFIXUPVAL_CALL(flush_page_for_dma) == (unsigned long)viking_flush_page); + BTFIXUPSET_CALL(mmu_lockarea, iommu_lockarea, BTFIXUPCALL_RETO0); + BTFIXUPSET_CALL(mmu_unlockarea, iommu_unlockarea, BTFIXUPCALL_NOP); - if (!flush_page_for_dma) { + if (!BTFIXUPVAL_CALL(flush_page_for_dma)) { /* IO coherent chip */ - mmu_get_scsi_one = iommu_get_scsi_one_noflush; - mmu_get_scsi_sgl = iommu_get_scsi_sgl_noflush; + BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_noflush, BTFIXUPCALL_RETO0); + BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_noflush, BTFIXUPCALL_NORM); } else if (flush_page_for_dma_global) { /* flush_page_for_dma flushes everything, no matter of what page is it */ - mmu_get_scsi_one = iommu_get_scsi_one_gflush; - mmu_get_scsi_sgl = iommu_get_scsi_sgl_gflush; + BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_gflush, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_gflush, BTFIXUPCALL_NORM); } else { - mmu_get_scsi_one = iommu_get_scsi_one_pflush; - mmu_get_scsi_sgl = iommu_get_scsi_sgl_pflush; + BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_pflush, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_pflush, BTFIXUPCALL_NORM); } - mmu_release_scsi_one = iommu_release_scsi_one; - mmu_release_scsi_sgl = iommu_release_scsi_sgl; + BTFIXUPSET_CALL(mmu_release_scsi_one, iommu_release_scsi_one, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(mmu_release_scsi_sgl, iommu_release_scsi_sgl, BTFIXUPCALL_NOP); #ifdef CONFIG_SBUS - mmu_map_dma_area = iommu_map_dma_area; + BTFIXUPSET_CALL(mmu_map_dma_area, iommu_map_dma_area, BTFIXUPCALL_NORM); #endif } diff --git a/arch/sparc/mm/loadmmu.c b/arch/sparc/mm/loadmmu.c index 10eebecce..b38eea6d8 100644 --- a/arch/sparc/mm/loadmmu.c +++ b/arch/sparc/mm/loadmmu.c @@ -1,9 +1,10 @@ -/* $Id: loadmmu.c,v 1.46 1997/04/10 05:12:51 davem Exp $ +/* $Id: loadmmu.c,v 1.50 1998/02/05 14:19:02 jj Exp $ * loadmmu.c: This code loads up all the mm function pointers once the * machine type has been determined. It also sets the static * mmu values such as PAGE_NONE, etc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/kernel.h> @@ -16,6 +17,7 @@ #include <asm/pgtable.h> #include <asm/a.out.h> #include <asm/mmu_context.h> +#include <asm/oplib.h> unsigned long page_offset = 0xf0000000; unsigned long stack_top = 0xf0000000 - PAGE_SIZE; @@ -24,132 +26,8 @@ struct ctx_list *ctx_list_pool; struct ctx_list ctx_free; struct ctx_list ctx_used; -unsigned long (*alloc_kernel_stack)(struct task_struct *tsk); -void (*free_kernel_stack)(unsigned long stack); -struct task_struct *(*alloc_task_struct)(void); -void (*free_task_struct)(struct task_struct *tsk); - -void (*quick_kernel_fault)(unsigned long); - -void (*init_new_context)(struct mm_struct *mm); -void (*destroy_context)(struct mm_struct *mm); - -/* translate between physical and virtual addresses */ -unsigned long (*mmu_v2p)(unsigned long); -unsigned long (*mmu_p2v)(unsigned long); - -char *(*mmu_lockarea)(char *, unsigned long); -void (*mmu_unlockarea)(char *, unsigned long); - -__u32 (*mmu_get_scsi_one)(char *, unsigned long, struct linux_sbus *sbus); -void (*mmu_get_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus); -void (*mmu_release_scsi_one)(__u32, unsigned long, struct linux_sbus *sbus); -void (*mmu_release_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus); - -void (*mmu_map_dma_area)(unsigned long addr, int len); - -void (*update_mmu_cache)(struct vm_area_struct *vma, unsigned long address, pte_t pte); - -#ifdef __SMP__ -void (*local_flush_cache_all)(void); -void (*local_flush_cache_mm)(struct mm_struct *); -void (*local_flush_cache_range)(struct mm_struct *, unsigned long start, - unsigned long end); -void (*local_flush_cache_page)(struct vm_area_struct *, unsigned long address); - -void (*local_flush_tlb_all)(void); -void (*local_flush_tlb_mm)(struct mm_struct *); -void (*local_flush_tlb_range)(struct mm_struct *, unsigned long start, - unsigned long end); -void (*local_flush_tlb_page)(struct vm_area_struct *, unsigned long address); -void (*local_flush_page_to_ram)(unsigned long address); -void (*local_flush_sig_insns)(struct mm_struct *mm, unsigned long insn_addr); -#endif - -void (*flush_cache_all)(void); -void (*flush_cache_mm)(struct mm_struct *); -void (*flush_cache_range)(struct mm_struct *, unsigned long start, - unsigned long end); -void (*flush_cache_page)(struct vm_area_struct *, unsigned long address); - -void (*flush_tlb_all)(void); -void (*flush_tlb_mm)(struct mm_struct *); -void (*flush_tlb_range)(struct mm_struct *, unsigned long start, - unsigned long end); -void (*flush_tlb_page)(struct vm_area_struct *, unsigned long address); - -void (*flush_page_to_ram)(unsigned long page); - -void (*flush_sig_insns)(struct mm_struct *mm, unsigned long insn_addr); - -void (*set_pte)(pte_t *pteptr, pte_t pteval); - -unsigned int pmd_shift, pmd_size, pmd_mask; -unsigned int (*pmd_align)(unsigned int); -unsigned int pgdir_shift, pgdir_size, pgdir_mask; -unsigned int (*pgdir_align)(unsigned int); -unsigned int ptrs_per_pte, ptrs_per_pmd, ptrs_per_pgd; unsigned int pg_iobits; -pgprot_t page_none, page_shared, page_copy, page_readonly, page_kernel; - -unsigned long (*pte_page)(pte_t); -unsigned long (*pmd_page)(pmd_t); -unsigned long (*pgd_page)(pgd_t); - -void (*sparc_update_rootmmu_dir)(struct task_struct *, pgd_t *pgdir); -unsigned long (*(vmalloc_start))(void); -void (*switch_to_context)(struct task_struct *tsk); - -int (*pte_none)(pte_t); -int (*pte_present)(pte_t); -void (*pte_clear)(pte_t *); - -int (*pmd_none)(pmd_t); -int (*pmd_bad)(pmd_t); -int (*pmd_present)(pmd_t); -void (*pmd_clear)(pmd_t *); - -int (*pgd_none)(pgd_t); -int (*pgd_bad)(pgd_t); -int (*pgd_present)(pgd_t); -void (*pgd_clear)(pgd_t *); - -pte_t (*mk_pte)(unsigned long, pgprot_t); -pte_t (*mk_pte_phys)(unsigned long, pgprot_t); -pte_t (*mk_pte_io)(unsigned long, pgprot_t, int); -void (*pgd_set)(pgd_t *, pmd_t *); -pte_t (*pte_modify)(pte_t, pgprot_t); -pgd_t * (*pgd_offset)(struct mm_struct *, unsigned long); -pmd_t * (*pmd_offset)(pgd_t *, unsigned long); -pte_t * (*pte_offset)(pmd_t *, unsigned long); -void (*pte_free_kernel)(pte_t *); -pte_t * (*pte_alloc_kernel)(pmd_t *, unsigned long); - -void (*pmd_free_kernel)(pmd_t *); -pmd_t * (*pmd_alloc_kernel)(pgd_t *, unsigned long); -void (*pte_free)(pte_t *); -pte_t * (*pte_alloc)(pmd_t *, unsigned long); - -void (*pmd_free)(pmd_t *); -pmd_t * (*pmd_alloc)(pgd_t *, unsigned long); -void (*pgd_free)(pgd_t *); - -pgd_t * (*pgd_alloc)(void); - -int (*pte_write)(pte_t); -int (*pte_dirty)(pte_t); -int (*pte_young)(pte_t); - -pte_t (*pte_wrprotect)(pte_t); -pte_t (*pte_mkclean)(pte_t); -pte_t (*pte_mkold)(pte_t); -pte_t (*pte_mkwrite)(pte_t); -pte_t (*pte_mkdirty)(pte_t); -pte_t (*pte_mkyoung)(pte_t); - -char *(*mmu_info)(void); - extern void ld_mmu_sun4c(void); extern void ld_mmu_srmmu(void); @@ -157,6 +35,7 @@ __initfunc(void load_mmu(void)) { switch(sparc_cpu_model) { case sun4c: + case sun4: ld_mmu_sun4c(); break; case sun4m: @@ -169,9 +48,8 @@ __initfunc(void load_mmu(void)) break; #endif default: - printk("load_mmu:MMU support not available for this architecture\n"); - printk("load_mmu:sparc_cpu_model = %d\n", (int) sparc_cpu_model); - printk("load_mmu:Halting...\n"); - panic("load_mmu()"); + prom_printf("load_mmu: %d unsupported\n", (int)sparc_cpu_model); + prom_halt(); } + btfixup(); } diff --git a/arch/sparc/mm/nosrmmu.c b/arch/sparc/mm/nosrmmu.c new file mode 100644 index 000000000..f82599f42 --- /dev/null +++ b/arch/sparc/mm/nosrmmu.c @@ -0,0 +1,50 @@ +/* $Id: nosrmmu.c,v 1.1 1998/03/09 14:04:15 jj Exp $ + * nosrmmu.c: This file is a bunch of dummies for sun4 compiles, + * so that it does not need srmmu and avoid ifdefs. + * + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/init.h> +#include <asm/mbus.h> + +static char shouldnothappen[] __initdata = "SUN4 kernel can only run on SUN4\n"; + +enum mbus_module srmmu_modtype; + +__initfunc(static void should_not_happen(void)) +{ + prom_printf(shouldnothappen); + prom_halt(); +} + +__initfunc(void srmmu_frob_mem_map(unsigned long start_mem)) +{ + should_not_happen(); +} + +__initfunc(unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)) +{ + should_not_happen(); + return 0; +} + +__initfunc(void ld_mmu_srmmu(void)) +{ + should_not_happen(); +} + +void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly) +{ +} + +void srmmu_unmapioaddr(unsigned long virt_addr) +{ +} + +__initfunc(void srmmu_end_memory(unsigned long memory_size, unsigned long *mem_end_p)) +{ + return 0; +} diff --git a/arch/sparc/mm/nosun4c.c b/arch/sparc/mm/nosun4c.c new file mode 100644 index 000000000..7da883a31 --- /dev/null +++ b/arch/sparc/mm/nosun4c.c @@ -0,0 +1,77 @@ +/* $Id: nosun4c.c,v 1.1 1998/03/09 14:04:16 jj Exp $ + * nosun4c.c: This file is a bunch of dummies for SMP compiles, + * so that it does not need sun4c and avoid ifdefs. + * + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/init.h> +#include <asm/pgtable.h> + +static char shouldnothappen[] __initdata = "32bit SMP kernel only supports sun4m and sun4d\n"; + +/* Dummies */ +struct sun4c_mmu_ring { + unsigned long xxx1[3]; + unsigned char xxx2[2]; + int xxx3; +}; +struct sun4c_mmu_ring sun4c_kernel_ring; +struct sun4c_mmu_ring sun4c_kfree_ring; +unsigned long sun4c_kernel_faults; +unsigned long *sun4c_memerr_reg; + +__initfunc(static void should_not_happen(void)) +{ + prom_printf(shouldnothappen); + prom_halt(); +} + +__initfunc(unsigned long sun4c_paging_init(unsigned long start_mem, unsigned long end_mem)) +{ + should_not_happen(); + return 0; +} + +__initfunc(void ld_mmu_sun4c(void)) +{ + should_not_happen(); +} + +void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly) +{ +} + +void sun4c_unmapioaddr(unsigned long virt_addr) +{ +} + +void sun4c_complete_all_stores(void) +{ +} + +pgd_t *sun4c_pgd_offset(struct mm_struct * mm, unsigned long address) +{ + return NULL; +} + +pte_t *sun4c_pte_offset(pmd_t * dir, unsigned long address) +{ + return NULL; +} + +void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte) +{ +} + +__initfunc(void sun4c_probe_vac(void)) +{ + should_not_happen(); +} + +__initfunc(void sun4c_probe_memerr_reg(void)) +{ + should_not_happen(); +} diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index b16e3cc1e..f9794125d 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,10 +1,10 @@ -/* $Id: srmmu.c,v 1.156 1997/11/28 14:23:42 jj Exp $ +/* $Id: srmmu.c,v 1.170 1998/03/09 14:04:01 jj Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h> @@ -30,6 +30,7 @@ #include <asm/a.out.h> #include <asm/mmu_context.h> #include <asm/io-unit.h> +#include <asm/spinlock.h> /* Now the cpu specific definitions. */ #include <asm/viking.h> @@ -39,6 +40,11 @@ #include <asm/swift.h> #include <asm/turbosparc.h> +#include <asm/btfixup.h> + +/* #define DEBUG_MAP_KERNEL */ +/* #define PAGESKIP_DEBUG */ + enum mbus_module srmmu_modtype; unsigned int hwbug_bitmask; int vac_cache_size; @@ -47,10 +53,6 @@ int vac_badbits; extern unsigned long sparc_iobase_vaddr; -extern unsigned long sun4d_dma_base; -extern unsigned long sun4d_dma_size; -extern unsigned long sun4d_dma_vbase; - #ifdef __SMP__ #define FLUSH_BEGIN(mm) #define FLUSH_END @@ -60,16 +62,24 @@ extern unsigned long sun4d_dma_vbase; #endif static int phys_mem_contig; -long page_contig_offset; +BTFIXUPDEF_SETHI(page_contig_offset) + +BTFIXUPDEF_CALL(void, ctxd_set, ctxd_t *, pgd_t *) +BTFIXUPDEF_CALL(void, pmd_set, pmd_t *, pte_t *) -static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp); -static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep); +#define ctxd_set(ctxp,pgdp) BTFIXUP_CALL(ctxd_set)(ctxp,pgdp) +#define pmd_set(pmdp,ptep) BTFIXUP_CALL(pmd_set)(pmdp,ptep) -void (*flush_page_for_dma)(unsigned long page); +BTFIXUPDEF_CALL(void, flush_page_for_dma, unsigned long) +BTFIXUPDEF_CALL(void, flush_chunk, unsigned long) + +#define flush_page_for_dma(page) BTFIXUP_CALL(flush_page_for_dma)(page) int flush_page_for_dma_global = 1; -static void (*flush_chunk)(unsigned long chunk); +#define flush_chunk(chunk) BTFIXUP_CALL(flush_chunk)(chunk) #ifdef __SMP__ -static void (*local_flush_page_for_dma)(unsigned long page); +BTFIXUPDEF_CALL(void, local_flush_page_for_dma, unsigned long) + +#define local_flush_page_for_dma(page) BTFIXUP_CALL(local_flush_page_for_dma)(page) #endif static struct srmmu_stats { @@ -79,7 +89,7 @@ static struct srmmu_stats { int invmm; } module_stats; -static char *srmmu_name; +char *srmmu_name; ctxd_t *srmmu_ctx_table_phys; ctxd_t *srmmu_context_table; @@ -96,8 +106,8 @@ static struct srmmu_trans { #define SRMMU_HASHSZ 256 /* Not static, viking.S uses it. */ -struct srmmu_trans *srmmu_v2p_hash[SRMMU_HASHSZ]; -static struct srmmu_trans *srmmu_p2v_hash[SRMMU_HASHSZ]; +unsigned long srmmu_v2p_hash[SRMMU_HASHSZ]; +static unsigned long srmmu_p2v_hash[SRMMU_HASHSZ]; #define srmmu_ahashfn(addr) ((addr) >> 24) @@ -111,20 +121,17 @@ int viking_mxcc_present = 0; */ static inline unsigned long srmmu_v2p(unsigned long vaddr) { - struct srmmu_trans *tp = srmmu_v2p_hash[srmmu_ahashfn(vaddr)]; - - if(tp) - return (vaddr - tp->vbase + tp->pbase); - else - return 0xffffffffUL; + unsigned long off = srmmu_v2p_hash[srmmu_ahashfn(vaddr)]; + + return (vaddr + off); } static inline unsigned long srmmu_p2v(unsigned long paddr) { - struct srmmu_trans *tp = srmmu_p2v_hash[srmmu_ahashfn(paddr)]; - - if(tp) - return (paddr - tp->pbase + tp->vbase); + unsigned long off = srmmu_p2v_hash[srmmu_ahashfn(paddr)]; + + if (off != 0xffffffffUL) + return (paddr - off); else return 0xffffffffUL; } @@ -132,16 +139,47 @@ static inline unsigned long srmmu_p2v(unsigned long paddr) /* Physical memory on most SS1000/SC2000 can be contiguous, so we handle that case * as a special case to make things faster. */ +/* FIXME: gcc is stupid here and generates very very bad code in this + * heavily used routine. So we help it a bit. */ static inline unsigned long srmmu_c_v2p(unsigned long vaddr) { +#if KERNBASE != 0xf0000000 if (vaddr >= KERNBASE) return vaddr - KERNBASE; - return (vaddr - page_contig_offset); + return vaddr - BTFIXUP_SETHI(page_contig_offset); +#else + register unsigned long kernbase; + + __asm__ ("sethi %%hi(0xf0000000), %0" : "=r"(kernbase)); + return vaddr - ((vaddr >= kernbase) ? kernbase : BTFIXUP_SETHI(page_contig_offset)); +#endif } static inline unsigned long srmmu_c_p2v(unsigned long paddr) { +#if KERNBASE != 0xf0000000 if (paddr < (0xfd000000 - KERNBASE)) return paddr + KERNBASE; - return (paddr + page_contig_offset); + return (paddr + BTFIXUP_SETHI(page_contig_offset)); +#else + register unsigned long kernbase; + register unsigned long limit; + + __asm__ ("sethi %%hi(0x0d000000), %0" : "=r"(limit)); + __asm__ ("sethi %%hi(0xf0000000), %0" : "=r"(kernbase)); + + return paddr + ((paddr < limit) ? kernbase : BTFIXUP_SETHI(page_contig_offset)); +#endif +} + +/* On boxes where there is no lots_of_ram, KERNBASE is mapped to PA<0> and highest + PA is below 0x0d000000, we can optimize even more :) */ +static inline unsigned long srmmu_s_v2p(unsigned long vaddr) +{ + return vaddr - PAGE_OFFSET; +} + +static inline unsigned long srmmu_s_p2v(unsigned long paddr) +{ + return paddr + PAGE_OFFSET; } /* In general all page table modifications should use the V8 atomic @@ -157,19 +195,43 @@ static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value) /* Functions really use this, not srmmu_swap directly. */ #define srmmu_set_entry(ptr, newentry) srmmu_swap((unsigned long *) (ptr), (newentry)) +#ifdef PAGESKIP_DEBUG +#define PGSKIP_DEBUG(from,to) prom_printf("PG_skip %ld->%ld\n", (long)(from), (long)(to)); printk("PG_skip %ld->%ld\n", (long)(from), (long)(to)) +#else +#define PGSKIP_DEBUG(from,to) do { } while (0) +#endif + __initfunc(void srmmu_frob_mem_map(unsigned long start_mem)) { - unsigned long bank_start, bank_end; + unsigned long bank_start, bank_end = 0; unsigned long addr; int i; /* First, mark all pages as invalid. */ for(addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE) mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved); + + /* Next, pg[0-3] is sun4c cruft, so we can free it... */ + mem_map[MAP_NR(pg0)].flags &= ~(1<<PG_reserved); + mem_map[MAP_NR(pg1)].flags &= ~(1<<PG_reserved); + mem_map[MAP_NR(pg2)].flags &= ~(1<<PG_reserved); + mem_map[MAP_NR(pg3)].flags &= ~(1<<PG_reserved); start_mem = PAGE_ALIGN(start_mem); for(i = 0; srmmu_map[i].size; i++) { bank_start = srmmu_map[i].vbase; + + if (i && bank_start - bank_end > 2 * PAGE_SIZE) { + mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip); + mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(bank_start); + PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(bank_start)); + if (bank_end > KERNBASE && bank_start < KERNBASE) { + mem_map[0].flags |= (1<<PG_skip); + mem_map[0].next_hash = mem_map + MAP_NR(bank_start); + PGSKIP_DEBUG(0, MAP_NR(bank_start)); + } + } + bank_end = bank_start + srmmu_map[i].size; while(bank_start < bank_end) { if((bank_start >= KERNBASE) && @@ -180,23 +242,28 @@ __initfunc(void srmmu_frob_mem_map(unsigned long start_mem)) mem_map[MAP_NR(bank_start)].flags &= ~(1<<PG_reserved); bank_start += PAGE_SIZE; } + + if (bank_end == 0xfd000000) + bank_end = PAGE_OFFSET; } - if (sparc_cpu_model == sun4d) { - for (addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE) - if (addr < sun4d_dma_vbase || addr >= sun4d_dma_vbase + sun4d_dma_size) - clear_bit(PG_DMA, &mem_map[MAP_NR(addr)].flags); + + if (bank_end < KERNBASE) { + mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip); + mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(KERNBASE); + PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(KERNBASE)); + } else if (MAP_NR(bank_end) < max_mapnr) { + mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip); + if (mem_map[0].flags & (1 << PG_skip)) { + mem_map[MAP_NR(bank_end)].next_hash = mem_map[0].next_hash; + PGSKIP_DEBUG(MAP_NR(bank_end), mem_map[0].next_hash - mem_map); + } else { + mem_map[MAP_NR(bank_end)].next_hash = mem_map; + PGSKIP_DEBUG(MAP_NR(bank_end), 0); + } } } /* The very generic SRMMU page table operations. */ -static unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); } -static unsigned int srmmu_pgdir_align(unsigned int addr) { return SRMMU_PGDIR_ALIGN(addr); } - -static unsigned long srmmu_vmalloc_start(void) -{ - return SRMMU_VMALLOC_START; -} - static inline int srmmu_device_memory(unsigned long x) { return ((x & 0xF0000000) != 0); @@ -220,44 +287,53 @@ static unsigned long srmmu_c_pmd_page(pmd_t pmd) static unsigned long srmmu_c_pte_page(pte_t pte) { return srmmu_device_memory(pte_val(pte))?~0:srmmu_c_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); } -static int srmmu_pte_none(pte_t pte) +static unsigned long srmmu_s_pgd_page(pgd_t pgd) +{ return srmmu_device_memory(pgd_val(pgd))?~0:srmmu_s_p2v((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); } + +static unsigned long srmmu_s_pmd_page(pmd_t pmd) +{ return srmmu_device_memory(pmd_val(pmd))?~0:srmmu_s_p2v((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); } + +static unsigned long srmmu_s_pte_page(pte_t pte) +{ return srmmu_device_memory(pte_val(pte))?~0:srmmu_s_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); } + +static inline int srmmu_pte_none(pte_t pte) { return !(pte_val(pte) & 0xFFFFFFF); } -static int srmmu_pte_present(pte_t pte) +static inline int srmmu_pte_present(pte_t pte) { return ((pte_val(pte) & SRMMU_ET_MASK) == SRMMU_ET_PTE); } -static void srmmu_pte_clear(pte_t *ptep) { set_pte(ptep, __pte(0)); } +static inline void srmmu_pte_clear(pte_t *ptep) { set_pte(ptep, __pte(0)); } -static int srmmu_pmd_none(pmd_t pmd) +static inline int srmmu_pmd_none(pmd_t pmd) { return !(pmd_val(pmd) & 0xFFFFFFF); } -static int srmmu_pmd_bad(pmd_t pmd) +static inline int srmmu_pmd_bad(pmd_t pmd) { return (pmd_val(pmd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; } -static int srmmu_pmd_present(pmd_t pmd) +static inline int srmmu_pmd_present(pmd_t pmd) { return ((pmd_val(pmd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); } -static void srmmu_pmd_clear(pmd_t *pmdp) { set_pte((pte_t *)pmdp, __pte(0)); } +static inline void srmmu_pmd_clear(pmd_t *pmdp) { set_pte((pte_t *)pmdp, __pte(0)); } -static int srmmu_pgd_none(pgd_t pgd) +static inline int srmmu_pgd_none(pgd_t pgd) { return !(pgd_val(pgd) & 0xFFFFFFF); } -static int srmmu_pgd_bad(pgd_t pgd) +static inline int srmmu_pgd_bad(pgd_t pgd) { return (pgd_val(pgd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; } -static int srmmu_pgd_present(pgd_t pgd) +static inline int srmmu_pgd_present(pgd_t pgd) { return ((pgd_val(pgd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); } -static void srmmu_pgd_clear(pgd_t * pgdp) { set_pte((pte_t *)pgdp, __pte(0)); } +static inline void srmmu_pgd_clear(pgd_t * pgdp) { set_pte((pte_t *)pgdp, __pte(0)); } -static int srmmu_pte_write(pte_t pte) { return pte_val(pte) & SRMMU_WRITE; } -static int srmmu_pte_dirty(pte_t pte) { return pte_val(pte) & SRMMU_DIRTY; } -static int srmmu_pte_young(pte_t pte) { return pte_val(pte) & SRMMU_REF; } +static inline int srmmu_pte_write(pte_t pte) { return pte_val(pte) & SRMMU_WRITE; } +static inline int srmmu_pte_dirty(pte_t pte) { return pte_val(pte) & SRMMU_DIRTY; } +static inline int srmmu_pte_young(pte_t pte) { return pte_val(pte) & SRMMU_REF; } -static pte_t srmmu_pte_wrprotect(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_WRITE);} -static pte_t srmmu_pte_mkclean(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_DIRTY);} -static pte_t srmmu_pte_mkold(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_REF);} -static pte_t srmmu_pte_mkwrite(pte_t pte) { return __pte(pte_val(pte) | SRMMU_WRITE);} -static pte_t srmmu_pte_mkdirty(pte_t pte) { return __pte(pte_val(pte) | SRMMU_DIRTY);} -static pte_t srmmu_pte_mkyoung(pte_t pte) { return __pte(pte_val(pte) | SRMMU_REF);} +static inline pte_t srmmu_pte_wrprotect(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_WRITE);} +static inline pte_t srmmu_pte_mkclean(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_DIRTY);} +static inline pte_t srmmu_pte_mkold(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_REF);} +static inline pte_t srmmu_pte_mkwrite(pte_t pte) { return __pte(pte_val(pte) | SRMMU_WRITE);} +static inline pte_t srmmu_pte_mkdirty(pte_t pte) { return __pte(pte_val(pte) | SRMMU_DIRTY);} +static inline pte_t srmmu_pte_mkyoung(pte_t pte) { return __pte(pte_val(pte) | SRMMU_REF);} /* * Conversion functions: convert a page and protection to a page entry, @@ -269,6 +345,9 @@ static pte_t srmmu_mk_pte(unsigned long page, pgprot_t pgprot) static pte_t srmmu_c_mk_pte(unsigned long page, pgprot_t pgprot) { return __pte(((srmmu_c_v2p(page)) >> 4) | pgprot_val(pgprot)); } +static pte_t srmmu_s_mk_pte(unsigned long page, pgprot_t pgprot) +{ return __pte(((srmmu_s_v2p(page)) >> 4) | pgprot_val(pgprot)); } + static pte_t srmmu_mk_pte_phys(unsigned long page, pgprot_t pgprot) { return __pte(((page) >> 4) | pgprot_val(pgprot)); } @@ -307,41 +386,64 @@ static void srmmu_c_pmd_set(pmd_t * pmdp, pte_t * ptep) set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_c_v2p((unsigned long) ptep) >> 4))); } -static pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot) +static void srmmu_s_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp) +{ + set_pte((pte_t *)ctxp, (SRMMU_ET_PTD | (srmmu_s_v2p((unsigned long) pgdp) >> 4))); +} + +static void srmmu_s_pgd_set(pgd_t * pgdp, pmd_t * pmdp) +{ + set_pte((pte_t *)pgdp, (SRMMU_ET_PTD | (srmmu_s_v2p((unsigned long) pmdp) >> 4))); +} + +static void srmmu_s_pmd_set(pmd_t * pmdp, pte_t * ptep) +{ + set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_s_v2p((unsigned long) ptep) >> 4))); +} + +static inline pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot) { return __pte((pte_val(pte) & SRMMU_CHG_MASK) | pgprot_val(newprot)); } /* to find an entry in a top-level page table... */ -static pgd_t *srmmu_pgd_offset(struct mm_struct * mm, unsigned long address) +static inline pgd_t *srmmu_pgd_offset(struct mm_struct * mm, unsigned long address) { - return mm->pgd + ((address >> SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD - 1)); + return mm->pgd + (address >> SRMMU_PGDIR_SHIFT); } /* Find an entry in the second-level page table.. */ -static pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address) +static inline pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address) { return (pmd_t *) srmmu_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1)); } /* Find an entry in the third-level page table.. */ -static pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address) +static inline pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address) { return (pte_t *) srmmu_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)); } -/* Find an entry in the second-level page table.. */ -static pmd_t *srmmu_c_pmd_offset(pgd_t * dir, unsigned long address) +static inline pmd_t *srmmu_c_pmd_offset(pgd_t * dir, unsigned long address) { return (pmd_t *) srmmu_c_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1)); } -/* Find an entry in the third-level page table.. */ -static pte_t *srmmu_c_pte_offset(pmd_t * dir, unsigned long address) +static inline pte_t *srmmu_c_pte_offset(pmd_t * dir, unsigned long address) { return (pte_t *) srmmu_c_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)); } +static inline pmd_t *srmmu_s_pmd_offset(pgd_t * dir, unsigned long address) +{ + return (pmd_t *) srmmu_s_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1)); +} + +static inline pte_t *srmmu_s_pte_offset(pmd_t * dir, unsigned long address) +{ + return (pte_t *) srmmu_s_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)); +} + /* This must update the context table entry for this process. */ static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { @@ -352,334 +454,146 @@ static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) } } -static inline void srmmu_putpage(unsigned long page) -{ - free_page(page); -} - -#define LC_HIGH_WATER 128 -#define BC_HIGH_WATER 32 - -static unsigned long *lcnks = 0; -static unsigned long *bcnks = 0; -static int lcwater = 0; -static int bcwater = 0; -static int chunk_pages = 0; -static int clct_pages = 0; - -#define RELAX_JIFFIES 16 - -static int lcjiffies; -static int bcjiffies; - -struct chunk { - struct chunk *next; - struct chunk *prev; - struct chunk *npage; - struct chunk *ppage; - int count; -}; - -static int garbage_calls = 0; - -#define OTHER_PAGE(p,q) (((unsigned long)(p) ^ (unsigned long)(q)) & PAGE_MASK) - -static int garbage_collect(unsigned long **cnks, int n, int cpp) +static inline pte_t *srmmu_get_pte_fast(void) { - struct chunk *root = (struct chunk *)*cnks; - struct chunk *p, *q, *curr, *next; - int water = n; - - next = root->next; - curr = root->prev = root->next = root->npage = root->ppage = root; - root->count = 1; - - garbage_calls++; - - while (--n) { - p = next; - next = next->next; - - if (OTHER_PAGE(p, curr)) { - - q = curr->npage; - while (q != curr) { - if (!OTHER_PAGE(p, q)) - break; - q = q->npage; - } - - if (q == curr) { - - (p->npage = curr->npage)->ppage = p; - curr->npage = p; - p->ppage = curr; - - p->next = p->prev = p; - p->count = 1; - - curr = p; - - continue; - } - curr = q; - } - - (p->next = curr->next)->prev = p; - curr->next = p; - p->prev = curr; - - if (++curr->count == cpp) { - - q = curr->npage; - if (curr == q) { - - srmmu_putpage((unsigned long)curr & PAGE_MASK); - water -= cpp; - - clct_pages++; - chunk_pages--; - - if (--n) { - p = next; - next = next->next; - - curr = root->prev = - root->next = root->npage = - root->ppage = root = p; - root->count = 1; - - continue; - } - return 0; - } - - if (curr == root) - root = q; - - curr->ppage->npage = q; - q->ppage = curr->ppage; - - srmmu_putpage((unsigned long)curr & PAGE_MASK); - water -= cpp; - - clct_pages++; - chunk_pages--; - - curr = q; - } - } - - p = root; - while (p->npage != root) { - p->prev->next = p->npage; - p = p->npage; + struct page *ret; + + spin_lock(&pte_spinlock); + if ((ret = (struct page *)pte_quicklist) != NULL) { + unsigned int mask = (unsigned int)ret->pprev_hash; + unsigned int tmp, off; + + if (mask & 0xff) + for (tmp = 0x001, off = 0; (mask & tmp) == 0; tmp <<= 1, off += 256); + else + for (tmp = 0x100, off = 2048; (mask & tmp) == 0; tmp <<= 1, off += 256); + (unsigned int)ret->pprev_hash = mask & ~tmp; + if (!(mask & ~tmp)) + pte_quicklist = (unsigned long *)ret->next_hash; + ret = (struct page *)(PAGE_OFFSET + (ret->map_nr << PAGE_SHIFT) + off); + pgtable_cache_size--; } - - *cnks = (unsigned long *)root; - return water; + spin_unlock(&pte_spinlock); + return (pte_t *)ret; } -static unsigned long *get_small_chunk(void) +static inline pte_t *srmmu_get_pte_slow(void) { - unsigned long *rval; - unsigned long flags; - - save_and_cli(flags); - if(lcwater) { - lcwater--; - rval = lcnks; - lcnks = (unsigned long *) *rval; - } else { - rval = (unsigned long *) __get_free_page(GFP_KERNEL); - - if(!rval) { - restore_flags(flags); - return 0; - } - chunk_pages++; - - lcnks = (rval + 64); - - /* Cache stomping, I know... */ - *(rval + 64) = (unsigned long) (rval + 128); - *(rval + 128) = (unsigned long) (rval + 192); - *(rval + 192) = (unsigned long) (rval + 256); - *(rval + 256) = (unsigned long) (rval + 320); - *(rval + 320) = (unsigned long) (rval + 384); - *(rval + 384) = (unsigned long) (rval + 448); - *(rval + 448) = (unsigned long) (rval + 512); - *(rval + 512) = (unsigned long) (rval + 576); - *(rval + 576) = (unsigned long) (rval + 640); - *(rval + 640) = (unsigned long) (rval + 704); - *(rval + 704) = (unsigned long) (rval + 768); - *(rval + 768) = (unsigned long) (rval + 832); - *(rval + 832) = (unsigned long) (rval + 896); - *(rval + 896) = (unsigned long) (rval + 960); - *(rval + 960) = 0; - lcwater = 15; + pte_t *ret; + struct page *page; + + ret = (pte_t *)get_free_page(GFP_KERNEL); + if (ret) { + page = mem_map + MAP_NR(ret); + flush_chunk((unsigned long)ret); + (unsigned int)page->pprev_hash = 0xfffe; + spin_lock(&pte_spinlock); + (unsigned long *)page->next_hash = pte_quicklist; + pte_quicklist = (unsigned long *)page; + pgtable_cache_size += 15; } - lcjiffies = jiffies; - restore_flags(flags); - memset(rval, 0, 256); - flush_chunk((unsigned long)rval); - return rval; -} - -static inline void free_small_chunk(unsigned long *it) -{ - unsigned long flags; - - save_and_cli(flags); - *it = (unsigned long) lcnks; - lcnks = it; - lcwater++; - - if ((lcwater > LC_HIGH_WATER) && - (jiffies > lcjiffies + RELAX_JIFFIES)) - lcwater = garbage_collect(&lcnks, lcwater, 16); - - restore_flags(flags); + return ret; } -static unsigned long *get_big_chunk(void) +static inline pgd_t *srmmu_get_pgd_fast(void) { - unsigned long *rval; - unsigned long flags; - - save_and_cli(flags); - if(bcwater) { - bcwater--; - rval = bcnks; - bcnks = (unsigned long *) *rval; - } else { - rval = (unsigned long *) __get_free_page(GFP_KERNEL); - - if(!rval) { - restore_flags(flags); - return 0; - } - chunk_pages++; - - bcnks = (rval + 256); + struct page *ret; - /* Cache stomping, I know... */ - *(rval + 256) = (unsigned long) (rval + 512); - *(rval + 512) = (unsigned long) (rval + 768); - *(rval + 768) = 0; - bcwater = 3; + spin_lock(&pgd_spinlock); + if ((ret = (struct page *)pgd_quicklist) != NULL) { + unsigned int mask = (unsigned int)ret->pprev_hash; + unsigned int tmp, off; + + for (tmp = 0x001, off = 0; (mask & tmp) == 0; tmp <<= 1, off += 1024); + (unsigned int)ret->pprev_hash = mask & ~tmp; + if (!(mask & ~tmp)) + pgd_quicklist = (unsigned long *)ret->next_hash; + ret = (struct page *)(PAGE_OFFSET + (ret->map_nr << PAGE_SHIFT) + off); + pgd_cache_size--; } - bcjiffies = jiffies; - restore_flags(flags); - memset(rval, 0, 1024); - flush_chunk((unsigned long)rval); - return rval; + spin_unlock(&pgd_spinlock); + return (pte_t *)ret; } -static inline void free_big_chunk(unsigned long *it) +static inline pgd_t *srmmu_get_pgd_slow(void) { - unsigned long flags; - - save_and_cli(flags); - *it = (unsigned long) bcnks; - bcnks = it; - bcwater++; - - if ((bcwater > BC_HIGH_WATER) && - (jiffies > bcjiffies + RELAX_JIFFIES)) - bcwater = garbage_collect(&bcnks, bcwater, 4); - - restore_flags(flags); + pgd_t *ret; + struct page *page; + + ret = (pgd_t *)__get_free_page(GFP_KERNEL); + if (ret) { + pgd_t *init = pgd_offset(&init_mm, 0); + memset(ret + (0 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); + memcpy(ret + (0 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + memset(ret + (1 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); + memcpy(ret + (1 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + memset(ret + (2 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); + memcpy(ret + (2 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + memset(ret + (3 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); + memcpy(ret + (3 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + page = mem_map + MAP_NR(ret); + flush_chunk((unsigned long)ret); + (unsigned int)page->pprev_hash = 0xe; + spin_lock(&pgd_spinlock); + (unsigned long *)page->next_hash = pgd_quicklist; + pgd_quicklist = (unsigned long *)page; + pgd_cache_size += 3; + spin_unlock(&pgd_spinlock); + } + return ret; } -#define NEW_PGD() (pgd_t *) get_big_chunk() -#define NEW_PMD() (pmd_t *) get_small_chunk() -#define NEW_PTE() (pte_t *) get_small_chunk() -#define FREE_PGD(chunk) free_big_chunk((unsigned long *)(chunk)) -#define FREE_PMD(chunk) free_small_chunk((unsigned long *)(chunk)) -#define FREE_PTE(chunk) free_small_chunk((unsigned long *)(chunk)) - -/* - * Allocate and free page tables. The xxx_kernel() versions are - * used to allocate a kernel page table - this turns on ASN bits - * if any, and marks the page tables reserved. - */ -static void srmmu_pte_free_kernel(pte_t *pte) +static void srmmu_free_pte_slow(pte_t *pte) { - FREE_PTE(pte); } -static pte_t *srmmu_pte_alloc_kernel(pmd_t *pmd, unsigned long address) +static void srmmu_free_pgd_slow(pgd_t *pgd) { - address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1); - if(srmmu_pmd_none(*pmd)) { - pte_t *page = NEW_PTE(); - if(srmmu_pmd_none(*pmd)) { - if(page) { - pmd_set(pmd, page); - return page + address; - } - pmd_set(pmd, BAD_PAGETABLE); - return NULL; - } - FREE_PTE(page); - } - if(srmmu_pmd_bad(*pmd)) { - printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); - pmd_set(pmd, BAD_PAGETABLE); - return NULL; - } - return (pte_t *) pmd_page(*pmd) + address; } -static void srmmu_pmd_free_kernel(pmd_t *pmd) +static inline void srmmu_pte_free(pte_t *pte) { - FREE_PMD(pmd); -} + struct page *page = mem_map + MAP_NR(pte); -static pmd_t *srmmu_pmd_alloc_kernel(pgd_t *pgd, unsigned long address) -{ - address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1); - if(srmmu_pgd_none(*pgd)) { - pmd_t *page; - page = NEW_PMD(); - if(srmmu_pgd_none(*pgd)) { - if(page) { - pgd_set(pgd, page); - return page + address; - } - pgd_set(pgd, (pmd_t *) BAD_PAGETABLE); - return NULL; - } - FREE_PMD(page); + spin_lock(&pte_spinlock); + if (!page->pprev_hash) { + (unsigned long *)page->next_hash = pte_quicklist; + pte_quicklist = (unsigned long *)page; } - if(srmmu_pgd_bad(*pgd)) { - printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd)); - pgd_set(pgd, (pmd_t *) BAD_PAGETABLE); - return NULL; - } - return (pmd_t *) pgd_page(*pgd) + address; -} - -static void srmmu_pte_free(pte_t *pte) -{ - FREE_PTE(pte); + (unsigned int)page->pprev_hash |= (1 << ((((unsigned long)pte) >> 8) & 15)); + pgtable_cache_size++; + spin_unlock(&pte_spinlock); } static pte_t *srmmu_pte_alloc(pmd_t * pmd, unsigned long address) { address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1); if(srmmu_pmd_none(*pmd)) { - pte_t *page = NEW_PTE(); + pte_t *page = srmmu_get_pte_fast(); + + if (page) { + pmd_set(pmd, page); + return page + address; + } + page = srmmu_get_pte_slow(); if(srmmu_pmd_none(*pmd)) { if(page) { + spin_unlock(&pte_spinlock); pmd_set(pmd, page); return page + address; } pmd_set(pmd, BAD_PAGETABLE); return NULL; } - FREE_PTE(page); + if (page) { + (unsigned int)(((struct page *)pte_quicklist)->pprev_hash) = 0xffff; + pgtable_cache_size++; + spin_unlock(&pte_spinlock); + } } if(srmmu_pmd_bad(*pmd)) { printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); @@ -692,23 +606,34 @@ static pte_t *srmmu_pte_alloc(pmd_t * pmd, unsigned long address) /* Real three-level page tables on SRMMU. */ static void srmmu_pmd_free(pmd_t * pmd) { - FREE_PMD(pmd); + return srmmu_pte_free((pte_t *)pmd); } static pmd_t *srmmu_pmd_alloc(pgd_t * pgd, unsigned long address) { address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1); if(srmmu_pgd_none(*pgd)) { - pmd_t *page = NEW_PMD(); + pmd_t *page = (pmd_t *)srmmu_get_pte_fast(); + + if (page) { + pgd_set(pgd, page); + return page + address; + } + page = (pmd_t *)srmmu_get_pte_slow(); if(srmmu_pgd_none(*pgd)) { if(page) { + spin_unlock(&pte_spinlock); pgd_set(pgd, page); return page + address; } pgd_set(pgd, (pmd_t *) BAD_PAGETABLE); return NULL; } - FREE_PMD(page); + if (page) { + (unsigned int)(((struct page *)pte_quicklist)->pprev_hash) = 0xffff; + pgtable_cache_size++; + spin_unlock(&pte_spinlock); + } } if(srmmu_pgd_bad(*pgd)) { printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd)); @@ -720,12 +645,58 @@ static pmd_t *srmmu_pmd_alloc(pgd_t * pgd, unsigned long address) static void srmmu_pgd_free(pgd_t *pgd) { - FREE_PGD(pgd); + struct page *page = mem_map + MAP_NR(pgd); + + spin_lock(&pgd_spinlock); + if (!page->pprev_hash) { + (unsigned long *)page->next_hash = pgd_quicklist; + pgd_quicklist = (unsigned long *)page; + } + (unsigned int)page->pprev_hash |= (1 << ((((unsigned long)pgd) >> 10) & 3)); + pgd_cache_size++; + spin_unlock(&pgd_spinlock); } static pgd_t *srmmu_pgd_alloc(void) { - return NEW_PGD(); + pgd_t *ret; + + ret = srmmu_get_pgd_fast(); + if (ret) return ret; + return srmmu_get_pgd_slow(); +} + + +static void srmmu_set_pgdir(unsigned long address, pgd_t entry) +{ + struct task_struct * p; + struct page *page; + + read_lock(&tasklist_lock); + for_each_task(p) { + if (!p->mm) + continue; + *pgd_offset(p->mm,address) = entry; + } + read_unlock(&tasklist_lock); + spin_lock(&pgd_spinlock); + address >>= SRMMU_PGDIR_SHIFT; + for (page = (struct page *)pgd_quicklist; page; page = page->next_hash) { + pgd_t *pgd = (pgd_t *)(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT)); + unsigned int mask = (unsigned int)page->pprev_hash; + + if (mask & 1) + pgd[address + 0 * SRMMU_PTRS_PER_PGD] = entry; + if (mask & 2) + pgd[address + 1 * SRMMU_PTRS_PER_PGD] = entry; + if (mask & 4) + pgd[address + 2 * SRMMU_PTRS_PER_PGD] = entry; + if (mask & 8) + pgd[address + 3 * SRMMU_PTRS_PER_PGD] = entry; + if (mask) + flush_chunk((unsigned long)pgd); + } + spin_unlock(&pgd_spinlock); } static void srmmu_set_pte_cacheable(pte_t *ptep, pte_t pteval) @@ -926,6 +897,19 @@ extern void tsunami_flush_tlb_mm(struct mm_struct *mm); extern void tsunami_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end); extern void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page); +/* Workaround, until we find what's going on with Swift. When low on memory, it sometimes + * loops in fault/handle_mm_fault incl. flush_tlb_page to find out it is already in page tables/ + * fault again on the same instruction. I really don't understand it, have checked it and contexts + * are right, flush_tlb_all is done as well, and it faults again... Strange. -jj + */ +static void swift_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) +{ + static unsigned long last; + + if (last == address) viking_hwprobe(address); + last = address; +} + /* Swift flushes. It has the recommended SRMMU specification flushing * facilities, so we can do things in a more fine grained fashion than we * could on the tsunami. Let's watch out for HARDWARE BUGS... @@ -1191,12 +1175,10 @@ static void cypress_flush_chunk(unsigned long chunk) cypress_flush_page_to_ram(chunk); } -#if NOTUSED /* Cypress is also IO cache coherent. */ static void cypress_flush_page_for_dma(unsigned long page) { } -#endif /* Cypress has unified L2 VIPT, from which both instructions and data * are stored. It does not have an onboard icache of any sort, therefore @@ -1282,9 +1264,8 @@ extern void viking_flush_sig_insns(struct mm_struct *mm, unsigned long addr); extern void viking_flush_page(unsigned long page); extern void viking_mxcc_flush_page(unsigned long page); extern void viking_flush_chunk(unsigned long chunk); -extern void viking_c_flush_page(unsigned long page); -extern void viking_c_mxcc_flush_page(unsigned long page); extern void viking_c_flush_chunk(unsigned long chunk); +extern void viking_s_flush_chunk(unsigned long chunk); extern void viking_mxcc_flush_chunk(unsigned long chunk); extern void viking_flush_tlb_all(void); extern void viking_flush_tlb_mm(struct mm_struct *mm); @@ -1481,7 +1462,7 @@ static inline void srmmu_allocate_ptable_skeleton(unsigned long start, unsigned * looking at the prom's page table directly which is what most * other OS's do. Yuck... this is much better. */ -void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end) +__initfunc(void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end)) { pgd_t *pgdp; pmd_t *pmdp; @@ -1539,21 +1520,79 @@ void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end) } } -/* #define DEBUG_MAP_KERNEL */ - #ifdef DEBUG_MAP_KERNEL #define MKTRACE(foo) prom_printf foo #else #define MKTRACE(foo) #endif -static int lots_of_ram = 0; -static int large_pte_optimize = 1; +static int lots_of_ram __initdata = 0; +static int srmmu_low_pa __initdata = 0; +static unsigned long end_of_phys_memory __initdata = 0; + +__initfunc(void srmmu_end_memory(unsigned long memory_size, unsigned long *end_mem_p)) +{ + unsigned int sum = 0; + unsigned long last = 0xff000000; + long first, cur; + unsigned long pa; + unsigned long total = 0; + int i; + + pa = srmmu_hwprobe(KERNBASE + PAGE_SIZE); + pa = (pa & SRMMU_PTE_PMASK) << 4; + if (!sp_banks[0].base_addr && pa == PAGE_SIZE) { + for(i = 0; sp_banks[i].num_bytes != 0; i++) { + if (sp_banks[i].base_addr + sp_banks[i].num_bytes > 0x0d000000) + break; + } + if (!sp_banks[i].num_bytes) { + srmmu_low_pa = 1; + end_of_phys_memory = SRMMU_PGDIR_ALIGN(sp_banks[i-1].base_addr + sp_banks[i-1].num_bytes); + *end_mem_p = KERNBASE + end_of_phys_memory; + if (sp_banks[0].num_bytes >= (6 * 1024 * 1024) || end_of_phys_memory <= 0x06000000) { + /* Make sure there will be enough memory for the whole mem_map (even if sparse) */ + return; + } + } + } + for(i = 0; sp_banks[i].num_bytes != 0; i++) { + pa = sp_banks[i].base_addr; + first = (pa & (~SRMMU_PGDIR_MASK)); + cur = (sp_banks[i].num_bytes + first - SRMMU_PGDIR_SIZE); + if (cur < 0) cur = 0; + if (!first || last != (pa & SRMMU_PGDIR_MASK)) + total += SRMMU_PGDIR_SIZE; + sum += sp_banks[i].num_bytes; + if (memory_size) { + if (sum > memory_size) { + sp_banks[i].num_bytes -= + (sum - memory_size); + cur = (sp_banks[i].num_bytes + first - SRMMU_PGDIR_SIZE); + if (cur < 0) cur = 0; + total += SRMMU_PGDIR_ALIGN(cur); + sum = memory_size; + sp_banks[++i].base_addr = 0xdeadbeef; + sp_banks[i].num_bytes = 0; + break; + } + } + total += SRMMU_PGDIR_ALIGN(cur); + last = (sp_banks[i].base_addr + sp_banks[i].num_bytes - 1) & SRMMU_PGDIR_MASK; + } + if (total <= 0x0d000000) + *end_mem_p = KERNBASE + total; + else { + *end_mem_p = 0xfd000000; + lots_of_ram = 1; + } + end_of_phys_memory = total; +} #define KERNEL_PTE(page_shifted) ((page_shifted)|SRMMU_CACHE|SRMMU_PRIV|SRMMU_VALID) /* Create a third-level SRMMU 16MB page mapping. */ -static inline void do_large_mapping(unsigned long vaddr, unsigned long phys_base) +__initfunc(static void do_large_mapping(unsigned long vaddr, unsigned long phys_base)) { pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, vaddr); unsigned long big_pte; @@ -1563,47 +1602,6 @@ static inline void do_large_mapping(unsigned long vaddr, unsigned long phys_base *pgdp = __pgd(big_pte); } -/* Create second-level SRMMU 256K medium sized page mappings. */ -static inline void do_medium_mapping(unsigned long vaddr, unsigned long vend, - unsigned long phys_base) -{ - pgd_t *pgdp; - pmd_t *pmdp; - unsigned long medium_pte; - - MKTRACE(("dmm[v<%08lx,%08lx>-->p<%08lx>]", vaddr, vend, phys_base)); - while(vaddr < vend) { - pgdp = srmmu_pgd_offset(init_task.mm, vaddr); - pmdp = srmmu_early_pmd_offset(pgdp, vaddr); - medium_pte = KERNEL_PTE(phys_base >> 4); - *pmdp = __pmd(medium_pte); - phys_base += SRMMU_PMD_SIZE; - vaddr += SRMMU_PMD_SIZE; - } -} - -/* Create a normal set of SRMMU page mappings for the virtual range - * START to END, using physical pages beginning at PHYS_BASE. - */ -static inline void do_small_mapping(unsigned long start, unsigned long end, - unsigned long phys_base) -{ - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - MKTRACE(("dsm[v<%08lx,%08lx>-->p<%08lx>]", start, end, phys_base)); - while(start < end) { - pgdp = srmmu_pgd_offset(init_task.mm, start); - pmdp = srmmu_early_pmd_offset(pgdp, start); - ptep = srmmu_early_pte_offset(pmdp, start); - - *ptep = __pte(KERNEL_PTE(phys_base >> 4)); - phys_base += PAGE_SIZE; - start += PAGE_SIZE; - } -} - /* Look in the sp_bank for the given physical page, return the * index number the entry was found in, or -1 for not found. */ @@ -1625,7 +1623,7 @@ static inline int find_in_spbanks(unsigned long phys_page) * array of char's, each member indicating if that spbank is mapped * yet or not. */ -static inline int find_free_spbank(char *taken_vector) +__initfunc(static int find_free_spbank(char *taken_vector)) { int entry; @@ -1635,78 +1633,28 @@ static inline int find_free_spbank(char *taken_vector) return entry; } -/* Same as above, but with a given bank size limit BLIMIT. */ -static inline int find_free_spbank_limited(char *taken_vector, unsigned long limit) -{ - int entry; - - for(entry = 0; sp_banks[entry].num_bytes; entry++) - if(!taken_vector[entry] && - (sp_banks[entry].num_bytes < limit)) - break; - return entry; -} +static unsigned long map_spbank_last_pa __initdata = 0xff000000; /* Map sp_bank entry SP_ENTRY, starting at virtual address VBASE. - * This routine is expected to update the srmmu_map and try as - * hard as possible to use 16MB level-one SRMMU pte's when at all - * possible to get short termination and faster translations. */ -static inline unsigned long map_spbank(unsigned long vbase, int sp_entry) +__initfunc(static unsigned long map_spbank(unsigned long vbase, int sp_entry)) { - unsigned long pstart = sp_banks[sp_entry].base_addr; - unsigned long vstart = vbase; - unsigned long vend = vbase + sp_banks[sp_entry].num_bytes; + unsigned long pstart = (sp_banks[sp_entry].base_addr & SRMMU_PGDIR_MASK); + unsigned long vstart = (vbase & SRMMU_PGDIR_MASK); + unsigned long vend = SRMMU_PGDIR_ALIGN(vbase + sp_banks[sp_entry].num_bytes); static int srmmu_bank = 0; - /* If physically not aligned on 16MB boundry, just shortcut - * right here by mapping them with 4k normal pages, and bumping - * the next virtual address to the next 16MB boundry. You can - * get this with various RAM configurations due to the way in - * which the PROM carves out it's own chunks of memory. - */ - if(pstart & ~SRMMU_PGDIR_MASK) { - do_small_mapping(vstart, vend, pstart); - vstart = SRMMU_PGDIR_ALIGN(vend); - goto finish_up; - } + MKTRACE(("map_spbank %d[v<%08lx>p<%08lx>s<%08lx>]", sp_entry, vbase, sp_banks[sp_entry].base_addr, sp_banks[sp_entry].num_bytes)); + MKTRACE(("map_spbank2 %d[p%08lx v%08lx-%08lx]", sp_entry, pstart, vstart, vend)); while(vstart < vend) { - unsigned long coverage, next_aligned; - if(vstart & ~SRMMU_PMD_MASK) { - next_aligned = SRMMU_PMD_ALIGN(vstart); - if(next_aligned <= vend) { - coverage = (next_aligned - vstart); - do_small_mapping(vstart, next_aligned, pstart); - } else { - coverage = (vend - vstart); - do_small_mapping(vstart, vend, pstart); - } - } else if(vstart & ~SRMMU_PGDIR_MASK) { - next_aligned = SRMMU_PGDIR_ALIGN(vstart); - if(next_aligned <= vend) { - coverage = (next_aligned - vstart); - do_medium_mapping(vstart, next_aligned, pstart); - } else { - coverage = (vend - vstart); - do_small_mapping(vstart, vend, pstart); - } - } else { - coverage = SRMMU_PGDIR_SIZE; - if(large_pte_optimize || ((vstart+coverage)<=vend)) { - do_large_mapping(vstart, pstart); - } else { - coverage = (vend - vstart); - do_small_mapping(vstart, vend, pstart); - } - } - vstart += coverage; pstart += coverage; + do_large_mapping(vstart, pstart); + vstart += SRMMU_PGDIR_SIZE; pstart += SRMMU_PGDIR_SIZE; } -finish_up: srmmu_map[srmmu_bank].vbase = vbase; srmmu_map[srmmu_bank].pbase = sp_banks[sp_entry].base_addr; srmmu_map[srmmu_bank].size = sp_banks[sp_entry].num_bytes; - MKTRACE(("SRMMUBANK[v<%08lx>p<%08lx>s<%08lx>]", vbase, sp_banks[sp_entry].base_addr, sp_banks[sp_entry].num_bytes)); srmmu_bank++; + map_spbank_last_pa = pstart - SRMMU_PGDIR_SIZE; return vstart; } @@ -1721,10 +1669,10 @@ static inline void memprobe_error(char *msg) * is part of a full bank which is at least 4MB in size and begins at * 0xf0000000 (ie. KERNBASE). */ -static void map_kernel(void) +static inline void map_kernel(void) { unsigned long raw_pte, physpage; - unsigned long vaddr, tally, low_base; + unsigned long vaddr, low_base; char etaken[SPARC_PHYS_BANKS]; int entry; @@ -1735,17 +1683,7 @@ static void map_kernel(void) low_base = KERNBASE; - /* Step 2: Calculate 'lots_of_ram'. */ - tally = 0; - for(entry = 0; sp_banks[entry].num_bytes; entry++) - tally += sp_banks[entry].num_bytes; - if(tally > (0xfd000000 - KERNBASE)) - lots_of_ram = 1; - else - lots_of_ram = 0; - MKTRACE(("tally=%08lx lots_of_ram<%d>\n", tally, lots_of_ram)); - - /* Step 3: Fill in KERNBASE base pgd. Lots of sanity checking here. */ + /* Step 2: Fill in KERNBASE base pgd. Lots of sanity checking here. */ raw_pte = srmmu_hwprobe(KERNBASE + PAGE_SIZE); if((raw_pte & SRMMU_ET_MASK) != SRMMU_ET_PTE) memprobe_error("Wheee, kernel not mapped at all by boot loader.\n"); @@ -1757,11 +1695,10 @@ static void map_kernel(void) if(entry == -1 || (sp_banks[entry].base_addr != physpage)) memprobe_error("Kernel mapped in non-existant memory.\n"); MKTRACE(("map_kernel: map_spbank(vbase=%08x, entry<%d>)[%08lx,%08lx]\n", KERNBASE, entry, sp_banks[entry].base_addr, sp_banks[entry].num_bytes)); - if(((KERNBASE + (sp_banks[entry].num_bytes)) > 0xfd000000) || - ((KERNBASE + (sp_banks[entry].num_bytes)) < KERNBASE)) { + if (sp_banks[entry].num_bytes > 0x0d000000) { unsigned long orig_base = sp_banks[entry].base_addr; unsigned long orig_len = sp_banks[entry].num_bytes; - unsigned long can_map = (0xfd000000 - KERNBASE); + unsigned long can_map = 0x0d000000; /* Map a partial bank in this case, adjust the base * and the length, but don't mark it used. @@ -1779,7 +1716,7 @@ static void map_kernel(void) vaddr = map_spbank(KERNBASE, entry); etaken[entry] = 1; - /* Step 4: Map what we can above KERNBASE. */ + /* Step 3: Map what we can above KERNBASE. */ MKTRACE(("map_kernel: vaddr=%08lx, entering first loop\n", vaddr)); for(;;) { unsigned long bank_size; @@ -1790,8 +1727,14 @@ static void map_kernel(void) MKTRACE(("<%d> base=%08lx bs=%08lx ", entry, sp_banks[entry].base_addr, bank_size)); if(!bank_size) break; - if(((vaddr + bank_size) > 0xfd000000) || - ((vaddr + bank_size) < KERNBASE)) { + if (srmmu_low_pa) + vaddr = KERNBASE + sp_banks[entry].base_addr; + else if (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK)) { + if (map_spbank_last_pa == (sp_banks[entry].base_addr & SRMMU_PGDIR_MASK)) + vaddr -= SRMMU_PGDIR_SIZE; + vaddr += (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK)); + } + if ((vaddr + bank_size - KERNBASE) > 0x0d000000) { unsigned long orig_base = sp_banks[entry].base_addr; unsigned long orig_len = sp_banks[entry].num_bytes; unsigned long can_map = (0xfd000000 - vaddr); @@ -1808,8 +1751,6 @@ static void map_kernel(void) MKTRACE(("adjust[%08lx,%08lx]\n", (orig_base + can_map), (orig_len - can_map))); break; } - if(!bank_size) - break; /* Ok, we can map this one, do it. */ MKTRACE(("map_spbank(%08lx,entry<%d>) ", vaddr, entry)); @@ -1823,22 +1764,16 @@ loop_skip: if(!lots_of_ram) goto check_and_return; - /* Step 5: Map the rest (if any) right below KERNBASE. */ + /* Step 4: Map the rest (if any) right below KERNBASE. */ MKTRACE(("map_kernel: doing low mappings... ")); - tally = 0; - for(entry = 0; sp_banks[entry].num_bytes; entry++) { - if(!etaken[entry]) - tally += SRMMU_PGDIR_ALIGN(sp_banks[entry].num_bytes); - } - if(!tally) - memprobe_error("Whee, lots_of_ram yet no low pages to map.\n"); - low_base = (KERNBASE - tally); - MKTRACE(("tally=%08lx low_base=%08lx\n", tally, low_base)); + low_base = (KERNBASE - end_of_phys_memory + 0x0d000000); + MKTRACE(("end_of_phys_memory=%08lx low_base=%08lx\n", end_of_phys_memory, low_base)); /* Ok, now map 'em. */ MKTRACE(("map_kernel: Allocate pt skeleton (%08lx, %08x)\n",low_base,KERNBASE)); srmmu_allocate_ptable_skeleton(low_base, KERNBASE); vaddr = low_base; + map_spbank_last_pa = 0xff000000; MKTRACE(("map_kernel: vaddr=%08lx Entering second loop for low maps.\n", vaddr)); for(;;) { unsigned long bank_size; @@ -1848,19 +1783,22 @@ loop_skip: MKTRACE(("map_kernel: e<%d> base=%08lx bs=%08lx ", entry, sp_banks[entry].base_addr, bank_size)); if(!bank_size) break; + if (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK)) { + if (map_spbank_last_pa == (sp_banks[entry].base_addr & SRMMU_PGDIR_MASK)) + vaddr -= SRMMU_PGDIR_SIZE; + vaddr += (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK)); + } if((vaddr + bank_size) > KERNBASE) memprobe_error("Wheee, kernel low mapping overflow.\n"); MKTRACE(("map_spbank(%08lx, %d) ", vaddr, entry)); vaddr = map_spbank(vaddr, entry); etaken[entry] = 1; - tally -= SRMMU_PGDIR_ALIGN(bank_size); - MKTRACE(("Now, vaddr=%08lx tally=%08lx\n", vaddr, tally)); + MKTRACE(("Now, vaddr=%08lx end_of_phys_memory=%08lx\n", vaddr, end_of_phys_memory)); } MKTRACE(("\n")); - if(tally) - memprobe_error("Wheee, did not map all of low mappings.\n"); + check_and_return: - /* Step 6: Sanity check, make sure we did it all. */ + /* Step 5: Sanity check, make sure we did it all. */ MKTRACE(("check_and_return: ")); for(entry = 0; sp_banks[entry].num_bytes; entry++) { MKTRACE(("e[%d]=%d ", entry, etaken[entry])); @@ -1872,6 +1810,10 @@ check_and_return: MKTRACE(("success\n")); init_task.mm->mmap->vm_start = page_offset = low_base; stack_top = page_offset - PAGE_SIZE; + BTFIXUPSET_SETHI(page_offset, low_base); + BTFIXUPSET_SETHI(stack_top, page_offset - PAGE_SIZE); + BTFIXUPSET_SIMM13(user_ptrs_per_pgd, page_offset / SRMMU_PGDIR_SIZE); + #if 1 for(entry = 0; srmmu_map[entry].size; entry++) { printk("[%d]: v[%08lx,%08lx](%lx) p[%08lx]\n", entry, @@ -1884,90 +1826,73 @@ check_and_return: /* Now setup the p2v/v2p hash tables. */ for(entry = 0; entry < SRMMU_HASHSZ; entry++) - srmmu_v2p_hash[entry] = srmmu_p2v_hash[entry] = NULL; + srmmu_v2p_hash[entry] = ((0xff - entry) << 24); + for(entry = 0; entry < SRMMU_HASHSZ; entry++) + srmmu_p2v_hash[entry] = 0xffffffffUL; for(entry = 0; srmmu_map[entry].size; entry++) { unsigned long addr; for(addr = srmmu_map[entry].vbase; addr < (srmmu_map[entry].vbase + srmmu_map[entry].size); addr += (1 << 24)) - srmmu_v2p_hash[srmmu_ahashfn(addr)] = &srmmu_map[entry]; + srmmu_v2p_hash[srmmu_ahashfn(addr)] = + srmmu_map[entry].pbase - srmmu_map[entry].vbase; for(addr = srmmu_map[entry].pbase; addr < (srmmu_map[entry].pbase + srmmu_map[entry].size); addr += (1 << 24)) - srmmu_p2v_hash[srmmu_ahashfn(addr)] = &srmmu_map[entry]; + srmmu_p2v_hash[srmmu_ahashfn(addr)] = + srmmu_map[entry].pbase - srmmu_map[entry].vbase; } - page_contig_offset = page_offset - (0xfd000000 - KERNBASE); - phys_mem_contig = 1; - for(entry = 0; srmmu_map[entry].size; entry++) - if (srmmu_map[entry].pbase != srmmu_c_v2p (srmmu_map[entry].vbase)) { - phys_mem_contig = 0; - break; - } - if (phys_mem_contig) { - printk ("SRMMU: Physical memory is contiguous, bypassing VA<->PA hashes\n"); - pte_page = srmmu_c_pte_page; - pmd_page = srmmu_c_pmd_page; - pgd_page = srmmu_c_pgd_page; - mk_pte = srmmu_c_mk_pte; - pte_offset = srmmu_c_pte_offset; - pmd_offset = srmmu_c_pmd_offset; - if (ctxd_set == srmmu_ctxd_set) - ctxd_set = srmmu_c_ctxd_set; - pgd_set = srmmu_c_pgd_set; - pmd_set = srmmu_c_pmd_set; - mmu_v2p = srmmu_c_v2p; - mmu_p2v = srmmu_c_p2v; - if (flush_chunk == viking_flush_chunk) - flush_chunk = viking_c_flush_chunk; - } - - if (sparc_cpu_model == sun4d) { - int i, j = -1; - unsigned long bank_start, bank_end; - - sun4d_dma_vbase = 0; - sun4d_dma_size = IOUNIT_DMA_SIZE - IOUNIT_DVMA_SIZE; - for (i = 0; srmmu_map[i].size; i++) { - bank_start = srmmu_map[i].vbase; - bank_end = bank_start + srmmu_map[i].size; - if (bank_start <= KERNBASE && bank_end > KERNBASE) - j = i; - else if (srmmu_map[i].size >= sun4d_dma_size) { - sun4d_dma_vbase = srmmu_map[i].vbase; + BTFIXUPSET_SETHI(page_contig_offset, page_offset - (0xfd000000 - KERNBASE)); + if (srmmu_low_pa) + phys_mem_contig = 0; + else { + phys_mem_contig = 1; + for(entry = 0; srmmu_map[entry].size; entry++) + if (srmmu_map[entry].pbase != srmmu_c_v2p (srmmu_map[entry].vbase)) { + phys_mem_contig = 0; break; } - } - if (!sun4d_dma_vbase && j != -1) { - if (srmmu_map[j].size >= sun4d_dma_size + 0x1000000) - sun4d_dma_vbase = srmmu_map[j].vbase + 0x1000000; - else { - sun4d_dma_vbase = srmmu_map[j].vbase; - if (srmmu_map[j].size < sun4d_dma_size) - sun4d_dma_size = srmmu_map[j].size; - } - } - sun4d_dma_base = IOUNIT_DMA_BASE - srmmu_v2p(sun4d_dma_vbase); } + if (phys_mem_contig) { + printk ("SRMMU: Physical memory is contiguous, bypassing VA<->PA hashes.\n"); + BTFIXUPSET_CALL(pte_page, srmmu_c_pte_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_page, srmmu_c_pmd_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_page, srmmu_c_pgd_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mk_pte, srmmu_c_mk_pte, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_offset, srmmu_c_pte_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_offset, srmmu_c_pmd_offset, BTFIXUPCALL_NORM); + if (BTFIXUPVAL_CALL(ctxd_set) == (unsigned long)srmmu_ctxd_set) + BTFIXUPSET_CALL(ctxd_set, srmmu_c_ctxd_set, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_set, srmmu_c_pgd_set, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_set, srmmu_c_pmd_set, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_v2p, srmmu_c_v2p, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_p2v, srmmu_c_p2v, BTFIXUPCALL_NORM); + if (BTFIXUPVAL_CALL(flush_chunk) == (unsigned long)viking_flush_chunk) + BTFIXUPSET_CALL(flush_chunk, viking_c_flush_chunk, BTFIXUPCALL_NORM); + } else if (srmmu_low_pa) { + printk ("SRMMU: Compact physical memory. Using strightforward VA<->PA translations.\n"); + BTFIXUPSET_CALL(pte_page, srmmu_s_pte_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_page, srmmu_s_pmd_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_page, srmmu_s_pgd_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mk_pte, srmmu_s_mk_pte, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_offset, srmmu_s_pte_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_offset, srmmu_s_pmd_offset, BTFIXUPCALL_NORM); + if (BTFIXUPVAL_CALL(ctxd_set) == (unsigned long)srmmu_ctxd_set) + BTFIXUPSET_CALL(ctxd_set, srmmu_s_ctxd_set, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_set, srmmu_s_pgd_set, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_set, srmmu_s_pmd_set, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_v2p, srmmu_s_v2p, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_p2v, srmmu_s_p2v, BTFIXUPCALL_NORM); + if (BTFIXUPVAL_CALL(flush_chunk) == (unsigned long)viking_flush_chunk) + BTFIXUPSET_CALL(flush_chunk, viking_s_flush_chunk, BTFIXUPCALL_NORM); + } + btfixup(); return; /* SUCCESS! */ } -unsigned long srmmu_endmem_fixup(unsigned long mem_end_now) -{ - unsigned long tally = 0; - int i; - - for(i = 0; sp_banks[i].num_bytes; i++) - tally += SRMMU_PGDIR_ALIGN(sp_banks[i].num_bytes); - if(tally < (0x0d000000UL)) { - return KERNBASE + tally; - } else { - return 0xfd000000UL; - } -} - /* Paging initialization on the Sparc Reference MMU. */ extern unsigned long free_area_init(unsigned long, unsigned long); extern unsigned long sparc_context_init(unsigned long, int); @@ -1975,9 +1900,9 @@ extern unsigned long sparc_context_init(unsigned long, int); extern int physmem_mapped_contig; extern int linux_num_cpus; -void (*poke_srmmu)(void); +void (*poke_srmmu)(void) __initdata = NULL; -unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem) +__initfunc(unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)) { unsigned long ptables_start; int i, cpunode; @@ -2029,7 +1954,7 @@ unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem) start_mem = PAGE_ALIGN(mempool); flush_cache_all(); - if(flush_page_for_dma == viking_flush_page) { + if(BTFIXUPVAL_CALL(flush_page_for_dma) == (unsigned long)viking_flush_page) { unsigned long start = ptables_start; unsigned long end = start_mem; @@ -2048,37 +1973,22 @@ unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem) return PAGE_ALIGN(start_mem); } -static char srmmuinfo[512]; - -static char *srmmu_mmu_info(void) +static int srmmu_mmu_info(char *buf) { - sprintf(srmmuinfo, "MMU type\t: %s\n" + return sprintf(buf, + "MMU type\t: %s\n" "invall\t\t: %d\n" "invmm\t\t: %d\n" "invrnge\t\t: %d\n" "invpg\t\t: %d\n" "contexts\t: %d\n" -#ifdef USE_CHUNK_ALLOC - "big chunks\t: %d\n" - "little chunks\t: %d\n" - "chunk pages\t: %d\n" - "garbage\t\t: %d\n" - "garbage hits\t: %d\n" -#endif , srmmu_name, module_stats.invall, module_stats.invmm, module_stats.invrnge, module_stats.invpg, num_contexts -#ifdef USE_CHUNK_ALLOC - , bcwater, lcwater, - chunk_pages, - garbage_calls, - clct_pages -#endif - ); - return srmmuinfo; + ); } static void srmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) @@ -2242,7 +2152,7 @@ __initfunc(static void init_vac_layout(void)) (int)vac_cache_size, (int)vac_line_size); } -static void poke_hypersparc(void) +__initfunc(static void poke_hypersparc(void)) { volatile unsigned long clear; unsigned long mreg = srmmu_get_mmureg(); @@ -2271,35 +2181,38 @@ __initfunc(static void init_hypersparc(void)) init_vac_layout(); - set_pte = srmmu_set_pte_nocache_hyper; - flush_cache_all = hypersparc_flush_cache_all; - flush_cache_mm = hypersparc_flush_cache_mm; - flush_cache_range = hypersparc_flush_cache_range; - flush_cache_page = hypersparc_flush_cache_page; - - flush_tlb_all = hypersparc_flush_tlb_all; - flush_tlb_mm = hypersparc_flush_tlb_mm; - flush_tlb_range = hypersparc_flush_tlb_range; - flush_tlb_page = hypersparc_flush_tlb_page; - - flush_page_to_ram = hypersparc_flush_page_to_ram; - flush_sig_insns = hypersparc_flush_sig_insns; - flush_page_for_dma = NULL /* hypersparc_flush_page_for_dma */; - - flush_chunk = hypersparc_flush_chunk; /* local flush _only_ */ - - ctxd_set = hypersparc_ctxd_set; - switch_to_context = hypersparc_switch_to_context; - init_new_context = hypersparc_init_new_context; - destroy_context = hypersparc_destroy_context; - update_mmu_cache = srmmu_vac_update_mmu_cache; - sparc_update_rootmmu_dir = hypersparc_update_rootmmu_dir; + BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_hyper, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_all, hypersparc_flush_cache_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_mm, hypersparc_flush_cache_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, hypersparc_flush_cache_range, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, hypersparc_flush_cache_page, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(flush_tlb_all, hypersparc_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, hypersparc_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, hypersparc_flush_tlb_range, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, hypersparc_flush_tlb_page, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(flush_page_to_ram, hypersparc_flush_page_to_ram, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_sig_insns, hypersparc_flush_sig_insns, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_page_for_dma, hypersparc_flush_page_for_dma, BTFIXUPCALL_NOP); + + BTFIXUPSET_CALL(flush_chunk, hypersparc_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */ + + BTFIXUPSET_CALL(ctxd_set, hypersparc_ctxd_set, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(switch_to_context, hypersparc_switch_to_context, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(init_new_context, hypersparc_init_new_context, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(destroy_context, hypersparc_destroy_context, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(sparc_update_rootmmu_dir, hypersparc_update_rootmmu_dir, BTFIXUPCALL_NORM); poke_srmmu = poke_hypersparc; hypersparc_setup_blockops(); } -static void poke_cypress(void) +__initfunc(static void poke_cypress(void)) { unsigned long mreg = srmmu_get_mmureg(); unsigned long faddr, tagval; @@ -2342,25 +2255,28 @@ __initfunc(static void init_cypress_common(void)) { init_vac_layout(); - set_pte = srmmu_set_pte_nocache_cypress; - flush_cache_all = cypress_flush_cache_all; - flush_cache_mm = cypress_flush_cache_mm; - flush_cache_range = cypress_flush_cache_range; - flush_cache_page = cypress_flush_cache_page; + BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_cypress, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_all, cypress_flush_cache_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_mm, cypress_flush_cache_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, cypress_flush_cache_range, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, cypress_flush_cache_page, BTFIXUPCALL_NORM); - flush_tlb_all = cypress_flush_tlb_all; - flush_tlb_mm = cypress_flush_tlb_mm; - flush_tlb_page = cypress_flush_tlb_page; - flush_tlb_range = cypress_flush_tlb_range; + BTFIXUPSET_CALL(flush_tlb_all, cypress_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, cypress_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, cypress_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, cypress_flush_tlb_range, BTFIXUPCALL_NORM); - flush_chunk = cypress_flush_chunk; /* local flush _only_ */ + BTFIXUPSET_CALL(flush_chunk, cypress_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */ - flush_page_to_ram = cypress_flush_page_to_ram; - flush_sig_insns = cypress_flush_sig_insns; - flush_page_for_dma = NULL /* cypress_flush_page_for_dma */; - sparc_update_rootmmu_dir = cypress_update_rootmmu_dir; + BTFIXUPSET_CALL(flush_page_to_ram, cypress_flush_page_to_ram, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_sig_insns, cypress_flush_sig_insns, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_page_for_dma, cypress_flush_page_for_dma, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(sparc_update_rootmmu_dir, cypress_update_rootmmu_dir, BTFIXUPCALL_NORM); - update_mmu_cache = srmmu_vac_update_mmu_cache; + BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM); poke_srmmu = poke_cypress; } @@ -2388,7 +2304,7 @@ __initfunc(static void init_cypress_605(unsigned long mrev)) init_cypress_common(); } -static void poke_swift(void) +__initfunc(static void poke_swift(void)) { unsigned long mreg = srmmu_get_mmureg(); @@ -2456,21 +2372,23 @@ __initfunc(static void init_swift(void)) break; }; - flush_cache_all = swift_flush_cache_all; - flush_cache_mm = swift_flush_cache_mm; - flush_cache_page = swift_flush_cache_page; - flush_cache_range = swift_flush_cache_range; + BTFIXUPSET_CALL(flush_cache_all, swift_flush_cache_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_mm, swift_flush_cache_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, swift_flush_cache_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, swift_flush_cache_range, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */ - flush_chunk = swift_flush_chunk; /* local flush _only_ */ + BTFIXUPSET_CALL(flush_tlb_all, swift_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, swift_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, swift_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, swift_flush_tlb_range, BTFIXUPCALL_NORM); - flush_tlb_all = swift_flush_tlb_all; - flush_tlb_mm = swift_flush_tlb_mm; - flush_tlb_page = swift_flush_tlb_page; - flush_tlb_range = swift_flush_tlb_range; + BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_sig_insns, swift_flush_sig_insns, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_page_for_dma, swift_flush_page_for_dma, BTFIXUPCALL_NORM); - flush_page_to_ram = swift_flush_page_to_ram; - flush_sig_insns = swift_flush_sig_insns; - flush_page_for_dma = swift_flush_page_for_dma; + BTFIXUPSET_CALL(update_mmu_cache, swift_update_mmu_cache, BTFIXUPCALL_NORM); /* Are you now convinced that the Swift is one of the * biggest VLSI abortions of all time? Bravo Fujitsu! @@ -2484,8 +2402,9 @@ __initfunc(static void init_swift(void)) /* turbosparc.S */ extern void turbosparc_flush_cache_all(void); extern void turbosparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr); +extern void turbosparc_flush_page_for_dma(unsigned long page); -static void poke_turbosparc(void) +__initfunc(static void poke_turbosparc(void)) { unsigned long mreg = srmmu_get_mmureg(); unsigned long ccreg; @@ -2529,31 +2448,31 @@ __initfunc(static void init_turbosparc(void)) srmmu_name = "Fujitsu TurboSparc"; srmmu_modtype = TurboSparc; - flush_cache_all = turbosparc_flush_cache_all; - flush_cache_mm = hypersparc_flush_cache_mm; - flush_cache_page = hypersparc_flush_cache_page; - flush_cache_range = hypersparc_flush_cache_range; + BTFIXUPSET_CALL(flush_cache_all, turbosparc_flush_cache_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_mm, hypersparc_flush_cache_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, hypersparc_flush_cache_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, hypersparc_flush_cache_range, BTFIXUPCALL_NORM); - flush_tlb_all = hypersparc_flush_tlb_all; - flush_tlb_mm = hypersparc_flush_tlb_mm; - flush_tlb_page = hypersparc_flush_tlb_page; - flush_tlb_range = hypersparc_flush_tlb_range; + BTFIXUPSET_CALL(flush_tlb_all, hypersparc_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, hypersparc_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, hypersparc_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, hypersparc_flush_tlb_range, BTFIXUPCALL_NORM); #ifdef TURBOSPARC_WRITEBACK - flush_page_to_ram = hypersparc_flush_page_to_ram; - flush_chunk = hypersparc_flush_chunk; + BTFIXUPSET_CALL(flush_page_to_ram, hypersparc_flush_page_to_ram, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_chunk, hypersparc_flush_chunk, BTFIXUPCALL_NORM); #else - flush_page_to_ram = swift_flush_page_to_ram; - flush_chunk = swift_flush_chunk; + BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NOP); #endif - flush_sig_insns = turbosparc_flush_sig_insns; - flush_page_for_dma = NULL /* turbosparc_flush_page_for_dma */; + BTFIXUPSET_CALL(flush_sig_insns, turbosparc_flush_sig_insns, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_page_for_dma, turbosparc_flush_page_for_dma, BTFIXUPCALL_NOP); poke_srmmu = poke_turbosparc; } -static void poke_tsunami(void) +__initfunc(static void poke_tsunami(void)) { unsigned long mreg = srmmu_get_mmureg(); @@ -2574,26 +2493,26 @@ __initfunc(static void init_tsunami(void)) srmmu_name = "TI Tsunami"; srmmu_modtype = Tsunami; - flush_cache_all = tsunami_flush_cache_all; - flush_cache_mm = tsunami_flush_cache_mm; - flush_cache_page = tsunami_flush_cache_page; - flush_cache_range = tsunami_flush_cache_range; + BTFIXUPSET_CALL(flush_cache_all, tsunami_flush_cache_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_mm, tsunami_flush_cache_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, tsunami_flush_cache_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, tsunami_flush_cache_range, BTFIXUPCALL_NORM); - flush_chunk = tsunami_flush_chunk; /* local flush _only_ */ + BTFIXUPSET_CALL(flush_chunk, tsunami_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */ - flush_tlb_all = tsunami_flush_tlb_all; - flush_tlb_mm = tsunami_flush_tlb_mm; - flush_tlb_page = tsunami_flush_tlb_page; - flush_tlb_range = tsunami_flush_tlb_range; + BTFIXUPSET_CALL(flush_tlb_all, tsunami_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, tsunami_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, tsunami_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, tsunami_flush_tlb_range, BTFIXUPCALL_NORM); - flush_page_to_ram = tsunami_flush_page_to_ram; - flush_sig_insns = tsunami_flush_sig_insns; - flush_page_for_dma = tsunami_flush_page_for_dma; + BTFIXUPSET_CALL(flush_page_to_ram, tsunami_flush_page_to_ram, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_sig_insns, tsunami_flush_sig_insns, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_page_for_dma, tsunami_flush_page_for_dma, BTFIXUPCALL_NORM); poke_srmmu = poke_tsunami; } -static void poke_viking(void) +__initfunc(static void poke_viking(void)) { unsigned long mreg = srmmu_get_mmureg(); static int smp_catch = 0; @@ -2637,13 +2556,14 @@ static void poke_viking(void) #ifdef __SMP__ /* Avoid unnecessary cross calls. */ - flush_cache_all = local_flush_cache_all; - flush_cache_mm = local_flush_cache_mm; - flush_cache_range = local_flush_cache_range; - flush_cache_page = local_flush_cache_page; - flush_page_to_ram = local_flush_page_to_ram; - flush_sig_insns = local_flush_sig_insns; - flush_page_for_dma = local_flush_page_for_dma; + BTFIXUPCOPY_CALL(flush_cache_all, local_flush_cache_all); + BTFIXUPCOPY_CALL(flush_cache_mm, local_flush_cache_mm); + BTFIXUPCOPY_CALL(flush_cache_range, local_flush_cache_range); + BTFIXUPCOPY_CALL(flush_cache_page, local_flush_cache_page); + BTFIXUPCOPY_CALL(flush_page_to_ram, local_flush_page_to_ram); + BTFIXUPCOPY_CALL(flush_sig_insns, local_flush_sig_insns); + BTFIXUPCOPY_CALL(flush_page_for_dma, local_flush_page_for_dma); + btfixup(); #endif } @@ -2664,10 +2584,13 @@ __initfunc(static void init_viking(void)) msi_set_sync(); - set_pte = srmmu_set_pte_nocache_viking; - sparc_update_rootmmu_dir = viking_update_rootmmu_dir; + BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_viking, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(sparc_update_rootmmu_dir, viking_update_rootmmu_dir, BTFIXUPCALL_NORM); - flush_chunk = viking_flush_chunk; /* local flush _only_ */ + BTFIXUPSET_CALL(flush_chunk, viking_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */ /* We need this to make sure old viking takes no hits * on it's cache for dma snoops to workaround the @@ -2675,7 +2598,7 @@ __initfunc(static void init_viking(void)) * This is only necessary because of the new way in * which we use the IOMMU. */ - flush_page_for_dma = viking_flush_page; + BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page, BTFIXUPCALL_NORM); /* Also, this is so far the only chip which actually uses the page argument to flush_page_for_dma */ flush_page_for_dma_global = 0; @@ -2683,24 +2606,25 @@ __initfunc(static void init_viking(void)) srmmu_name = "TI Viking/MXCC"; viking_mxcc_present = 1; - flush_chunk = viking_mxcc_flush_chunk; /* local flush _only_ */ + BTFIXUPSET_CALL(flush_chunk, viking_mxcc_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */ /* MXCC vikings lack the DMA snooping bug. */ - flush_page_for_dma = NULL /* viking_flush_page_for_dma */; + BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page_for_dma, BTFIXUPCALL_NOP); } - flush_cache_all = viking_flush_cache_all; - flush_cache_mm = viking_flush_cache_mm; - flush_cache_page = viking_flush_cache_page; - flush_cache_range = viking_flush_cache_range; + /* flush_cache_* are nops */ + BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NOP); - flush_tlb_all = viking_flush_tlb_all; - flush_tlb_mm = viking_flush_tlb_mm; - flush_tlb_page = viking_flush_tlb_page; - flush_tlb_range = viking_flush_tlb_range; + BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM); - flush_page_to_ram = viking_flush_page_to_ram; - flush_sig_insns = viking_flush_sig_insns; + BTFIXUPSET_CALL(flush_page_to_ram, viking_flush_page_to_ram, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_sig_insns, viking_flush_sig_insns, BTFIXUPCALL_NOP); poke_srmmu = poke_viking; } @@ -2798,6 +2722,67 @@ __initfunc(static void get_srmmu_type(void)) srmmu_is_bad(); } +/* Low and high watermarks for page table cache. + The system should try to have pgt_water[0] <= cache elements <= pgt_water[1] + */ +extern int pgt_cache_water[2]; + +void srmmu_check_pgt_cache(void) +{ + struct page *page, *page2; + + if (pgtable_cache_size > pgt_cache_water[0]) { + spin_lock(&pte_spinlock); + for (page2 = NULL, page = (struct page *)pte_quicklist; page;) { + if ((unsigned int)page->pprev_hash == 0xffff) { + if (page2) + page2->next_hash = page->next_hash; + else + (struct page *)pte_quicklist = page->next_hash; + page->next_hash = NULL; + page->pprev_hash = NULL; + pgtable_cache_size -= 16; + free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT)); + if (page2) + page = page2->next_hash; + else + page = (struct page *)pte_quicklist; + if (pgtable_cache_size <= pgt_cache_water[1]) + break; + continue; + } + page2 = page; + page = page->next_hash; + } + spin_unlock(&pte_spinlock); + } + if (pgd_cache_size > pgt_cache_water[0] / 4) { + spin_lock(&pgd_spinlock); + for (page2 = NULL, page = (struct page *)pgd_quicklist; page;) { + if ((unsigned int)page->pprev_hash == 0xf) { + if (page2) + page2->next_hash = page->next_hash; + else + (struct page *)pgd_quicklist = page->next_hash; + page->next_hash = NULL; + page->pprev_hash = NULL; + pgd_cache_size -= 4; + free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT)); + if (page2) + page = page2->next_hash; + else + page = (struct page *)pgd_quicklist; + if (pgd_cache_size <= pgt_cache_water[1] / 4) + break; + continue; + } + page2 = page; + page = page->next_hash; + } + spin_unlock(&pgd_spinlock); + } +} + extern unsigned long spwin_mmu_patchme, fwin_mmu_patchme, tsetup_mmu_patchme, rtrap_mmu_patchme; @@ -2810,7 +2795,7 @@ extern unsigned long srmmu_fault; iaddr = &(insn); \ daddr = &(dest); \ *iaddr = SPARC_BRANCH((unsigned long) daddr, (unsigned long) iaddr); \ - } while(0); + } while(0); __initfunc(static void patch_window_trap_handlers(void)) { @@ -2829,7 +2814,7 @@ __initfunc(static void patch_window_trap_handlers(void)) /* Local cross-calls. */ static void smp_flush_page_for_dma(unsigned long page) { - xc1((smpfunc_t) local_flush_page_for_dma, page); + xc1((smpfunc_t) BTFIXUP_CALL(local_flush_page_for_dma), page); } #endif @@ -2839,98 +2824,107 @@ __initfunc(void ld_mmu_srmmu(void)) { extern void ld_mmu_iommu(void); extern void ld_mmu_iounit(void); + extern void ___xchg32_sun4md(void); /* First the constants */ - pmd_shift = SRMMU_PMD_SHIFT; - pmd_size = SRMMU_PMD_SIZE; - pmd_mask = SRMMU_PMD_MASK; - pgdir_shift = SRMMU_PGDIR_SHIFT; - pgdir_size = SRMMU_PGDIR_SIZE; - pgdir_mask = SRMMU_PGDIR_MASK; - - ptrs_per_pte = SRMMU_PTRS_PER_PTE; - ptrs_per_pmd = SRMMU_PTRS_PER_PMD; - ptrs_per_pgd = SRMMU_PTRS_PER_PGD; - - page_none = SRMMU_PAGE_NONE; - page_shared = SRMMU_PAGE_SHARED; - page_copy = SRMMU_PAGE_COPY; - page_readonly = SRMMU_PAGE_RDONLY; - page_kernel = SRMMU_PAGE_KERNEL; + BTFIXUPSET_SIMM13(pmd_shift, SRMMU_PMD_SHIFT); + BTFIXUPSET_SETHI(pmd_size, SRMMU_PMD_SIZE); + BTFIXUPSET_SETHI(pmd_mask, SRMMU_PMD_MASK); + BTFIXUPSET_SIMM13(pgdir_shift, SRMMU_PGDIR_SHIFT); + BTFIXUPSET_SETHI(pgdir_size, SRMMU_PGDIR_SIZE); + BTFIXUPSET_SETHI(pgdir_mask, SRMMU_PGDIR_MASK); + + BTFIXUPSET_SIMM13(ptrs_per_pte, SRMMU_PTRS_PER_PTE); + BTFIXUPSET_SIMM13(ptrs_per_pmd, SRMMU_PTRS_PER_PMD); + BTFIXUPSET_SIMM13(ptrs_per_pgd, SRMMU_PTRS_PER_PGD); + + BTFIXUPSET_INT(page_none, pgprot_val(SRMMU_PAGE_NONE)); + BTFIXUPSET_INT(page_shared, pgprot_val(SRMMU_PAGE_SHARED)); + BTFIXUPSET_INT(page_copy, pgprot_val(SRMMU_PAGE_COPY)); + BTFIXUPSET_INT(page_readonly, pgprot_val(SRMMU_PAGE_RDONLY)); + BTFIXUPSET_INT(page_kernel, pgprot_val(SRMMU_PAGE_KERNEL)); pg_iobits = SRMMU_VALID | SRMMU_WRITE | SRMMU_REF; - + /* Functions */ - set_pte = srmmu_set_pte_cacheable; - init_new_context = srmmu_init_new_context; - switch_to_context = srmmu_switch_to_context; - pmd_align = srmmu_pmd_align; - pgdir_align = srmmu_pgdir_align; - vmalloc_start = srmmu_vmalloc_start; - - pte_page = srmmu_pte_page; - pmd_page = srmmu_pmd_page; - pgd_page = srmmu_pgd_page; - - sparc_update_rootmmu_dir = srmmu_update_rootmmu_dir; - - pte_none = srmmu_pte_none; - pte_present = srmmu_pte_present; - pte_clear = srmmu_pte_clear; - - pmd_none = srmmu_pmd_none; - pmd_bad = srmmu_pmd_bad; - pmd_present = srmmu_pmd_present; - pmd_clear = srmmu_pmd_clear; - - pgd_none = srmmu_pgd_none; - pgd_bad = srmmu_pgd_bad; - pgd_present = srmmu_pgd_present; - pgd_clear = srmmu_pgd_clear; - - mk_pte = srmmu_mk_pte; - mk_pte_phys = srmmu_mk_pte_phys; - pgd_set = srmmu_pgd_set; - mk_pte_io = srmmu_mk_pte_io; - pte_modify = srmmu_pte_modify; - pgd_offset = srmmu_pgd_offset; - pmd_offset = srmmu_pmd_offset; - pte_offset = srmmu_pte_offset; - pte_free_kernel = srmmu_pte_free_kernel; - pmd_free_kernel = srmmu_pmd_free_kernel; - pte_alloc_kernel = srmmu_pte_alloc_kernel; - pmd_alloc_kernel = srmmu_pmd_alloc_kernel; - pte_free = srmmu_pte_free; - pte_alloc = srmmu_pte_alloc; - pmd_free = srmmu_pmd_free; - pmd_alloc = srmmu_pmd_alloc; - pgd_free = srmmu_pgd_free; - pgd_alloc = srmmu_pgd_alloc; - - pte_write = srmmu_pte_write; - pte_dirty = srmmu_pte_dirty; - pte_young = srmmu_pte_young; - pte_wrprotect = srmmu_pte_wrprotect; - pte_mkclean = srmmu_pte_mkclean; - pte_mkold = srmmu_pte_mkold; - pte_mkwrite = srmmu_pte_mkwrite; - pte_mkdirty = srmmu_pte_mkdirty; - pte_mkyoung = srmmu_pte_mkyoung; - update_mmu_cache = srmmu_update_mmu_cache; - destroy_context = srmmu_destroy_context; +#ifndef __SMP__ + BTFIXUPSET_CALL(___xchg32, ___xchg32_sun4md, BTFIXUPCALL_SWAPG1G2); +#endif + BTFIXUPSET_CALL(get_pte_fast, srmmu_get_pte_fast, BTFIXUPCALL_RETINT(0)); + BTFIXUPSET_CALL(get_pgd_fast, srmmu_get_pgd_fast, BTFIXUPCALL_RETINT(0)); + BTFIXUPSET_CALL(free_pte_slow, srmmu_free_pte_slow, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(free_pgd_slow, srmmu_free_pgd_slow, BTFIXUPCALL_NOP); + + BTFIXUPSET_CALL(set_pgdir, srmmu_set_pgdir, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(set_pte, srmmu_set_pte_cacheable, BTFIXUPCALL_SWAPO0O1); + BTFIXUPSET_CALL(init_new_context, srmmu_init_new_context, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(switch_to_context, srmmu_switch_to_context, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(pte_page, srmmu_pte_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_page, srmmu_pmd_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_page, srmmu_pgd_page, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(sparc_update_rootmmu_dir, srmmu_update_rootmmu_dir, BTFIXUPCALL_NORM); + + BTFIXUPSET_SETHI(none_mask, 0xF0000000); + + BTFIXUPSET_CALL(pte_present, srmmu_pte_present, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_SWAPO0G0); + + BTFIXUPSET_CALL(pmd_bad, srmmu_pmd_bad, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_present, srmmu_pmd_present, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_SWAPO0G0); + + BTFIXUPSET_CALL(pgd_none, srmmu_pgd_none, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_bad, srmmu_pgd_bad, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_present, srmmu_pgd_present, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_SWAPO0G0); + + BTFIXUPSET_CALL(mk_pte, srmmu_mk_pte, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mk_pte_phys, srmmu_mk_pte_phys, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mk_pte_io, srmmu_mk_pte_io, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_set, srmmu_pgd_set, BTFIXUPCALL_NORM); - mmu_info = srmmu_mmu_info; - mmu_v2p = srmmu_v2p; - mmu_p2v = srmmu_p2v; + BTFIXUPSET_INT(pte_modify_mask, SRMMU_CHG_MASK); + BTFIXUPSET_CALL(pgd_offset, srmmu_pgd_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_offset, srmmu_pmd_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_offset, srmmu_pte_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_free_kernel, srmmu_pte_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_free_kernel, srmmu_pmd_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_alloc_kernel, srmmu_pte_alloc, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_alloc_kernel, srmmu_pmd_alloc, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_free, srmmu_pte_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_alloc, srmmu_pte_alloc, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_free, srmmu_pmd_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_alloc, srmmu_pmd_alloc, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_free, srmmu_pgd_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_alloc, srmmu_pgd_alloc, BTFIXUPCALL_NORM); + + BTFIXUPSET_HALF(pte_writei, SRMMU_WRITE); + BTFIXUPSET_HALF(pte_dirtyi, SRMMU_DIRTY); + BTFIXUPSET_HALF(pte_youngi, SRMMU_REF); + BTFIXUPSET_HALF(pte_wrprotecti, SRMMU_WRITE); + BTFIXUPSET_HALF(pte_mkcleani, SRMMU_DIRTY); + BTFIXUPSET_HALF(pte_mkoldi, SRMMU_REF); + BTFIXUPSET_CALL(pte_mkwrite, srmmu_pte_mkwrite, BTFIXUPCALL_ORINT(SRMMU_WRITE)); + BTFIXUPSET_CALL(pte_mkdirty, srmmu_pte_mkdirty, BTFIXUPCALL_ORINT(SRMMU_DIRTY)); + BTFIXUPSET_CALL(pte_mkyoung, srmmu_pte_mkyoung, BTFIXUPCALL_ORINT(SRMMU_REF)); + BTFIXUPSET_CALL(update_mmu_cache, srmmu_update_mmu_cache, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(destroy_context, srmmu_destroy_context, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(mmu_info, srmmu_mmu_info, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_v2p, srmmu_v2p, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_p2v, srmmu_p2v, BTFIXUPCALL_NORM); /* Task struct and kernel stack allocating/freeing. */ - alloc_task_struct = srmmu_alloc_task_struct; - free_task_struct = srmmu_free_task_struct; + BTFIXUPSET_CALL(alloc_task_struct, srmmu_alloc_task_struct, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(free_task_struct, srmmu_free_task_struct, BTFIXUPCALL_NORM); - quick_kernel_fault = srmmu_quick_kernel_fault; + BTFIXUPSET_CALL(quick_kernel_fault, srmmu_quick_kernel_fault, BTFIXUPCALL_NORM); /* SRMMU specific. */ - ctxd_set = srmmu_ctxd_set; - pmd_set = srmmu_pmd_set; + BTFIXUPSET_CALL(ctxd_set, srmmu_ctxd_set, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_set, srmmu_pmd_set, BTFIXUPCALL_NORM); get_srmmu_type(); patch_window_trap_handlers(); @@ -2938,33 +2932,38 @@ __initfunc(void ld_mmu_srmmu(void)) #ifdef __SMP__ /* El switcheroo... */ - local_flush_cache_all = flush_cache_all; - local_flush_cache_mm = flush_cache_mm; - local_flush_cache_range = flush_cache_range; - local_flush_cache_page = flush_cache_page; - local_flush_tlb_all = flush_tlb_all; - local_flush_tlb_mm = flush_tlb_mm; - local_flush_tlb_range = flush_tlb_range; - local_flush_tlb_page = flush_tlb_page; - local_flush_page_to_ram = flush_page_to_ram; - local_flush_sig_insns = flush_sig_insns; - local_flush_page_for_dma = flush_page_for_dma; - - flush_cache_all = smp_flush_cache_all; - flush_cache_mm = smp_flush_cache_mm; - flush_cache_range = smp_flush_cache_range; - flush_cache_page = smp_flush_cache_page; - flush_tlb_all = smp_flush_tlb_all; - flush_tlb_mm = smp_flush_tlb_mm; - flush_tlb_range = smp_flush_tlb_range; - flush_tlb_page = smp_flush_tlb_page; - flush_page_to_ram = smp_flush_page_to_ram; - flush_sig_insns = smp_flush_sig_insns; - if (flush_page_for_dma) - flush_page_for_dma = smp_flush_page_for_dma; + BTFIXUPCOPY_CALL(local_flush_cache_all, flush_cache_all); + BTFIXUPCOPY_CALL(local_flush_cache_mm, flush_cache_mm); + BTFIXUPCOPY_CALL(local_flush_cache_range, flush_cache_range); + BTFIXUPCOPY_CALL(local_flush_cache_page, flush_cache_page); + BTFIXUPCOPY_CALL(local_flush_tlb_all, flush_tlb_all); + BTFIXUPCOPY_CALL(local_flush_tlb_mm, flush_tlb_mm); + BTFIXUPCOPY_CALL(local_flush_tlb_range, flush_tlb_range); + BTFIXUPCOPY_CALL(local_flush_tlb_page, flush_tlb_page); + BTFIXUPCOPY_CALL(local_flush_page_to_ram, flush_page_to_ram); + BTFIXUPCOPY_CALL(local_flush_sig_insns, flush_sig_insns); + BTFIXUPCOPY_CALL(local_flush_page_for_dma, flush_page_for_dma); + + BTFIXUPSET_CALL(flush_cache_all, smp_flush_cache_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_mm, smp_flush_cache_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, smp_flush_cache_range, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, smp_flush_cache_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_page_to_ram, smp_flush_page_to_ram, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_sig_insns, smp_flush_sig_insns, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_page_for_dma, smp_flush_page_for_dma, BTFIXUPCALL_NORM); #endif if (sparc_cpu_model == sun4d) ld_mmu_iounit(); else ld_mmu_iommu(); +#ifdef __SMP__ + if (sparc_cpu_model == sun4d) + sun4d_init_smp(); + else + sun4m_init_smp(); +#endif } diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index c70753fa4..d247e1f2d 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -1,11 +1,14 @@ -/* $Id: sun4c.c,v 1.149 1997/07/20 05:59:38 davem Exp $ +/* $Id: sun4c.c,v 1.163 1998/03/11 04:08:21 tdyas Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1996 Andrew Tridgell (Andrew.Tridgell@anu.edu.au) + * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au) + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/init.h> @@ -22,6 +25,7 @@ #include <asm/oplib.h> #include <asm/openprom.h> #include <asm/mmu_context.h> +#include <asm/sun4paddr.h> /* TODO: Make it such that interrupt handlers cannot dick with * the user segment lists, most of the cli/sti pairs can @@ -59,11 +63,15 @@ extern int num_segmaps, num_contexts; : "g4", "cc"); \ } while(0); -/* That's it, we prom_halt() if the cache size is something other than 65536. +#ifdef CONFIG_SUN4 +#define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes +#else +/* That's it, we prom_halt() on sun4c if the cache size is something other than 65536. * So let's save some cycles and just use that everywhere except for that bootup * sanity check. */ -#define SUN4C_VAC_SIZE 65536 +#define SUN4C_VAC_SIZE 65536 +#endif #define SUN4C_KERNEL_BUCKETS 32 @@ -427,22 +435,76 @@ static inline void sun4c_init_clean_mmu(unsigned long kernel_end) sun4c_set_context(savectx); } -void sun4c_probe_vac(void) +__initfunc(void sun4c_probe_vac(void)) { sun4c_disable_vac(); - if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* PROM on SS1 lacks this info, to be super safe we - * hard code it here since this arch is cast in stone. - */ - sun4c_vacinfo.num_bytes = 65536; - sun4c_vacinfo.linesize = 16; + + if (ARCH_SUN4) { + switch(idprom->id_machtype) { + + case (SM_SUN4|SM_4_110): + sun4c_vacinfo.type = NONE; + sun4c_vacinfo.num_bytes = 0; + sun4c_vacinfo.linesize = 0; + sun4c_vacinfo.do_hwflushes = 0; + prom_printf("No VAC. Get some bucks and buy a real computer."); + prom_halt(); + break; + + case (SM_SUN4|SM_4_260): + sun4c_vacinfo.type = WRITE_BACK; + sun4c_vacinfo.num_bytes = 128 * 1024; + sun4c_vacinfo.linesize = 16; + sun4c_vacinfo.do_hwflushes = 0; + break; + + case (SM_SUN4|SM_4_330): + sun4c_vacinfo.type = WRITE_THROUGH; + sun4c_vacinfo.num_bytes = 128 * 1024; + sun4c_vacinfo.linesize = 16; + sun4c_vacinfo.do_hwflushes = 0; + break; + + case (SM_SUN4|SM_4_470): + sun4c_vacinfo.type = WRITE_BACK; + sun4c_vacinfo.num_bytes = 128 * 1024; + sun4c_vacinfo.linesize = 32; + sun4c_vacinfo.do_hwflushes = 0; + break; + + default: + prom_printf("Cannot initialize VAC - wierd sun4 model idprom->id_machtype = %d", idprom->id_machtype); + prom_halt(); + } } else { - sun4c_vacinfo.num_bytes = prom_getintdefault(prom_root_node, - "vac-size", 65536); - sun4c_vacinfo.linesize = prom_getintdefault(prom_root_node, - "vac-linesize", 16); + sun4c_vacinfo.type = WRITE_THROUGH; + + if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { + /* PROM on SS1 lacks this info, to be super safe we + * hard code it here since this arch is cast in stone. + */ + sun4c_vacinfo.num_bytes = 65536; + sun4c_vacinfo.linesize = 16; + } else { + sun4c_vacinfo.num_bytes = + prom_getintdefault(prom_root_node, "vac-size", 65536); + sun4c_vacinfo.linesize = + prom_getintdefault(prom_root_node, "vac-linesize", 16); + } + sun4c_vacinfo.do_hwflushes = + prom_getintdefault(prom_root_node, "vac-hwflush", 0); + + if(sun4c_vacinfo.do_hwflushes == 0) + sun4c_vacinfo.do_hwflushes = + prom_getintdefault(prom_root_node, "vac_hwflush", 0); + + if (sun4c_vacinfo.num_bytes != 65536) { + prom_printf("WEIRD Sun4C VAC cache size, tell davem"); + prom_halt(); + } } + sun4c_vacinfo.num_lines = (sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize); switch(sun4c_vacinfo.linesize) { @@ -458,17 +520,6 @@ void sun4c_probe_vac(void) prom_halt(); }; - sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node, - "vac-hwflush", 0); - if(sun4c_vacinfo.do_hwflushes == 0) - sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node, - "vac_hwflush", 0); - - if(sun4c_vacinfo.num_bytes != 65536) { - prom_printf("WEIRD Sun4C VAC cache size, tell davem"); - prom_halt(); - } - sun4c_flush_all(); sun4c_enable_vac(); } @@ -476,6 +527,7 @@ void sun4c_probe_vac(void) /* Patch instructions for the low level kernel fault handler. */ extern unsigned long invalid_segment_patch1, invalid_segment_patch1_ff; extern unsigned long invalid_segment_patch2, invalid_segment_patch2_ff; +extern unsigned long invalid_segment_patch1_1ff, invalid_segment_patch2_1ff; extern unsigned long num_context_patch1, num_context_patch1_16; extern unsigned long num_context_patch2, num_context_patch2_16; extern unsigned long vac_linesize_patch, vac_linesize_patch_32; @@ -502,6 +554,12 @@ static void patch_kernel_fault_handler(void) PATCH_INSN(invalid_segment_patch2_ff, invalid_segment_patch2); break; + case 512: + PATCH_INSN(invalid_segment_patch1_1ff, + invalid_segment_patch1); + PATCH_INSN(invalid_segment_patch2_1ff, + invalid_segment_patch2); + break; default: prom_printf("Unhandled number of segmaps: %d\n", num_segmaps); @@ -541,38 +599,80 @@ static void patch_kernel_fault_handler(void) } } -static void sun4c_probe_mmu(void) +__initfunc(static void sun4c_probe_mmu(void)) { - if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* Hardcode these just to be safe, PROM on SS1 does - * not have this info available in the root node. - */ - num_segmaps = 128; - num_contexts = 8; + if (ARCH_SUN4) { + switch(idprom->id_machtype) { + case (SM_SUN4|SM_4_110): + prom_printf("No support for 4100 yet\n"); + prom_halt(); + num_segmaps = 256; + num_contexts = 8; + break; + + case (SM_SUN4|SM_4_260): + prom_printf("No support for 4200 yet\n"); + prom_halt(); + num_segmaps = 512; + num_contexts = 16; + break; + + case (SM_SUN4|SM_4_330): + num_segmaps = 256; + num_contexts = 16; + break; + + case (SM_SUN4|SM_4_470): + prom_printf("No support for 4400 yet\n"); + prom_halt(); + num_segmaps = 1024; + num_contexts = 64; + break; + default: + prom_printf("Invalid SUN4 model\n"); + prom_halt(); + } } else { - num_segmaps = prom_getintdefault(prom_root_node, "mmu-npmg", 128); - num_contexts = prom_getintdefault(prom_root_node, "mmu-nctx", 0x8); + if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { + /* Hardcode these just to be safe, PROM on SS1 does + * not have this info available in the root node. + */ + num_segmaps = 128; + num_contexts = 8; + } else { + num_segmaps = + prom_getintdefault(prom_root_node, "mmu-npmg", 128); + num_contexts = + prom_getintdefault(prom_root_node, "mmu-nctx", 0x8); + } } patch_kernel_fault_handler(); } volatile unsigned long *sun4c_memerr_reg = 0; -void sun4c_probe_memerr_reg(void) +__initfunc(void sun4c_probe_memerr_reg(void)) { int node; struct linux_prom_registers regs[1]; - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(prom_root_node, "memory-error"); - if (!node) - return; - prom_getproperty(node, "reg", (char *)regs, sizeof(regs)); - sun4c_memerr_reg = sparc_alloc_io(regs[0].phys_addr, 0, - regs[0].reg_size, - "memory parity error", - regs[0].which_io, 0); + if (ARCH_SUN4) { + sun4c_memerr_reg = sparc_alloc_io(SUN4_MEMREG_PHYSADDR, 0, + PAGE_SIZE, + "memory parity error", + 0x0, 0); + } else { + node = prom_getchild(prom_root_node); + node = prom_searchsiblings(prom_root_node, "memory-error"); + if (!node) + return; + prom_getproperty(node, "reg", (char *)regs, sizeof(regs)); + sun4c_memerr_reg = sparc_alloc_io(regs[0].phys_addr, 0, + regs[0].reg_size, + "memory parity error", + regs[0].which_io, 0); + } } static inline void sun4c_init_ss2_cache_bug(void) @@ -581,6 +681,7 @@ static inline void sun4c_init_ss2_cache_bug(void) if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) || (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) || + (idprom->id_machtype == (SM_SUN4 | SM_4_330)) || (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) { /* Whee.. */ printk("SS2 cache bug detected, uncaching trap table page\n"); @@ -626,13 +727,14 @@ struct sun4c_mmu_entry { unsigned char pseg; unsigned char locked; }; -static struct sun4c_mmu_entry mmu_entry_pool[256]; + +static struct sun4c_mmu_entry mmu_entry_pool[SUN4C_MAX_SEGMAPS]; __initfunc(static void sun4c_init_mmu_entry_pool(void)) { int i; - for(i=0; i < 256; i++) { + for(i=0; i < SUN4C_MAX_SEGMAPS; i++) { mmu_entry_pool[i].pseg = i; mmu_entry_pool[i].next = 0; mmu_entry_pool[i].prev = 0; @@ -703,7 +805,8 @@ struct sun4c_mmu_ring { struct sun4c_mmu_entry ringhd; int num_entries; }; -static struct sun4c_mmu_ring sun4c_context_ring[16]; /* used user entries */ + +static struct sun4c_mmu_ring sun4c_context_ring[SUN4C_MAX_CONTEXTS]; /* used user entries */ static struct sun4c_mmu_ring sun4c_ufree_ring; /* free user entries */ struct sun4c_mmu_ring sun4c_kernel_ring; /* used kernel entries */ struct sun4c_mmu_ring sun4c_kfree_ring; /* free kernel entries */ @@ -711,7 +814,7 @@ struct sun4c_mmu_ring sun4c_kfree_ring; /* free kernel entries */ static inline void sun4c_init_rings(unsigned long *mempool) { int i; - for(i=0; i<16; i++) { + for(i=0; i<SUN4C_MAX_CONTEXTS; i++) { sun4c_context_ring[i].ringhd.next = sun4c_context_ring[i].ringhd.prev = &sun4c_context_ring[i].ringhd; @@ -1120,7 +1223,7 @@ static int sun4c_lowbucket_avail; #define BUCKET_PTE(page) \ ((((page) - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(SUN4C_PAGE_KERNEL)) #define BUCKET_PTE_PAGE(pte) \ - (PAGE_OFFSET + (((pte) & 0xffff) << PAGE_SHIFT)) + (PAGE_OFFSET + (((pte) & SUN4C_PFN_MASK) << PAGE_SHIFT)) static inline void get_locked_segment(unsigned long addr) { @@ -1180,12 +1283,18 @@ static inline void garbage_collect(int entry) free_locked_segment(BUCKET_ADDR(entry)); } +#ifdef CONFIG_SUN4 +#define TASK_STRUCT_ORDER 0 +#else +#define TASK_STRUCT_ORDER 1 +#endif + static struct task_struct *sun4c_alloc_task_struct(void) { unsigned long addr, pages; int entry; - pages = __get_free_pages(GFP_KERNEL, 1); + pages = __get_free_pages(GFP_KERNEL, TASK_STRUCT_ORDER); if(!pages) return (struct task_struct *) 0; @@ -1193,7 +1302,7 @@ static struct task_struct *sun4c_alloc_task_struct(void) if(sun4c_bucket[entry] == BUCKET_EMPTY) break; if(entry == NR_TASKS) { - free_pages(pages, 1); + free_pages(pages, TASK_STRUCT_ORDER); return (struct task_struct *) 0; } if(entry >= sun4c_lowbucket_avail) @@ -1204,8 +1313,9 @@ static struct task_struct *sun4c_alloc_task_struct(void) if(sun4c_get_segmap(addr) == invalid_segment) get_locked_segment(addr); sun4c_put_pte(addr, BUCKET_PTE(pages)); +#ifndef CONFIG_SUN4 sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE)); - +#endif return (struct task_struct *) addr; } @@ -1217,15 +1327,18 @@ static void sun4c_free_task_struct_hw(struct task_struct *tsk) /* We are deleting a mapping, so the flush here is mandatory. */ sun4c_flush_page_hw(tsaddr); +#ifndef CONFIG_SUN4 sun4c_flush_page_hw(tsaddr + PAGE_SIZE); - +#endif sun4c_put_pte(tsaddr, 0); +#ifndef CONFIG_SUN4 sun4c_put_pte(tsaddr + PAGE_SIZE, 0); +#endif sun4c_bucket[entry] = BUCKET_EMPTY; if(entry < sun4c_lowbucket_avail) sun4c_lowbucket_avail = entry; - free_pages(pages, 1); + free_pages(pages, TASK_STRUCT_ORDER); garbage_collect(entry); } @@ -1237,15 +1350,18 @@ static void sun4c_free_task_struct_sw(struct task_struct *tsk) /* We are deleting a mapping, so the flush here is mandatory. */ sun4c_flush_page_sw(tsaddr); +#ifndef CONFIG_SUN4 sun4c_flush_page_sw(tsaddr + PAGE_SIZE); - +#endif sun4c_put_pte(tsaddr, 0); +#ifndef CONFIG_SUN4 sun4c_put_pte(tsaddr + PAGE_SIZE, 0); +#endif sun4c_bucket[entry] = BUCKET_EMPTY; if(entry < sun4c_lowbucket_avail) sun4c_lowbucket_avail = entry; - free_pages(pages, 1); + free_pages(pages, TASK_STRUCT_ORDER); garbage_collect(entry); } @@ -1253,9 +1369,8 @@ __initfunc(static void sun4c_init_buckets(void)) { int entry; - if(sizeof(union task_union) != (PAGE_SIZE << 1)) { - prom_printf("task union not 2 pages!\n"); - prom_halt(); + if(sizeof(union task_union) != (PAGE_SIZE << TASK_STRUCT_ORDER)) { + prom_printf("task union not %d page(s)!\n", 1 << TASK_STRUCT_ORDER); } for(entry = 0; entry < NR_TASKS; entry++) sun4c_bucket[entry] = BUCKET_EMPTY; @@ -1949,12 +2064,17 @@ static void sun4c_set_pte(pte_t *ptep, pte_t pte) *ptep = pte; } +static void sun4c_pgd_set(pgd_t * pgdp, pmd_t * pmdp) +{ +} + + void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly) { unsigned long page_entry; - page_entry = ((physaddr >> PAGE_SHIFT) & 0xffff); + page_entry = ((physaddr >> PAGE_SHIFT) & SUN4C_PFN_MASK); page_entry |= ((pg_iobits | _SUN4C_PAGE_PRIV) & ~(_SUN4C_PAGE_PRESENT)); if(rdonly) page_entry &= ~_SUN4C_WRITEABLE; @@ -2092,21 +2212,17 @@ static void sun4c_destroy_context_sw(struct mm_struct *mm) } } -#if KGPROF_PROFILING -static char s4cinfo[10240]; -#else -static char s4cinfo[512]; -#endif - -static char *sun4c_mmu_info(void) +static int sun4c_mmu_info(char *buf) { int used_user_entries, i; + int len; used_user_entries = 0; for(i=0; i < num_contexts; i++) used_user_entries += sun4c_context_ring[i].num_entries; - sprintf(s4cinfo, "vacsize\t\t: %d bytes\n" + len = sprintf(buf, + "vacsize\t\t: %d bytes\n" "vachwflush\t: %s\n" "vaclinesize\t: %d bytes\n" "mmuctxs\t\t: %d\n" @@ -2135,29 +2251,31 @@ static char *sun4c_mmu_info(void) #if KGPROF_PROFILING { - char *p = s4cinfo + strlen(s4cinfo); int i,j; - sprintf(p,"kgprof profiling:\n"); p += strlen(p); + len += sprintf(buf + len,"kgprof profiling:\n"); for (i=0;i<KGPROF_SIZE && kgprof_counters[i].addr[0];i++) { - sprintf(p,"%5d ",kgprof_counters[i].count); p += strlen(p); + len += sprintf(buf + len,"%5d ",kgprof_counters[i].count); for (j=0;j<KGPROF_DEPTH;j++) { - sprintf(p,"%08x ",kgprof_counters[i].addr[j]); - p += strlen(p); + len += sprintf(buf + len,"%08x ",kgprof_counters[i].addr[j]); } - sprintf(p,"\n"); p += strlen(p); + len += sprintf(buf + len,"\n"); } } #endif - return s4cinfo; + return len; } /* Nothing below here should touch the mmu hardware nor the mmu_entry * data structures. */ +#if 0 /* Not used due to BTFIXUPs */ static unsigned int sun4c_pmd_align(unsigned int addr) { return SUN4C_PMD_ALIGN(addr); } +#endif +#if 0 /* Not used due to BTFIXUPs */ static unsigned int sun4c_pgdir_align(unsigned int addr) { return SUN4C_PGDIR_ALIGN(addr); } +#endif /* First the functions which the mid-level code uses to directly * manipulate the software page tables. Some defines since we are @@ -2170,12 +2288,17 @@ static unsigned int sun4c_pgdir_align(unsigned int addr) { return SUN4C_PGDIR_AL #define PGD_DIRTY 0x040 #define PGD_TABLE (PGD_PRESENT | PGD_RW | PGD_USER | PGD_ACCESSED | PGD_DIRTY) +#if 0 /* Not used due to BTFIXUPs */ static unsigned long sun4c_vmalloc_start(void) { return SUN4C_VMALLOC_START; } +#endif +#if 0 /* Not used due to BTFIXUPs */ static int sun4c_pte_none(pte_t pte) { return !pte_val(pte); } +#endif + static int sun4c_pte_present(pte_t pte) { return ((pte_val(pte) & (_SUN4C_PAGE_PRESENT | _SUN4C_PAGE_PRIV)) != 0); @@ -2204,35 +2327,47 @@ static void sun4c_pgd_clear(pgd_t * pgdp) { } * The following only work if pte_present() is true. * Undefined behaviour if not.. */ +#if 0 /* Not used due to BTFIXUPs */ static int sun4c_pte_write(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_WRITE; } +#endif +#if 0 /* Not used due to BTFIXUPs */ static int sun4c_pte_dirty(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_MODIFIED; } +#endif +#if 0 /* Not used due to BTFIXUPs */ static int sun4c_pte_young(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_ACCESSED; } +#endif +#if 0 /* Not used due to BTFIXUPs */ static pte_t sun4c_pte_wrprotect(pte_t pte) { return __pte(pte_val(pte) & ~(_SUN4C_PAGE_WRITE | _SUN4C_PAGE_SILENT_WRITE)); } +#endif +#if 0 /* Not used due to BTFIXUPs */ static pte_t sun4c_pte_mkclean(pte_t pte) { return __pte(pte_val(pte) & ~(_SUN4C_PAGE_MODIFIED | _SUN4C_PAGE_SILENT_WRITE)); } +#endif +#if 0 /* Not used due to BTFIXUPs */ static pte_t sun4c_pte_mkold(pte_t pte) { return __pte(pte_val(pte) & ~(_SUN4C_PAGE_ACCESSED | _SUN4C_PAGE_SILENT_READ)); } +#endif static pte_t sun4c_pte_mkwrite(pte_t pte) { @@ -2277,22 +2412,29 @@ static pte_t sun4c_mk_pte_io(unsigned long page, pgprot_t pgprot, int space) return __pte(((page - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(pgprot)); } +#if 0 /* Not used due to BTFIXUPs */ static pte_t sun4c_pte_modify(pte_t pte, pgprot_t newprot) { return __pte((pte_val(pte) & _SUN4C_PAGE_CHG_MASK) | pgprot_val(newprot)); } +#endif static unsigned long sun4c_pte_page(pte_t pte) { - return (PAGE_OFFSET + ((pte_val(pte) & 0xffff) << (PAGE_SHIFT))); + return (PAGE_OFFSET + ((pte_val(pte) & SUN4C_PFN_MASK) << (PAGE_SHIFT))); } -static unsigned long sun4c_pmd_page(pmd_t pmd) +static inline unsigned long sun4c_pmd_page(pmd_t pmd) { return (pmd_val(pmd) & PAGE_MASK); } +static unsigned long sun4c_pgd_page(pgd_t pgd) +{ + return 0; +} + /* to find an entry in a page-table-directory */ pgd_t *sun4c_pgd_offset(struct mm_struct * mm, unsigned long address) { @@ -2351,6 +2493,16 @@ static pte_t *sun4c_pte_alloc_kernel(pmd_t *pmd, unsigned long address) return (pte_t *) sun4c_pmd_page(*pmd) + address; } +static void sun4c_free_pte_slow(pte_t *pte) +{ + free_page((unsigned long)pte); +} + +static void sun4c_free_pgd_slow(pgd_t *pgd) +{ + free_page((unsigned long)pgd); +} + /* * allocating and freeing a pmd is trivial: the 1-entry pmd is * inside the pgd, so has no extra memory associated with it. @@ -2364,16 +2516,73 @@ static pmd_t *sun4c_pmd_alloc_kernel(pgd_t *pgd, unsigned long address) return (pmd_t *) pgd; } +extern __inline__ pgd_t *sun4c_get_pgd_fast(void) +{ + unsigned long *ret; + + if((ret = pgd_quicklist) != NULL) { + pgd_quicklist = (unsigned long *)(*ret); + ret[0] = ret[1]; + pgtable_cache_size--; + } else { + pgd_t *init; + + ret = (unsigned long *)__get_free_page(GFP_KERNEL); + memset (ret, 0, (KERNBASE / SUN4C_PGDIR_SIZE) * sizeof(pgd_t)); + init = pgd_offset(&init_mm, 0); + memcpy (((pgd_t *)ret) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + } + return (pgd_t *)ret; +} + +static void sun4c_set_pgdir(unsigned long address, pgd_t entry) +{ + /* Nothing to do */ +} + +extern __inline__ void sun4c_free_pgd_fast(pgd_t *pgd) +{ + *(unsigned long *)pgd = (unsigned long) pgd_quicklist; + pgd_quicklist = (unsigned long *) pgd; + pgtable_cache_size++; +} + +extern __inline__ pte_t *sun4c_get_pte_fast(void) +{ + unsigned long *ret; + + if((ret = (unsigned long *)pte_quicklist) != NULL) { + pte_quicklist = (unsigned long *)(*ret); + ret[0] = ret[1]; + pgtable_cache_size--; + } + return (pte_t *)ret; +} + +extern __inline__ void sun4c_free_pte_fast(pte_t *pte) +{ + *(unsigned long *)pte = (unsigned long) pte_quicklist; + pte_quicklist = (unsigned long *) pte; + pgtable_cache_size++; +} + static void sun4c_pte_free(pte_t *pte) { - free_page((unsigned long) pte); + sun4c_free_pte_fast(pte); } static pte_t *sun4c_pte_alloc(pmd_t * pmd, unsigned long address) { address = (address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1); if (sun4c_pmd_none(*pmd)) { - pte_t *page = (pte_t *) get_free_page(GFP_KERNEL); + pte_t *page = (pte_t *) sun4c_get_pte_fast(); + + if (page) { + *pmd = __pmd(PGD_TABLE | (unsigned long) page); + return page + address; + } + page = (pte_t *) get_free_page(GFP_KERNEL); if (sun4c_pmd_none(*pmd)) { if (page) { *pmd = __pmd(PGD_TABLE | (unsigned long) page); @@ -2392,13 +2601,17 @@ static pte_t *sun4c_pte_alloc(pmd_t * pmd, unsigned long address) return (pte_t *) sun4c_pmd_page(*pmd) + address; } +static pte_t *sun4c_pte_get(void) +{ + return sun4c_get_pte_fast(); +} + /* * allocating and freeing a pmd is trivial: the 1-entry pmd is * inside the pgd, so has no extra memory associated with it. */ static void sun4c_pmd_free(pmd_t * pmd) { - *pmd = __pmd(0); } static pmd_t *sun4c_pmd_alloc(pgd_t * pgd, unsigned long address) @@ -2408,12 +2621,12 @@ static pmd_t *sun4c_pmd_alloc(pgd_t * pgd, unsigned long address) static void sun4c_pgd_free(pgd_t *pgd) { - free_page((unsigned long) pgd); + sun4c_free_pgd_fast(pgd); } static pgd_t *sun4c_pgd_alloc(void) { - return (pgd_t *) get_free_page(GFP_KERNEL); + return sun4c_get_pgd_fast(); } /* There are really two cases of aliases to watch out for, and these @@ -2435,12 +2648,13 @@ static pgd_t *sun4c_pgd_alloc(void) */ static void sun4c_vac_alias_fixup(struct vm_area_struct *vma, unsigned long address, pte_t pte) { - struct dentry *dentry; + struct dentry *dentry = NULL; struct inode *inode = NULL; pgd_t *pgdp; pte_t *ptep; - dentry = vma->vm_dentry; + if (vma->vm_file) + dentry = vma->vm_file->f_dentry; if(dentry) inode = dentry->d_inode; if(inode) { @@ -2556,134 +2770,147 @@ __initfunc(unsigned long sun4c_paging_init(unsigned long start_mem, unsigned lon /* Load up routines and constants for sun4c mmu */ __initfunc(void ld_mmu_sun4c(void)) { + extern void ___xchg32_sun4c(void); + printk("Loading sun4c MMU routines\n"); /* First the constants */ - pmd_shift = SUN4C_PMD_SHIFT; - pmd_size = SUN4C_PMD_SIZE; - pmd_mask = SUN4C_PMD_MASK; - pgdir_shift = SUN4C_PGDIR_SHIFT; - pgdir_size = SUN4C_PGDIR_SIZE; - pgdir_mask = SUN4C_PGDIR_MASK; - - ptrs_per_pte = SUN4C_PTRS_PER_PTE; - ptrs_per_pmd = SUN4C_PTRS_PER_PMD; - ptrs_per_pgd = SUN4C_PTRS_PER_PGD; - - page_none = SUN4C_PAGE_NONE; - page_shared = SUN4C_PAGE_SHARED; - page_copy = SUN4C_PAGE_COPY; - page_readonly = SUN4C_PAGE_READONLY; - page_kernel = SUN4C_PAGE_KERNEL; + BTFIXUPSET_SIMM13(pmd_shift, SUN4C_PMD_SHIFT); + BTFIXUPSET_SETHI(pmd_size, SUN4C_PMD_SIZE); + BTFIXUPSET_SETHI(pmd_mask, SUN4C_PMD_MASK); + BTFIXUPSET_SIMM13(pgdir_shift, SUN4C_PGDIR_SHIFT); + BTFIXUPSET_SETHI(pgdir_size, SUN4C_PGDIR_SIZE); + BTFIXUPSET_SETHI(pgdir_mask, SUN4C_PGDIR_MASK); + + BTFIXUPSET_SIMM13(ptrs_per_pte, SUN4C_PTRS_PER_PTE); + BTFIXUPSET_SIMM13(ptrs_per_pmd, SUN4C_PTRS_PER_PMD); + BTFIXUPSET_SIMM13(ptrs_per_pgd, SUN4C_PTRS_PER_PGD); + BTFIXUPSET_SIMM13(user_ptrs_per_pgd, KERNBASE / SUN4C_PGDIR_SIZE); + + BTFIXUPSET_INT(page_none, pgprot_val(SUN4C_PAGE_NONE)); + BTFIXUPSET_INT(page_shared, pgprot_val(SUN4C_PAGE_SHARED)); + BTFIXUPSET_INT(page_copy, pgprot_val(SUN4C_PAGE_COPY)); + BTFIXUPSET_INT(page_readonly, pgprot_val(SUN4C_PAGE_READONLY)); + BTFIXUPSET_INT(page_kernel, pgprot_val(SUN4C_PAGE_KERNEL)); pg_iobits = _SUN4C_PAGE_PRESENT | _SUN4C_READABLE | _SUN4C_WRITEABLE | _SUN4C_PAGE_IO | _SUN4C_PAGE_NOCACHE; /* Functions */ - flush_cache_all = sun4c_flush_cache_all; +#ifndef __SMP__ + BTFIXUPSET_CALL(___xchg32, ___xchg32_sun4c, BTFIXUPCALL_NORM); +#endif + BTFIXUPSET_CALL(get_pte_fast, sun4c_pte_get, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(get_pgd_fast, sun4c_pgd_alloc, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(free_pte_slow, sun4c_free_pte_slow, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(free_pgd_slow, sun4c_free_pgd_slow, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(set_pgdir, sun4c_set_pgdir, BTFIXUPCALL_NOP); + + BTFIXUPSET_CALL(flush_cache_all, sun4c_flush_cache_all, BTFIXUPCALL_NORM); if(sun4c_vacinfo.do_hwflushes) { - flush_cache_mm = sun4c_flush_cache_mm_hw; - flush_cache_range = sun4c_flush_cache_range_hw; - flush_cache_page = sun4c_flush_cache_page_hw; - flush_page_to_ram = sun4c_flush_page_to_ram_hw; - flush_tlb_mm = sun4c_flush_tlb_mm_hw; - flush_tlb_range = sun4c_flush_tlb_range_hw; - flush_tlb_page = sun4c_flush_tlb_page_hw; - free_task_struct = sun4c_free_task_struct_hw; - switch_to_context = sun4c_switch_to_context_hw; - destroy_context = sun4c_destroy_context_hw; - init_new_context = sun4c_init_new_context_hw; + BTFIXUPSET_CALL(flush_cache_mm, sun4c_flush_cache_mm_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, sun4c_flush_cache_range_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, sun4c_flush_cache_page_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_page_to_ram, sun4c_flush_page_to_ram_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, sun4c_flush_tlb_mm_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, sun4c_flush_tlb_range_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, sun4c_flush_tlb_page_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(free_task_struct, sun4c_free_task_struct_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(switch_to_context, sun4c_switch_to_context_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(destroy_context, sun4c_destroy_context_hw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(init_new_context, sun4c_init_new_context_hw, BTFIXUPCALL_NORM); } else { - flush_cache_mm = sun4c_flush_cache_mm_sw; - flush_cache_range = sun4c_flush_cache_range_sw; - flush_cache_page = sun4c_flush_cache_page_sw; - flush_page_to_ram = sun4c_flush_page_to_ram_sw; - flush_tlb_mm = sun4c_flush_tlb_mm_sw; - flush_tlb_range = sun4c_flush_tlb_range_sw; - flush_tlb_page = sun4c_flush_tlb_page_sw; - free_task_struct = sun4c_free_task_struct_sw; - switch_to_context = sun4c_switch_to_context_sw; - destroy_context = sun4c_destroy_context_sw; - init_new_context = sun4c_init_new_context_sw; - } - - flush_tlb_all = sun4c_flush_tlb_all; - - flush_sig_insns = sun4c_flush_sig_insns; - - set_pte = sun4c_set_pte; - pmd_align = sun4c_pmd_align; - pgdir_align = sun4c_pgdir_align; - vmalloc_start = sun4c_vmalloc_start; - - pte_page = sun4c_pte_page; - pmd_page = sun4c_pmd_page; - - sparc_update_rootmmu_dir = sun4c_update_rootmmu_dir; - - pte_none = sun4c_pte_none; - pte_present = sun4c_pte_present; - pte_clear = sun4c_pte_clear; - - pmd_none = sun4c_pmd_none; - pmd_bad = sun4c_pmd_bad; - pmd_present = sun4c_pmd_present; - pmd_clear = sun4c_pmd_clear; - - pgd_none = sun4c_pgd_none; - pgd_bad = sun4c_pgd_bad; - pgd_present = sun4c_pgd_present; - pgd_clear = sun4c_pgd_clear; - - mk_pte = sun4c_mk_pte; - mk_pte_phys = sun4c_mk_pte_phys; - mk_pte_io = sun4c_mk_pte_io; - pte_modify = sun4c_pte_modify; - pgd_offset = sun4c_pgd_offset; - pmd_offset = sun4c_pmd_offset; - pte_offset = sun4c_pte_offset; - pte_free_kernel = sun4c_pte_free_kernel; - pmd_free_kernel = sun4c_pmd_free_kernel; - pte_alloc_kernel = sun4c_pte_alloc_kernel; - pmd_alloc_kernel = sun4c_pmd_alloc_kernel; - pte_free = sun4c_pte_free; - pte_alloc = sun4c_pte_alloc; - pmd_free = sun4c_pmd_free; - pmd_alloc = sun4c_pmd_alloc; - pgd_free = sun4c_pgd_free; - pgd_alloc = sun4c_pgd_alloc; - - pte_write = sun4c_pte_write; - pte_dirty = sun4c_pte_dirty; - pte_young = sun4c_pte_young; - pte_wrprotect = sun4c_pte_wrprotect; - pte_mkclean = sun4c_pte_mkclean; - pte_mkold = sun4c_pte_mkold; - pte_mkwrite = sun4c_pte_mkwrite; - pte_mkdirty = sun4c_pte_mkdirty; - pte_mkyoung = sun4c_pte_mkyoung; - update_mmu_cache = sun4c_update_mmu_cache; - - mmu_lockarea = sun4c_lockarea; - mmu_unlockarea = sun4c_unlockarea; - - mmu_get_scsi_one = sun4c_get_scsi_one; - mmu_get_scsi_sgl = sun4c_get_scsi_sgl; - mmu_release_scsi_one = sun4c_release_scsi_one; - mmu_release_scsi_sgl = sun4c_release_scsi_sgl; - - mmu_map_dma_area = sun4c_map_dma_area; - - mmu_v2p = sun4c_v2p; - mmu_p2v = sun4c_p2v; + BTFIXUPSET_CALL(flush_cache_mm, sun4c_flush_cache_mm_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, sun4c_flush_cache_range_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, sun4c_flush_cache_page_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_page_to_ram, sun4c_flush_page_to_ram_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, sun4c_flush_tlb_mm_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, sun4c_flush_tlb_range_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, sun4c_flush_tlb_page_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(free_task_struct, sun4c_free_task_struct_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(switch_to_context, sun4c_switch_to_context_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(destroy_context, sun4c_destroy_context_sw, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(init_new_context, sun4c_init_new_context_sw, BTFIXUPCALL_NORM); + } + + BTFIXUPSET_CALL(flush_tlb_all, sun4c_flush_tlb_all, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(flush_sig_insns, sun4c_flush_sig_insns, BTFIXUPCALL_NOP); + + BTFIXUPSET_CALL(set_pte, sun4c_set_pte, BTFIXUPCALL_STO1O0); + + BTFIXUPSET_CALL(pte_page, sun4c_pte_page, BTFIXUPCALL_NORM); +#if PAGE_SHIFT <= 12 + BTFIXUPSET_CALL(pmd_page, sun4c_pmd_page, BTFIXUPCALL_ANDNINT(PAGE_SIZE - 1)); +#else + BTFIXUPSET_CALL(pmd_page, sun4c_pmd_page, BTFIXUPCALL_NORM); +#endif + + BTFIXUPSET_CALL(sparc_update_rootmmu_dir, sun4c_update_rootmmu_dir, BTFIXUPCALL_NOP); + + BTFIXUPSET_CALL(pte_present, sun4c_pte_present, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_clear, sun4c_pte_clear, BTFIXUPCALL_STG0O0); + + BTFIXUPSET_CALL(pmd_bad, sun4c_pmd_bad, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_present, sun4c_pmd_present, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_clear, sun4c_pmd_clear, BTFIXUPCALL_STG0O0); + + BTFIXUPSET_CALL(pgd_none, sun4c_pgd_none, BTFIXUPCALL_RETINT(0)); + BTFIXUPSET_CALL(pgd_bad, sun4c_pgd_bad, BTFIXUPCALL_RETINT(0)); + BTFIXUPSET_CALL(pgd_present, sun4c_pgd_present, BTFIXUPCALL_RETINT(1)); + BTFIXUPSET_CALL(pgd_clear, sun4c_pgd_clear, BTFIXUPCALL_NOP); + + BTFIXUPSET_CALL(mk_pte, sun4c_mk_pte, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mk_pte_phys, sun4c_mk_pte_phys, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mk_pte_io, sun4c_mk_pte_io, BTFIXUPCALL_NORM); + + BTFIXUPSET_INT(pte_modify_mask, _SUN4C_PAGE_CHG_MASK); + BTFIXUPSET_CALL(pgd_offset, sun4c_pgd_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_offset, sun4c_pmd_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_offset, sun4c_pte_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_free_kernel, sun4c_pte_free_kernel, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_free_kernel, sun4c_pmd_free_kernel, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(pte_alloc_kernel, sun4c_pte_alloc_kernel, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_alloc_kernel, sun4c_pmd_alloc_kernel, BTFIXUPCALL_RETO0); + BTFIXUPSET_CALL(pte_free, sun4c_pte_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_alloc, sun4c_pte_alloc, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_free, sun4c_pmd_free, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(pmd_alloc, sun4c_pmd_alloc, BTFIXUPCALL_RETO0); + BTFIXUPSET_CALL(pgd_free, sun4c_pgd_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pgd_alloc, sun4c_pgd_alloc, BTFIXUPCALL_NORM); + + BTFIXUPSET_HALF(pte_writei, _SUN4C_PAGE_WRITE); + BTFIXUPSET_HALF(pte_dirtyi, _SUN4C_PAGE_MODIFIED); + BTFIXUPSET_HALF(pte_youngi, _SUN4C_PAGE_ACCESSED); + BTFIXUPSET_HALF(pte_wrprotecti, _SUN4C_PAGE_WRITE|_SUN4C_PAGE_SILENT_WRITE); + BTFIXUPSET_HALF(pte_mkcleani, _SUN4C_PAGE_MODIFIED|_SUN4C_PAGE_SILENT_WRITE); + BTFIXUPSET_HALF(pte_mkoldi, _SUN4C_PAGE_ACCESSED|_SUN4C_PAGE_SILENT_READ); + BTFIXUPSET_CALL(pte_mkwrite, sun4c_pte_mkwrite, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_mkdirty, sun4c_pte_mkdirty, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pte_mkyoung, sun4c_pte_mkyoung, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(update_mmu_cache, sun4c_update_mmu_cache, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(mmu_lockarea, sun4c_lockarea, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_unlockarea, sun4c_unlockarea, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(mmu_get_scsi_one, sun4c_get_scsi_one, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_get_scsi_sgl, sun4c_get_scsi_sgl, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_release_scsi_one, sun4c_release_scsi_one, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_release_scsi_sgl, sun4c_release_scsi_sgl, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(mmu_map_dma_area, sun4c_map_dma_area, BTFIXUPCALL_NORM); + + BTFIXUPSET_CALL(mmu_v2p, sun4c_v2p, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_p2v, sun4c_p2v, BTFIXUPCALL_NORM); /* Task struct and kernel stack allocating/freeing. */ - alloc_task_struct = sun4c_alloc_task_struct; + BTFIXUPSET_CALL(alloc_task_struct, sun4c_alloc_task_struct, BTFIXUPCALL_NORM); - quick_kernel_fault = sun4c_quick_kernel_fault; - mmu_info = sun4c_mmu_info; + BTFIXUPSET_CALL(quick_kernel_fault, sun4c_quick_kernel_fault, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_info, sun4c_mmu_info, BTFIXUPCALL_NORM); /* These should _never_ get called with two level tables. */ - pgd_set = 0; - pgd_page = 0; + BTFIXUPSET_CALL(pgd_set, sun4c_pgd_set, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(pgd_page, sun4c_pgd_page, BTFIXUPCALL_RETO0); } diff --git a/arch/sparc/mm/turbosparc.S b/arch/sparc/mm/turbosparc.S index 415f09056..df580a85c 100644 --- a/arch/sparc/mm/turbosparc.S +++ b/arch/sparc/mm/turbosparc.S @@ -1,4 +1,4 @@ -/* $Id: turbosparc.S,v 1.2 1998/03/16 08:40:31 ralf Exp $ +/* $Id: turbosparc.S,v 1.3 1998/05/04 12:41:29 ralf Exp $ * turbosparc.S: High speed TurboSparc mmu/cache operations. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -27,6 +27,7 @@ .globl turbosparc_flush_cache_all .globl turbosparc_flush_sig_insns + .globl turbosparc_flush_page_for_dma turbosparc_flush_cache_all: WINDOW_FLUSH(%g4, %g5) @@ -42,5 +43,6 @@ turbosparc_flush_cache_all: sta %g0, [%g0] ASI_M_IC_FLCLEAR turbosparc_flush_sig_insns: +turbosparc_flush_page_for_dma: retl nop diff --git a/arch/sparc/mm/viking.S b/arch/sparc/mm/viking.S index b05b7b416..c65f72007 100644 --- a/arch/sparc/mm/viking.S +++ b/arch/sparc/mm/viking.S @@ -1,8 +1,8 @@ -/* $Id: viking.S,v 1.6 1997/11/27 15:42:32 jj Exp $ +/* $Id: viking.S,v 1.11 1998/02/20 18:07:50 jj Exp $ * viking.S: High speed Viking cache/mmu operations * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <asm/ptrace.h> @@ -13,6 +13,7 @@ #include <asm/pgtsrmmu.h> #include <asm/viking.h> #include <asm/cprefix.h> +#include <asm/btfixup.h> #define WINDOW_FLUSH(tmp1, tmp2) \ mov 0, tmp1; \ @@ -37,40 +38,33 @@ .globl viking_flush_tlb_all, viking_flush_tlb_mm .globl viking_flush_tlb_range, viking_flush_tlb_page - .globl viking_c_mxcc_flush_page - .globl viking_c_flush_page, viking_c_flush_chunk + .globl viking_c_flush_chunk, viking_s_flush_chunk + +viking_s_flush_chunk: + sethi %hi(KERNBASE), %g2 + ba 2f + sub %o0, %g2, %g3 -viking_c_flush_page: viking_c_flush_chunk: sethi %hi(KERNBASE), %g2 cmp %o0, %g2 bgeu 2f sub %o0, %g2, %g3 - sethi %hi(C_LABEL(page_contig_offset)), %g2 - ld [%g2 + %lo(C_LABEL(page_contig_offset))], %g2 + sethi BTFIXUP_SETHI(page_contig_offset), %g2 ba 2f sub %o0, %g2, %g3 viking_flush_page: viking_flush_chunk: sethi %hi(C_LABEL(srmmu_v2p_hash)), %g2 - or %g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2 srl %o0, 24, %o1 + or %g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2 sll %o1, 2, %o1 - ld [%g2 + %o1], %g3 - cmp %g3, 0 - bne 1f - and %o0, PAGE_MASK, %o0 - - retl - nop - -1: - ld [%g3], %o1 - sub %o0, %o1, %g2 - ld [%g3 + 4], %o0 - add %g2, %o0, %g3 + and %o0, PAGE_MASK, %o0 + cmp %g3, -1 + be 9f + add %o0, %g3, %g3 2: srl %g3, 12, %g1 ! ppage >> 12 clr %o1 ! set counter, 0 - 127 @@ -124,41 +118,22 @@ viking_flush_chunk: ble 5b clr %o2 - retl +9: retl nop -viking_c_mxcc_flush_page: - sethi %hi(KERNBASE), %g2 - cmp %o0, %g2 - bgeu 2f - sub %o0, %g2, %g3 - sethi %hi(C_LABEL(page_contig_offset)), %g2 - ld [%g2 + %lo(C_LABEL(page_contig_offset))], %g2 - ba 2f - sub %o0, %g2, %g3 - viking_mxcc_flush_page: sethi %hi(C_LABEL(srmmu_v2p_hash)), %g2 - or %g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2 srl %o0, 24, %o1 + or %g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2 sll %o1, 2, %o1 - ld [%g2 + %o1], %g3 - cmp %g3, 0 - bne 1f - and %o0, PAGE_MASK, %o0 - - retl - nop - -1: - ld [%g3], %o1 - sub %o0, %o1, %g2 - ld [%g3 + 4], %o0 - add %g2, %o0, %g3 + and %o0, PAGE_MASK, %o0 + cmp %g3, -1 + be 9f + add %o0, %g3, %g3 2: sub %g3, -PAGE_SIZE, %g3 ! ppage + PAGE_SIZE - mov 0x10, %g2 ! set cacheable bit sethi %hi(MXCC_SRCSTREAM), %o3 ! assume %hi(MXCC_SRCSTREAM) == %hi(MXCC_DESTSTREAM) + mov 0x10, %g2 ! set cacheable bit or %o3, %lo(MXCC_SRCSTREAM), %o2 or %o3, %lo(MXCC_DESSTREAM), %o3 sub %g3, MXCC_STREAM_SIZE, %g3 @@ -169,7 +144,7 @@ viking_mxcc_flush_page: bne 6b sub %g3, MXCC_STREAM_SIZE, %g3 - retl +9: retl nop viking_mxcc_flush_chunk: @@ -212,13 +187,12 @@ viking_flush_tlb_range: cmp %o3, -1 be 2f #endif - srl %o1, SRMMU_PGDIR_SHIFT, %o1 + sethi %hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4 sta %o3, [%g1] ASI_M_MMUREGS - sll %o1, SRMMU_PGDIR_SHIFT, %o1 - sethi %hi(1 << SRMMU_PGDIR_SHIFT), %o4 + and %o1, %o4, %o1 add %o1, 0x200, %o1 sta %g0, [%o1] ASI_M_FLUSH_PROBE -1: add %o1, %o4, %o1 +1: sub %o1, %o4, %o1 cmp %o1, %o2 blu,a 1b sta %g0, [%o1] ASI_M_FLUSH_PROBE diff --git a/arch/sparc/prom/Makefile b/arch/sparc/prom/Makefile index 9c820a006..917aa9ad7 100644 --- a/arch/sparc/prom/Makefile +++ b/arch/sparc/prom/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.5 1995/11/25 00:59:48 davem Exp $ +# $Id: Makefile,v 1.6 1998/01/30 10:58:59 jj Exp $ # Makefile for the Sun Boot PROM interface library under # Linux. # @@ -9,7 +9,11 @@ # Note 2! The CFLAGS definitions are now in the main makefile... OBJS = bootstr.o devmap.o devops.o init.o memory.o misc.o mp.o \ - palloc.o ranges.o segment.o tree.o console.o printf.o + palloc.o ranges.o segment.o console.o printf.o tree.o + +ifeq ($(CONFIG_SUN4),y) +OBJS += sun4prom.o +endif all: promlib.a diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr.c index e7bd9b06d..10a603455 100644 --- a/arch/sparc/prom/bootstr.c +++ b/arch/sparc/prom/bootstr.c @@ -1,4 +1,4 @@ -/* $Id: bootstr.c,v 1.14 1997/06/19 16:28:49 jj Exp $ +/* $Id: bootstr.c,v 1.17 1998/02/09 13:26:21 jj Exp $ * bootstr.c: Boot string/argument acquisition from the PROM. * * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -7,12 +7,15 @@ #include <linux/config.h> #include <linux/string.h> #include <asm/oplib.h> +#include <asm/sun4prom.h> #include <linux/init.h> #define BARG_LEN 256 -static char barg_buf[BARG_LEN] __initdata = { 0 }; +static char barg_buf[BARG_LEN] = { 0 }; static char fetched __initdata = 0; +extern linux_sun4_romvec *sun4_romvec; + __initfunc(char * prom_getbootargs(void)) { @@ -26,6 +29,7 @@ prom_getbootargs(void)) switch(prom_vers) { case PROM_V0: + case PROM_SUN4: cp = barg_buf; /* Start from 1 and go over fd(0,0,0)kernel */ for(iter = 1; iter < 8; iter++) { diff --git a/arch/sparc/prom/console.c b/arch/sparc/prom/console.c index 4c999477b..3bbc7ade0 100644 --- a/arch/sparc/prom/console.c +++ b/arch/sparc/prom/console.c @@ -1,4 +1,4 @@ -/* $Id: console.c,v 1.14 1997/05/14 20:44:58 davem Exp $ +/* $Id: console.c,v 1.17 1998/03/09 14:04:21 jj Exp $ * console.c: Routines that deal with sending and receiving IO * to/from the current console device using the PROM. * @@ -10,12 +10,12 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <asm/openprom.h> +#include <asm/sun4prom.h> #include <asm/oplib.h> #include <asm/system.h> #include <linux/string.h> -/* XXX Let's get rid of this thing if we can... */ -extern struct task_struct *current_set[NR_CPUS]; +extern void restore_current(void); /* Non blocking get character from console input device, returns -1 * if no input was taken. This can be used for polling. @@ -30,6 +30,7 @@ prom_nbgetchar(void) save_flags(flags); cli(); switch(prom_vers) { case PROM_V0: + case PROM_SUN4: i = (*(romvec->pv_nbgetchar))(); break; case PROM_V2: @@ -45,9 +46,7 @@ prom_nbgetchar(void) i = -1; break; }; - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return i; /* Ugh, we could spin forever on unsupported proms ;( */ } @@ -65,6 +64,7 @@ prom_nbputchar(char c) save_flags(flags); cli(); switch(prom_vers) { case PROM_V0: + case PROM_SUN4: i = (*(romvec->pv_nbputchar))(c); break; case PROM_V2: @@ -89,9 +89,7 @@ prom_nbputchar(char c) i = -1; break; }; - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return i; /* Ugh, we could spin forever on unsupported proms ;( */ } @@ -125,6 +123,7 @@ prom_query_input_device() switch(prom_vers) { case PROM_V0: case PROM_V2: + case PROM_SUN4: default: switch(*romvec->pv_stdin) { case PROMDEV_KBD: return PROMDEV_IKBD; @@ -136,9 +135,7 @@ prom_query_input_device() case PROM_V3: save_flags(flags); cli(); st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin); - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); if(prom_node_has_property(st_p, "keyboard")) return PROMDEV_IKBD; @@ -173,6 +170,7 @@ prom_query_output_device() switch(prom_vers) { case PROM_V0: + case PROM_SUN4: switch(*romvec->pv_stdin) { case PROMDEV_SCREEN: return PROMDEV_OSCREEN; case PROMDEV_TTYA: return PROMDEV_OTTYA; @@ -183,9 +181,7 @@ prom_query_output_device() case PROM_V3: save_flags(flags); cli(); st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout); - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb)); if (propl >= 0 && propl == sizeof("display") && diff --git a/arch/sparc/prom/devmap.c b/arch/sparc/prom/devmap.c index cd99ac3d6..463b07527 100644 --- a/arch/sparc/prom/devmap.c +++ b/arch/sparc/prom/devmap.c @@ -1,4 +1,4 @@ -/* $Id: devmap.c,v 1.5 1997/05/14 20:44:59 davem Exp $ +/* $Id: devmap.c,v 1.6 1998/03/09 14:04:23 jj Exp $ * promdevmap.c: Map device/IO areas to virtual addresses. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -11,8 +11,7 @@ #include <asm/openprom.h> #include <asm/oplib.h> -/* XXX Let's get rid of this thing if we can... */ -extern struct task_struct *current_set[NR_CPUS]; +extern void restore_current(void); /* Just like the routines in palloc.c, these should not be used * by the kernel at all. Bootloader facility mainly. And again, @@ -35,9 +34,7 @@ prom_mapio(char *vhint, int ios, unsigned int paddr, unsigned int num_bytes) else ret = (*(romvec->pv_v2devops.v2_dumb_mmap))(vhint, ios, paddr, num_bytes); - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return ret; } @@ -51,9 +48,7 @@ prom_unmapio(char *vaddr, unsigned int num_bytes) if(num_bytes == 0x0) return; save_flags(flags); cli(); (*(romvec->pv_v2devops.v2_dumb_munmap))(vaddr, num_bytes); - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return; } diff --git a/arch/sparc/prom/devops.c b/arch/sparc/prom/devops.c index f7feb0815..c273b9922 100644 --- a/arch/sparc/prom/devops.c +++ b/arch/sparc/prom/devops.c @@ -1,4 +1,4 @@ -/* $Id: devops.c,v 1.10 1997/05/14 20:44:59 davem Exp $ +/* $Id: devops.c,v 1.11 1998/03/09 14:04:24 jj Exp $ * devops.c: Device operations using the PROM. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -10,8 +10,7 @@ #include <asm/openprom.h> #include <asm/oplib.h> -/* XXX Let's get rid of this thing if we can... */ -extern struct task_struct *current_set[NR_CPUS]; +extern void restore_current(void); /* Open the device described by the string 'dstr'. Returns the handle * to that device used for subsequent operations on that device. @@ -37,9 +36,7 @@ prom_devopen(char *dstr) handle = -1; break; }; - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return handle; @@ -63,9 +60,7 @@ prom_devclose(int dhandle) default: break; }; - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return 0; } @@ -90,9 +85,7 @@ prom_seek(int dhandle, unsigned int seekhi, unsigned int seeklo) default: break; }; - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return; diff --git a/arch/sparc/prom/init.c b/arch/sparc/prom/init.c index 6f691464a..2c70dd95a 100644 --- a/arch/sparc/prom/init.c +++ b/arch/sparc/prom/init.c @@ -1,8 +1,9 @@ -/* $Id: init.c,v 1.11 1997/03/18 17:58:24 jj Exp $ +/* $Id: init.c,v 1.12 1998/01/30 10:59:02 jj Exp $ * init.c: Initialize internal variables used by the PROM * library functions. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h> @@ -11,10 +12,12 @@ #include <asm/openprom.h> #include <asm/oplib.h> +#include <asm/sun4prom.h> struct linux_romvec *romvec; enum prom_major_version prom_vers; unsigned int prom_rev, prom_prev; +linux_sun4_romvec *sun4_romvec; /* The root node of the prom device tree. */ int prom_root_node; @@ -34,11 +37,14 @@ extern void prom_ranges_init(void); __initfunc(void prom_init(struct linux_romvec *rp)) { +#ifdef CONFIG_SUN4 + extern struct linux_romvec *sun4_prom_init(void); + rp = sun4_prom_init(); +#endif #if CONFIG_AP1000 extern struct linux_romvec *ap_prom_init(void); rp = ap_prom_init(); #endif - romvec = rp; switch(romvec->pv_romvers) { @@ -51,6 +57,9 @@ __initfunc(void prom_init(struct linux_romvec *rp)) case 3: prom_vers = PROM_V3; break; + case 40: + prom_vers = PROM_SUN4; + break; case 42: /* why not :-) */ prom_vers = PROM_AP1000; break; @@ -83,8 +92,11 @@ __initfunc(void prom_init(struct linux_romvec *rp)) prom_ranges_init(); +#ifndef CONFIG_SUN4 + /* SUN4 prints this in sun4_prom_init */ printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n", romvec->pv_romvers, prom_rev); +#endif /* Initialization successful. */ return; diff --git a/arch/sparc/prom/memory.c b/arch/sparc/prom/memory.c index b53bd17ea..af5019eb8 100644 --- a/arch/sparc/prom/memory.c +++ b/arch/sparc/prom/memory.c @@ -1,8 +1,9 @@ -/* $Id: memory.c,v 1.12 1997/05/27 06:45:57 davem Exp $ +/* $Id: memory.c,v 1.13 1998/01/30 10:59:03 jj Exp $ * memory.c: Prom routine for acquiring various bits of information * about RAM on the machine, both virtual and physical. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1997 Michael A. Griffith (grif@acm.org) */ #include <linux/config.h> @@ -10,6 +11,7 @@ #include <linux/init.h> #include <asm/openprom.h> +#include <asm/sun4prom.h> #include <asm/oplib.h> /* This routine, for consistency, returns the ram parameters in the @@ -177,6 +179,21 @@ __initfunc(void prom_meminit(void)) prom_sortmemlist(prom_phys_avail); break; + case PROM_SUN4: +#ifdef CONFIG_SUN4 + /* how simple :) */ + prom_phys_total[0].start_adr = 0x0; + prom_phys_total[0].num_bytes = *(sun4_romvec->memorysize); + prom_phys_total[0].theres_more = 0x0; + prom_prom_taken[0].start_adr = 0x0; + prom_prom_taken[0].num_bytes = 0x0; + prom_prom_taken[0].theres_more = 0x0; + prom_phys_avail[0].start_adr = 0x0; + prom_phys_avail[0].num_bytes = *(sun4_romvec->memoryavail); + prom_phys_avail[0].theres_more = 0x0; +#endif + break; + case PROM_AP1000: #if CONFIG_AP1000 /* really simple memory map */ @@ -189,9 +206,6 @@ __initfunc(void prom_meminit(void)) prom_phys_avail[0].start_adr = 0x00000000; prom_phys_avail[0].num_bytes = prom_phys_total[0].num_bytes; prom_phys_avail[0].theres_more = 0x0; - prom_sortmemlist(prom_phys_total); - prom_sortmemlist(prom_prom_taken); - prom_sortmemlist(prom_phys_avail); #endif default: break; diff --git a/arch/sparc/prom/misc.c b/arch/sparc/prom/misc.c index fede033dd..d2ec600e1 100644 --- a/arch/sparc/prom/misc.c +++ b/arch/sparc/prom/misc.c @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.15 1997/05/14 20:45:00 davem Exp $ +/* $Id: misc.c,v 1.16 1998/03/09 14:04:25 jj Exp $ * misc.c: Miscellaneous prom functions that don't belong * anywhere else. * @@ -13,8 +13,7 @@ #include <asm/oplib.h> #include <asm/auxio.h> -/* XXX Let's get rid of this thing if we can... */ -extern struct task_struct *current_set[NR_CPUS]; +extern void restore_current(void); /* Reset and reboot the machine with the command 'bcommand'. */ void @@ -24,9 +23,7 @@ prom_reboot(char *bcommand) save_flags(flags); cli(); (*(romvec->pv_reboot))(bcommand); /* Never get here. */ - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); } @@ -42,9 +39,7 @@ prom_feval(char *fstring) (*(romvec->pv_fortheval.v0_eval))(strlen(fstring), fstring); else (*(romvec->pv_fortheval.v2_eval))(fstring); - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); } @@ -74,9 +69,7 @@ prom_cmdline(void) install_obp_ticker(); save_flags(flags); cli(); (*(romvec->pv_abort))(); - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); install_linux_ticker(); #ifdef CONFIG_SUN_AUXIO @@ -99,9 +92,7 @@ again: save_flags(flags); cli(); (*(romvec->pv_halt))(); /* Never get here. */ - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); goto again; /* PROM is out to get me -DaveM */ } diff --git a/arch/sparc/prom/mp.c b/arch/sparc/prom/mp.c index 8f07f9d40..2346e3564 100644 --- a/arch/sparc/prom/mp.c +++ b/arch/sparc/prom/mp.c @@ -1,4 +1,4 @@ -/* $Id: mp.c,v 1.9 1997/05/14 20:45:01 davem Exp $ +/* $Id: mp.c,v 1.10 1998/03/09 14:04:26 jj Exp $ * mp.c: OpenBoot Prom Multiprocessor support routines. Don't call * these on a UP or else you will halt and catch fire. ;) * @@ -12,8 +12,7 @@ #include <asm/openprom.h> #include <asm/oplib.h> -/* XXX Let's get rid of this thing if we can... */ -extern struct task_struct *current_set[NR_CPUS]; +extern void restore_current(void); /* Start cpu with prom-tree node 'cpunode' using context described * by 'ctable_reg' in context 'ctx' at program counter 'pc'. @@ -38,9 +37,7 @@ prom_startcpu(int cpunode, struct linux_prom_registers *ctable_reg, int ctx, cha ret = (*(romvec->v3_cpustart))(cpunode, (int) ctable_reg, ctx, pc); break; }; - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return ret; @@ -67,9 +64,7 @@ prom_stopcpu(int cpunode) ret = (*(romvec->v3_cpustop))(cpunode); break; }; - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return ret; @@ -96,9 +91,7 @@ prom_idlecpu(int cpunode) ret = (*(romvec->v3_cpuidle))(cpunode); break; }; - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return ret; @@ -125,9 +118,7 @@ prom_restartcpu(int cpunode) ret = (*(romvec->v3_cpuresume))(cpunode); break; }; - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return ret; diff --git a/arch/sparc/prom/ranges.c b/arch/sparc/prom/ranges.c index 7f7b1da54..b4fd3989e 100644 --- a/arch/sparc/prom/ranges.c +++ b/arch/sparc/prom/ranges.c @@ -1,4 +1,4 @@ -/* $Id: ranges.c,v 1.10 1997/12/19 12:37:18 jj Exp $ +/* $Id: ranges.c,v 1.11 1998/01/30 10:59:05 jj Exp $ * ranges.c: Handle ranges in newer proms for obio/sbus. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -68,7 +68,7 @@ prom_apply_obio_ranges(struct linux_prom_registers *regs, int nregs) void prom_apply_sbus_ranges(struct linux_sbus *sbus, struct linux_prom_registers *regs, int nregs, struct linux_sbus_device *sdev) { - if(sbus->num_sbus_ranges) { + if(sbus && sbus->num_sbus_ranges) { if(sdev && (sdev->ranges_applied == 0)) { sdev->ranges_applied = 1; prom_adjust_regs(regs, nregs, sbus->sbus_ranges, diff --git a/arch/sparc/prom/segment.c b/arch/sparc/prom/segment.c index 96b543727..62b3f8542 100644 --- a/arch/sparc/prom/segment.c +++ b/arch/sparc/prom/segment.c @@ -1,4 +1,4 @@ -/* $Id: segment.c,v 1.5 1997/05/14 20:45:02 davem Exp $ +/* $Id: segment.c,v 1.6 1998/03/09 14:04:27 jj Exp $ * segment.c: Prom routine to map segments in other contexts before * a standalone is completely mapped. This is for sun4 and * sun4c architectures only. @@ -12,8 +12,7 @@ #include <asm/openprom.h> #include <asm/oplib.h> -/* XXX Let's get rid of this thing if we can... */ -extern struct task_struct *current_set[NR_CPUS]; +extern void restore_current(void); /* Set physical segment 'segment' at virtual address 'vaddr' in * context 'ctx'. @@ -24,9 +23,7 @@ prom_putsegment(int ctx, unsigned long vaddr, int segment) unsigned long flags; save_flags(flags); cli(); (*(romvec->pv_setctxt))(ctx, (char *) vaddr, segment); - __asm__ __volatile__("ld [%0], %%g6\n\t" : : - "r" (¤t_set[hard_smp_processor_id()]) : - "memory"); + restore_current(); restore_flags(flags); return; } diff --git a/arch/sparc/prom/sun4prom.c b/arch/sparc/prom/sun4prom.c new file mode 100644 index 000000000..ce15ebb43 --- /dev/null +++ b/arch/sparc/prom/sun4prom.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 1996 The Australian National University. + * Copyright (C) 1996 Fujitsu Laboratories Limited + * Copyright (C) 1997 Michael A. Griffith (grif@acm.org) + * Copyright (C) 1997 Sun Weenie (ko@ko.reno.nv.us) + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * This software may be distributed under the terms of the Gnu + * Public License version 2 or later + * + * fake a really simple Sun prom for the SUN4 + */ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <asm/oplib.h> +#include <asm/idprom.h> +#include <asm/machines.h> +#include <asm/sun4prom.h> +#include <asm/asi.h> +#include <asm/contregs.h> +#include <linux/init.h> + +static struct linux_romvec sun4romvec; +static struct idprom sun4_idprom; + +struct property { + char *name; + char *value; + int length; +}; + +struct node { + int level; + struct property *properties; +}; + +struct property null_properties = { NULL, NULL, -1 }; + +struct property root_properties[] = { + {"device_type", "cpu", 4}, + {"idprom", (char *)&sun4_idprom, sizeof(struct idprom)}, + {NULL, NULL, -1} +}; + +struct node nodes[] = { + { 0, &null_properties }, + { 0, root_properties }, + { -1,&null_properties } +}; + + +static int no_nextnode(int node) +{ + if (nodes[node].level == nodes[node+1].level) + return node+1; + return -1; +} + +static int no_child(int node) +{ + if (nodes[node].level == nodes[node+1].level-1) + return node+1; + return -1; +} + +static struct property *find_property(int node,char *name) +{ + struct property *prop = &nodes[node].properties[0]; + while (prop && prop->name) { + if (strcmp(prop->name,name) == 0) return prop; + prop++; + } + return NULL; +} + +static int no_proplen(int node,char *name) +{ + struct property *prop = find_property(node,name); + if (prop) return prop->length; + return -1; +} + +static int no_getprop(int node,char *name,char *value) +{ + struct property *prop = find_property(node,name); + if (prop) { + memcpy(value,prop->value,prop->length); + return 1; + } + return -1; +} + +static int no_setprop(int node,char *name,char *value,int len) +{ + return -1; +} + +static char *no_nextprop(int node,char *name) +{ + struct property *prop = find_property(node,name); + if (prop) return prop[1].name; + return NULL; +} + +static struct linux_nodeops sun4_nodeops = { + no_nextnode, + no_child, + no_proplen, + no_getprop, + no_setprop, + no_nextprop +}; + +static int synch_hook; + +__initfunc(struct linux_romvec *sun4_prom_init(void)) +{ + int i; + unsigned char x; + char *p; + + p = (char *)&sun4_idprom; + for (i = 0; i < sizeof(sun4_idprom); i++) { + __asm__ __volatile__ ("lduba [%1] %2, %0" : "=r" (x) : + "r" (AC_IDPROM + i), "i" (ASI_CONTROL)); + *p++ = x; + } + + memset(&sun4romvec,0,sizeof(sun4romvec)); + + sun4_romvec = (linux_sun4_romvec *) SUN4_PROM_VECTOR; + + sun4romvec.pv_romvers = 40; + sun4romvec.pv_nodeops = &sun4_nodeops; + sun4romvec.pv_reboot = sun4_romvec->reboot; + sun4romvec.pv_abort = sun4_romvec->abortentry; + sun4romvec.pv_halt = sun4_romvec->exittomon; + sun4romvec.pv_synchook = (void (**)(void))&synch_hook; + sun4romvec.pv_setctxt = sun4_romvec->setcxsegmap; + sun4romvec.pv_v0bootargs = sun4_romvec->bootParam; + sun4romvec.pv_nbgetchar = sun4_romvec->mayget; + sun4romvec.pv_nbputchar = sun4_romvec->mayput; + sun4romvec.pv_stdin = sun4_romvec->insource; + sun4romvec.pv_stdout = sun4_romvec->outsink; + + /* + * We turn on the LEDs to let folks without monitors or + * terminals know we booted. Nothing too fancy now. They + * are all on, except for LED 5, which blinks. When we + * have more time, we can teach the penguin to say "By your + * command" or "Activating turbo boost, Michael". :-) + */ + sun4_romvec->setLEDs(0x0); + + printk("PROMLIB: Old Sun4 boot PROM monitor %s, romvec version %d\n", + sun4_romvec->monid, + sun4_romvec->romvecversion); + + return &sun4romvec; +} diff --git a/arch/sparc/prom/tree.c b/arch/sparc/prom/tree.c index 616180e81..1256aacec 100644 --- a/arch/sparc/prom/tree.c +++ b/arch/sparc/prom/tree.c @@ -1,4 +1,4 @@ -/* $Id: tree.c,v 1.22 1997/09/25 02:19:22 davem Exp $ +/* $Id: tree.c,v 1.24 1998/03/09 14:04:29 jj Exp $ * tree.c: Basic device tree traversal/scanning for the Linux * prom library. * @@ -15,13 +15,7 @@ #include <asm/openprom.h> #include <asm/oplib.h> -/* XXX Let's get rid of this thing if we can... */ -extern struct task_struct *current_set[NR_CPUS]; - -/* Macro to restore "current" to the g6 register. */ -#define restore_current() __asm__ __volatile__("ld [%0], %%g6\n\t" : : \ - "r" (¤t_set[hard_smp_processor_id()]) : \ - "memory") +extern void restore_current(void); static char promlib_buf[128]; @@ -95,12 +89,11 @@ int prom_getproplen(int node, char *prop) int ret; unsigned long flags; - save_flags(flags); cli(); - if((!node) || (!prop)) - ret = -1; - else - ret = prom_nodeops->no_proplen(node, prop); + return -1; + + save_flags(flags); cli(); + ret = prom_nodeops->no_proplen(node, prop); restore_current(); restore_flags(flags); return ret; @@ -115,15 +108,12 @@ int prom_getproperty(int node, char *prop, char *buffer, int bufsize) int plen, ret; unsigned long flags; - save_flags(flags); cli(); - plen = prom_getproplen(node, prop); if((plen > bufsize) || (plen == 0) || (plen == -1)) - ret = -1; - else { - /* Ok, things seem all right. */ - ret = prom_nodeops->no_getprop(node, prop, buffer); - } + return -1; + /* Ok, things seem all right. */ + save_flags(flags); cli(); + ret = prom_nodeops->no_getprop(node, prop, buffer); restore_current(); restore_flags(flags); return ret; diff --git a/arch/sparc/vmlinux.lds b/arch/sparc/vmlinux.lds index 8141f5755..cbfc9fb3c 100644 --- a/arch/sparc/vmlinux.lds +++ b/arch/sparc/vmlinux.lds @@ -32,6 +32,7 @@ SECTIONS . = ALIGN(4096); __init_begin = .; .text.init : { *(.text.init) } + __init_text_end = .; .data.init : { *(.data.init) } . = ALIGN(4096); __init_end = .; diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile index 9e8b0b862..e004359c3 100644 --- a/arch/sparc64/Makefile +++ b/arch/sparc64/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.24 1997/10/02 16:31:16 jj Exp $ +# $Id: Makefile,v 1.25 1998/04/06 16:10:31 jj Exp $ # sparc64/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -38,9 +38,7 @@ ifneq ($(CONFIG_SOLARIS_EMUL),n) SUBDIRS += arch/sparc64/solaris endif -ifneq ($(CONFIG_MATHEMU),n) - SUBDIRS += arch/sparc64/math-emu -endif +SUBDIRS += arch/sparc64/math-emu CORE_FILES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(CORE_FILES) @@ -48,9 +46,7 @@ ifeq ($(CONFIG_SOLARIS_EMUL),y) CORE_FILES += arch/sparc64/solaris/solaris.o endif -ifeq ($(CONFIG_MATHEMU),y) - CORE_FILES += arch/sparc64/math-emu/math-emu.o -endif +CORE_FILES += arch/sparc64/math-emu/math-emu.o LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc64/prom/promlib.a \ $(TOPDIR)/arch/sparc64/lib/lib.a diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index 4461cdea0..8cd86f9db 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.36 1998/01/10 19:04:30 ecd Exp $ +# $Id: config.in,v 1.44 1998/04/06 16:10:35 jj Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -67,9 +67,6 @@ fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Solaris binary emulation' CONFIG_SOLARIS_EMUL fi -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Kernel Quad FPU (long double) and subnormal float/double emulation' CONFIG_MATHEMU -fi endmenu if [ "$CONFIG_PCI" = "y" ]; then @@ -84,6 +81,7 @@ if [ "$CONFIG_PCI" = "y" ]; then bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK fi fi + tristate 'SUNW,envctrl support' CONFIG_ENVCTRL fi mainmenu_option next_comment @@ -109,14 +107,16 @@ tristate 'Network block device support' CONFIG_BLK_DEV_NBD if [ "$CONFIG_PCI" = "y" ]; then tristate 'Ultra/PCI IDE disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE - if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then + if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE - define_bool CONFIG_IDE_CHIPSETS y - define_bool CONFIG_BLK_DEV_NS87415_AX y + define_bool CONFIG_BLK_DEV_IDEPCI y + define_bool CONFIG_BLK_DEV_IDEDMA y + define_bool CONFIG_BLK_DEV_NS87415 y + define_bool CONFIG_BLK_DEV_CMD646 y fi fi @@ -218,17 +218,6 @@ if [ "$CONFIG_NET" = "y" ]; then endmenu fi -# Conditionally compile in the Uniform CD-ROM driver -if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then - define_bool CONFIG_CDROM y -else - if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then - define_bool CONFIG_CDROM m - else - define_bool CONFIG_CDROM n - fi -fi - source fs/Config.in source fs/nls/Config.in diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index f3c2e2ca4..0a76f100f 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -25,12 +25,12 @@ CONFIG_SBUSCHAR=y CONFIG_SUN_MOUSE=y CONFIG_SERIAL=y CONFIG_SUN_SERIAL=y +CONFIG_SERIAL_CONSOLE=y CONFIG_SUN_KEYBOARD=y CONFIG_SUN_CONSOLE=y CONFIG_SUN_AUXIO=y CONFIG_SUN_IO=y CONFIG_PCI=y -CONFIG_PCI_OLD_PROC=y # # SBUS Frame Buffer support @@ -52,10 +52,11 @@ SUN_FB_CREATOR=y CONFIG_SUN_OPENPROMIO=m CONFIG_SUN_MOSTEK_RTC=y CONFIG_SAB82532=y -# CONFIG_OBP_FLASH is not set +CONFIG_OBP_FLASH=m # CONFIG_SUN_BPP is not set # CONFIG_SUN_VIDEOPIX is not set CONFIG_SUN_OPENPROMFS=m +CONFIG_PCI_OLD_PROC=y CONFIG_NET=y CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set @@ -67,11 +68,12 @@ CONFIG_BINFMT_AOUT32=y CONFIG_BINFMT_MISC=m CONFIG_BINFMT_JAVA=m CONFIG_SOLARIS_EMUL=m -CONFIG_MATHEMU=m CONFIG_PARPORT=y CONFIG_PARPORT_AX=y +# CONFIG_PARPORT_OTHER is not set CONFIG_PRINTER=y CONFIG_PRINTER_READBACK=y +CONFIG_ENVCTRL=y # # Floppy, IDE, and other block devices @@ -92,8 +94,10 @@ CONFIG_BLK_DEV_IDECD=y CONFIG_BLK_DEV_IDETAPE=m CONFIG_BLK_DEV_IDEFLOPPY=m # CONFIG_BLK_DEV_IDESCSI is not set -CONFIG_IDE_CHIPSETS=y -CONFIG_BLK_DEV_NS87415_AX=y +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_BLK_DEV_IDEDMA=y +CONFIG_BLK_DEV_NS87415=y +CONFIG_BLK_DEV_CMD646=y # # Networking options @@ -101,18 +105,18 @@ CONFIG_BLK_DEV_NS87415_AX=y CONFIG_PACKET=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set +CONFIG_NET_ALIAS=y +# CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set # CONFIG_IP_PNP is not set # CONFIG_IP_ACCT is not set -# CONFIG_IP_MASQUERADE is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set -# CONFIG_IP_ALIAS is not set +CONFIG_IP_ALIAS=y # CONFIG_SYN_COOKIES is not set # @@ -123,30 +127,34 @@ CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y CONFIG_IPV6=m # CONFIG_IPV6_EUI64 is not set -# CONFIG_IPV6_NO_PB is not set # # # CONFIG_IPX=m + +# +# IPX options +# # CONFIG_IPX_INTERN is not set CONFIG_ATALK=m -# 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_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set CONFIG_NET_SCHED=y CONFIG_NET_SCH_CBQ=y CONFIG_NET_SCH_CSZ=y -CONFIG_NET_SCH_HFQ=y CONFIG_NET_SCH_RED=y CONFIG_NET_SCH_SFQ=y CONFIG_NET_SCH_TBF=y CONFIG_NET_SCH_PFIFO=y CONFIG_NET_SCH_PRIO=y +# CONFIG_NET_PROFILE is not set # # SCSI support @@ -187,6 +195,21 @@ CONFIG_SCSI_NCR53C8XX_SYNC=10 # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set # +# Fibre Channel support +# +CONFIG_FC4=m + +# +# FC4 drivers +# +CONFIG_FC4_SOC=m + +# +# FC4 targets +# +CONFIG_SCSI_PLUTO=m + +# # Network device support # CONFIG_NETDEVICES=y @@ -205,7 +228,6 @@ CONFIG_HAPPYMEAL=y CONFIG_SUNQE=m CONFIG_MYRI_SBUS=m CONFIG_DE4X5=y -CONFIG_CDROM=y # # Filesystems @@ -228,21 +250,31 @@ CONFIG_LOCKD=y CONFIG_SMB_FS=m CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set CONFIG_HPFS_FS=m +# CONFIG_NTFS_FS is not set CONFIG_SYSV_FS=m CONFIG_AFFS_FS=m +# CONFIG_HFS_FS is not set CONFIG_ROMFS_FS=m CONFIG_AUTOFS_FS=m CONFIG_AMIGA_PARTITION=y CONFIG_UFS_FS=m CONFIG_BSD_DISKLABEL=y CONFIG_SMD_DISKLABEL=y +CONFIG_SOLARIS_X86_PARTITION=y +# CONFIG_ADFS_FS is not set # CONFIG_MAC_PARTITION is not set +CONFIG_NLS=y # # Native Language Support # -CONFIG_NLS=y # CONFIG_NLS_CODEPAGE_437 is not set # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 7889c84a4..1c3f459bc 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.35 1997/09/20 21:48:58 davem Exp $ +# $Id: Makefile,v 1.36 1998/02/01 11:15:55 ecd Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -61,13 +61,24 @@ head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S etrap.S rtrap.S \ binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c check_asm: dummy + @echo "/* Automatically generated. Do not edit. */" > asm_offsets.h + @echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h + @echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#ifndef __SMP__" >> asm_offsets.h + @echo "" >> asm_offsets.h @echo "#include <linux/sched.h>" > tmp.c $(CC) -E tmp.c -o tmp.i - @echo "/* Automatically generated. Do not edit. */" > check_asm.c; echo "#include <linux/sched.h>" >> check_asm.c; echo 'struct task_struct _task; struct mm_struct _mm; struct thread_struct _thread; int main(void) { printf ("/* Automatically generated. Do not edit. */\n#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n\n");' >> check_asm.c + @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/sched.h>" >> check_asm.c + @echo 'struct task_struct _task;' >> check_asm.c + @echo 'struct mm_struct _mm;' >> check_asm.c + @echo 'struct thread_struct _thread;' >> check_asm.c + @echo 'int main(void) {' >> check_asm.c $(SH) ./check_asm.sh task tmp.i check_asm.c $(SH) ./check_asm.sh mm tmp.i check_asm.c $(SH) ./check_asm.sh thread tmp.i check_asm.c - @echo 'printf ("\n#endif /* __ASM_OFFSETS_H__ */\n"); return 0; }' >> check_asm.c + @echo 'return 0; }' >> check_asm.c @rm -f tmp.[ci] #$(CC) -o check_asm check_asm.c # <hack> Until we can do this natively, a hack has to take place @@ -75,9 +86,46 @@ check_asm: dummy $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s @rm -f check_asm.s # </hack> - ./check_asm > asm_offsets.h - @if test -r $(HPATH)/asm/asm_offsets.h; then if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then echo $(HPATH)/asm/asm_offsets.h is unchanged; rm -f asm_offsets.h; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi + ./check_asm >> asm_offsets.h @rm -f check_asm check_asm.c + @echo "" >> asm_offsets.h + @echo "#else /* __SMP__ */" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#include <linux/sched.h>" > tmp.c + $(CC) -D__SMP__ -E tmp.c -o tmp.i + @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/sched.h>" >> check_asm.c + @echo 'struct task_struct _task;' >> check_asm.c + @echo 'struct mm_struct _mm;' >> check_asm.c + @echo 'struct thread_struct _thread;' >> check_asm.c + @echo 'int main(void) {' >> check_asm.c + $(SH) ./check_asm.sh task tmp.i check_asm.c + $(SH) ./check_asm.sh mm tmp.i check_asm.c + $(SH) ./check_asm.sh thread tmp.i check_asm.c + @echo 'return 0; }' >> check_asm.c + @rm -f tmp.[ci] + #$(CC) -D__SMP__ -o check_asm check_asm.c + # <hack> Until we can do this natively, a hack has to take place + $(CC) -D__SMP__ -mmedlow -ffixed-g4 -S -o check_asm.s check_asm.c + $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s + @rm -f check_asm.s + # </hack> + ./check_asm >> asm_offsets.h + @rm -f check_asm check_asm.c + @echo "" >> asm_offsets.h + @echo "#endif /* __SMP__ */" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h + @if test -r $(HPATH)/asm/asm_offsets.h; then \ + if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then \ + echo $(HPATH)/asm/asm_offsets.h is unchanged; \ + rm -f asm_offsets.h; \ + else \ + mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \ + fi; \ + else \ + mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \ + fi include $(TOPDIR)/Rules.make diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c index 55ccbc203..b0dd675b0 100644 --- a/arch/sparc64/kernel/binfmt_aout32.c +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -20,6 +20,7 @@ #include <linux/string.h> #include <linux/stat.h> #include <linux/fcntl.h> +#include <linux/file.h> #include <linux/ptrace.h> #include <linux/user.h> #include <linux/malloc.h> @@ -257,7 +258,7 @@ static inline int do_load_aout32_binary(struct linux_binprm * bprm, unsigned long p = bprm->p; unsigned long fd_offset; unsigned long rlim; -int retval; + int retval; ex = *((struct exec *) bprm->buf); /* exec-header */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && @@ -326,10 +327,10 @@ int retval; printk(KERN_NOTICE "executable not page aligned\n"); fd = open_dentry(bprm->dentry, O_RDONLY); - if (fd < 0) return fd; - file = current->files->fd[fd]; + file = fcheck(fd); + if (!file->f_op || !file->f_op->mmap) { sys_close(fd); do_mmap(NULL, 0, ex.a_text+ex.a_data, @@ -397,6 +398,7 @@ load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) return retval; } +/* N.B. Move to .h file and use code in fs/binfmt_aout.c? */ static inline int do_load_aout32_library(int fd) { @@ -409,7 +411,7 @@ do_load_aout32_library(int fd) unsigned int start_addr; unsigned long error; - file = current->files->fd[fd]; + file = fcheck(fd); if (!file || !file->f_op) return -EACCES; diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c index 817a8ecd3..a54e89f2d 100644 --- a/arch/sparc64/kernel/central.c +++ b/arch/sparc64/kernel/central.c @@ -1,4 +1,4 @@ -/* $Id: central.c,v 1.4 1997/08/19 14:17:49 jj Exp $ +/* $Id: central.c,v 1.5 1998/02/12 15:57:59 jj Exp $ * central.c: Central FHC driver for Sunfire/Starfire/Wildfire. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -35,22 +35,18 @@ unsigned long central_probe(unsigned long memory_start) printk("no central found.\n"); return memory_start; } - prom_printf("CENTRAL: found central PROM node.\n"); printk("found central PROM node.\n"); /* Ok we got one, grab some memory for software state. */ memory_start = long_align(memory_start); central_bus = (struct linux_central *) (memory_start); - prom_printf("CENTRAL: central_bus[%p] ", central_bus); memory_start += sizeof(struct linux_central); memory_start = long_align(memory_start); fhc = (struct linux_fhc *)(memory_start); memory_start += sizeof(struct linux_fhc); memory_start = long_align(memory_start); - prom_printf("fhc[%p] ", fhc); - /* First init central. */ central_bus->child = fhc; central_bus->prom_node = cnode; @@ -58,7 +54,6 @@ unsigned long central_probe(unsigned long memory_start) prom_getstring(cnode, "name", namebuf, sizeof(namebuf)); strcpy(central_bus->prom_name, namebuf); - prom_printf("init_central_ranges "); prom_central_ranges_init(cnode, central_bus); /* And then central's FHC. */ @@ -73,27 +68,15 @@ unsigned long central_probe(unsigned long memory_start) prom_getstring(fnode, "name", namebuf, sizeof(namebuf)); strcpy(fhc->prom_name, namebuf); - prom_printf("cnode[%x] fnode[%x] init_fhc_ranges\n", cnode, fnode); prom_fhc_ranges_init(fnode, fhc); - /* Finally, map in FHC register set. (From the prtconf dumps - * I have seen on Ex000 boxes only the central ranges need to - * be applied to the fhc internal register set) -DaveM - */ - err = prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)); - if(err == -1) { + /* Finally, map in FHC register set. */ + if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1) { prom_printf("CENTRAL: fatal error, cannot get fhc regs.\n"); prom_halt(); } prom_apply_central_ranges(central_bus, &fpregs[0], 6); - prom_printf("CENTRAL: FHC_REGS[(%08x,%08x) (%08x,%08x) " - "(%08x,%08x) (%08x,%08x) (%08x,%08x) (%08x,%08x)]\n", - fpregs[0].which_io, fpregs[0].phys_addr, - fpregs[1].which_io, fpregs[1].phys_addr, - fpregs[2].which_io, fpregs[2].phys_addr, - fpregs[3].which_io, fpregs[3].phys_addr, - fpregs[4].which_io, fpregs[4].phys_addr, - fpregs[5].which_io, fpregs[5].phys_addr); + fhc->fhc_regs.pregs = (struct fhc_internal_regs *) __va((((unsigned long)fpregs[0].which_io)<<32) | (((unsigned long)fpregs[0].phys_addr))); @@ -112,14 +95,8 @@ unsigned long central_probe(unsigned long memory_start) fhc->fhc_regs.tregs = (struct fhc_tod_regs *) __va((((unsigned long)fpregs[5].which_io)<<32) | (((unsigned long)fpregs[5].phys_addr))); - prom_printf("CENTRAL: FHC_REGS[%p %p %p %p %p %p]\n", - fhc->fhc_regs.pregs, fhc->fhc_regs.ireg, - fhc->fhc_regs.ffregs, fhc->fhc_regs.sregs, - fhc->fhc_regs.uregs, fhc->fhc_regs.tregs); - prom_printf("CENTRAL: reading FHC_ID register... "); err = fhc->fhc_regs.pregs->fhc_id; - prom_printf("VALUE[%x]\n", err); printk("FHC Version[%x] PartID[%x] Manufacturer[%x]\n", ((err & FHC_ID_VERS) >> 28), ((err & FHC_ID_PARTID) >> 12), diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index d009f39d8..86efc4bb7 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -32,7 +32,8 @@ struct cpu_fp_info linux_sparc_fpu[] = { { 0x17, 0x10, 0, "UltraSparc I integrated FPU"}, { 0x22, 0x10, 0, "UltraSparc II integrated FPU"}, { 0x17, 0x11, 0, "UltraSparc II integrated FPU"}, - { 0x17, 0x12, 0, "UltraSparc III integrated FPU"}, + { 0x17, 0x12, 0, "UltraSparc IIi integrated FPU"}, + { 0x17, 0x13, 0, "UltraSparc III integrated FPU"}, }; #define NSPARCFPU (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info)) @@ -41,7 +42,8 @@ struct cpu_iu_info linux_sparc_chips[] = { { 0x17, 0x10, "TI UltraSparc I (SpitFire)"}, { 0x22, 0x10, "TI UltraSparc II (BlackBird)"}, { 0x17, 0x11, "TI UltraSparc II (BlackBird)"}, - { 0x17, 0x12, "TI UltraSparc III (Cheetah)"}, /* A guess... */ + { 0x17, 0x12, "TI UltraSparc IIi"}, + { 0x17, 0x13, "TI UltraSparc III (Cheetah)"}, /* A guess... */ }; #define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info)) diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index 24ca3ff10..8d3aca325 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c @@ -13,7 +13,8 @@ #include <asm/system.h> #include <asm/smp.h> -struct prom_cpuinfo linux_cpus[NR_CPUS]; +struct prom_cpuinfo linux_cpus[NR_CPUS] __initdata = { { 0 } }; +unsigned prom_cpu_nodes[NR_CPUS]; int linux_num_cpus = 0; extern void cpu_probe(void); @@ -64,6 +65,8 @@ device_scan(unsigned long mem_start)) prom_node_cpu = cpu_nds[0]; linux_num_cpus = cpu_ctr; + + prom_cpu_nodes[0] = prom_node_cpu; cpu_probe(); return central_probe(mem_start); diff --git a/arch/sparc64/kernel/dtlb_miss.S b/arch/sparc64/kernel/dtlb_miss.S index 4d71d967c..e5606cf33 100644 --- a/arch/sparc64/kernel/dtlb_miss.S +++ b/arch/sparc64/kernel/dtlb_miss.S @@ -1,9 +1,9 @@ -/* $Id: dtlb_miss.S,v 1.14 1997/10/14 01:48:28 davem Exp $ +/* $Id: dtlb_miss.S,v 1.15 1998/01/14 17:14:44 jj Exp $ * dtlb_miss.S: Data TLB miss code, this is included directly * into the trap table. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* The basic algorithm is: @@ -36,22 +36,22 @@ #define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) #define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W) -#define KERN_LOWBITS_IO ((_PAGE_E | _PAGE_P | _PAGE_W) ^ KERN_LOWBITS) +#define KERN_LOWBITS_IO (_PAGE_E | _PAGE_P | _PAGE_W) /* ICACHE line 1 */ /*0x00*/ ldxa [%g0] ASI_DMMU, %g1 ! Get TAG_TARGET - /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset - /*0x08*/ srlx %g1, 48, %g5 ! Shift down CONTEXT bits - /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset - /*0x10*/ sllx %g1, 2, %g4 ! Position PMD offset - /*0x14*/ brz,pn %g5, 3f ! Context 0 == kernel - /*0x18*/ and %g4, %g2, %g4 ! Mask PMD offset + /*0x04*/ srlx %g1, 10, %g3 ! Position PGD offset + /*0x08*/ andcc %g1, %g2, %g0 ! Test CONTEXT bits + /*0x0c*/ and %g3, 0xffc, %g3 ! Mask PGD offset + /*0x18*/ and %g1, 0xffe, %g4 ! Mask PMD offset + /*0x14*/ be,pn %xcc, 3f ! Context 0 == kernel + /*0x10*/ add %g4, %g4, %g4 ! Position PMD offset /*0x1c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset /* ICACHE line 2 */ - /*0x20*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD + /*0x20*/ lduwa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD /*0x24*/ srlx %g1, 1, %g1 ! PTE offset -2:/*0x28*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD +2:/*0x28*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD /*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE /*0x30*/ brgez,pn %g5, sparc64_dtlb_refbit_catch ! Valid set? /*0x34*/ nop ! delay @@ -61,22 +61,22 @@ 3: /* ICACHE line 3 */ /*0x40*/ sllx %g1, 22, %g5 ! This is now physical page + PAGE_OFFSET /*0x44*/ brgez,pn %g5, 4f ! If >= 0, then walk down page tables - /*0x48*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE ^ PAGE_OFFSET - /*0x4c*/ andcc %g3, 0x400, %g0 ! Slick trick... - /*0x50*/ sllx %g1, 32, %g1 ! Move high bits up - /*0x54*/ or %g1, (KERN_LOWBITS), %g1 ! Assume not IO - /*0x58*/ bne,a,pn %icc, 5f ! Is it an IO page? - /*0x5c*/ xor %g1, (KERN_LOWBITS_IO), %g1 ! Aha, it is IO... + /*0x48*/ or %g2, (KERN_LOWBITS), %g1 ! Construct PTE ^ PAGE_OFFSET + /*0x4c*/ andcc %g3, 0x100, %g0 ! Slick trick... + /*0x50*/ bne,a,pn %icc, 5f ! Is it an IO page? + /*0x54*/ or %g2, (KERN_LOWBITS_IO), %g1 ! Aha, it is IO... +5:/*0x58*/ xor %g1, %g5, %g1 ! Slick trick II... + /*0x5c*/ stxa %g1, [%g0] ASI_DTLB_DATA_IN ! TLB load /* ICACHE line 4 */ -5:/*0x60*/ xor %g1, %g5, %g1 ! Slick trick II... - /*0x64*/ stxa %g1, [%g0] ASI_DTLB_DATA_IN ! TLB load - /*0x68*/ retry ! Trap return -4:/*0x6c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset - /*0x70*/ ldxa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD - /*0x74*/ ba,pt %xcc, 2b ! Go back up top - /*0x78*/ srlx %g1, 1, %g1 - /*0x7c*/ nop + /*0x60*/ retry ! Trap return + /*0x64*/ nop + /*0x68*/ nop + /*0x6c*/ nop +4:/*0x70*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset + /*0x74*/ lduwa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD + /*0x78*/ ba,pt %xcc, 2b ! Go back up top + /*0x7c*/ srlx %g1, 1, %g1 #undef KERN_HIGHBITS #undef KERN_LOWBITS diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc64/kernel/dtlb_prot.S index 55e86c887..86cbfdc52 100644 --- a/arch/sparc64/kernel/dtlb_prot.S +++ b/arch/sparc64/kernel/dtlb_prot.S @@ -1,9 +1,9 @@ -/* $Id: dtlb_prot.S,v 1.14 1997/08/03 09:07:00 davem Exp $ +/* $Id: dtlb_prot.S,v 1.15 1998/01/14 17:14:46 jj Exp $ * dtlb_prot.S: Data TLB protection code, this is included directly * into the trap table. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* We know kernel never takes protection trap, @@ -15,12 +15,12 @@ /* ICACHE line 1 */ /*0x00*/ ldxa [%g0] ASI_DMMU, %g1 ! Get TAG_TARGET - /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset - /*0x08*/ sllx %g1, 2, %g4 ! Position PMD offset - /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset - /*0x10*/ and %g4, %g2, %g4 ! Mask PMD offset - /*0x14*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD - /*0x18*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g4 ! Load PMD + /*0x04*/ srlx %g1, 10, %g3 ! Position PGD offset + /*0x08*/ and %g1, 0xffe, %g4 ! Mask PMD offset + /*0x0c*/ and %g3, 0xffc, %g3 ! Mask PGD offset + /*0x10*/ add %g4, %g4, %g4 ! Position PMD offset + /*0x14*/ lduwa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD + /*0x18*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g4 ! Load PMD /*0x1c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset /* ICACHE line 2 */ @@ -34,10 +34,10 @@ /*0x3c*/ ldxa [%g5] ASI_DMMU, %g4 ! From MMU /* ICACHE line 3 */ - /*0x40*/ add %g2, 7, %g5 ! Compute mask - /*0x44*/ andn %g4, %g5, %g4 ! Mask page - /*0x48*/ mov TLB_SFSR, %g5 ! read SFSR - /*0x4c*/ ldxa [%g5] ASI_DMMU, %g5 ! from DMMU for + /*0x40*/ mov TLB_SFSR, %g5 ! read SFSR + /*0x44*/ srlx %g4, 13, %g4 ! Prepare... + /*0x48*/ ldxa [%g5] ASI_DMMU, %g5 ! from DMMU for + /*0x4c*/ sllx %g4, 13, %g4 ! ...and mask page /*0x50*/ and %g5, 0x10, %g5 ! context bit /*0x54*/ or %g4, %g5, %g4 ! for prot trap 1:/*0x58*/ stxa %g0, [%g4] ASI_DMMU_DEMAP ! TLB flush page diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index 02faf4e3c..954cfd4bc 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.17 1998/01/10 18:26:13 ecd Exp $ +/* $Id: ebus.c,v 1.23 1998/03/29 16:27:24 ecd Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -29,14 +29,12 @@ struct linux_ebus *ebus_chain = 0; extern void prom_ebus_ranges_init(struct linux_ebus *); +extern void prom_ebus_intmap_init(struct linux_ebus *); extern unsigned long pci_console_init(unsigned long memory_start); #ifdef CONFIG_SUN_OPENPROMIO extern int openprom_init(void); #endif -#ifdef CONFIG_SUN_MOSTEK_RTC -extern int rtc_init(void); -#endif #ifdef CONFIG_SPARCAUDIO extern int sparcaudio_init(void); #endif @@ -46,6 +44,9 @@ extern void auxio_probe(void); #ifdef CONFIG_OBP_FLASH extern int flash_init(void); #endif +#ifdef CONFIG_ENVCTRL +extern int envctrl_init(void); +#endif extern unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino); @@ -62,7 +63,35 @@ ebus_alloc(unsigned long *memory_start, size_t size) return mem; } -__initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev)) +__initfunc(void ebus_intmap_match(struct linux_ebus *ebus, + struct linux_prom_registers *reg, + int *interrupt)) +{ + unsigned int hi, lo, irq; + int i; + + if (!ebus->num_ebus_intmap) + return; + + hi = reg->which_io & ebus->ebus_intmask.phys_hi; + lo = reg->phys_addr & ebus->ebus_intmask.phys_lo; + irq = *interrupt & ebus->ebus_intmask.interrupt; + for (i = 0; i < ebus->num_ebus_intmap; i++) { + if ((ebus->ebus_intmap[i].phys_hi == hi) && + (ebus->ebus_intmap[i].phys_lo == lo) && + (ebus->ebus_intmap[i].interrupt == irq)) { + *interrupt = ebus->ebus_intmap[i].cinterrupt; + return; + } + } + + prom_printf("ebus: IRQ [%08x.%08x.%08x] not found in interrupt-map\n", + reg->which_io, reg->phys_addr, *interrupt); + prom_halt(); +} + +__initfunc(void fill_ebus_child(int node, struct linux_prom_registers *preg, + struct linux_ebus_child *dev)) { int regs[PROMREG_MAX]; int irqs[PROMREG_MAX]; @@ -90,8 +119,10 @@ __initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev)) dev->num_irqs = 0; } else { dev->num_irqs = len / sizeof(irqs[0]); - for (i = 0; i < dev->num_irqs; i++) + for (i = 0; i < dev->num_irqs; i++) { + ebus_intmap_match(dev->bus, preg, &irqs[i]); dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]); + } } #ifdef DEBUG_FILL_EBUS_DEV @@ -108,7 +139,8 @@ __initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev)) #endif } -__initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *dev, +__initfunc(unsigned long fill_ebus_device(int node, + struct linux_ebus_device *dev, unsigned long memory_start)) { struct linux_prom_registers regs[PROMREG_MAX]; @@ -142,8 +174,10 @@ __initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *de dev->num_irqs = 0; } else { dev->num_irqs = len / sizeof(irqs[0]); - for (i = 0; i < dev->num_irqs; i++) + for (i = 0; i < dev->num_irqs; i++) { + ebus_intmap_match(dev->bus, ®s[0], &irqs[i]); dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]); + } } #ifdef DEBUG_FILL_EBUS_DEV @@ -166,7 +200,7 @@ __initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *de child->next = 0; child->parent = dev; child->bus = dev->bus; - fill_ebus_child(node, child); + fill_ebus_child(node, ®s[0], child); while ((node = prom_getsibling(node))) { child->next = (struct linux_ebus_child *) @@ -176,13 +210,16 @@ __initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *de child->next = 0; child->parent = dev; child->bus = dev->bus; - fill_ebus_child(node, child); + fill_ebus_child(node, ®s[0], child); } } return memory_start; } +extern void sun4u_start_timers(void); +extern void clock_probe(void); + __initfunc(unsigned long ebus_init(unsigned long memory_start, unsigned long memory_end)) { @@ -199,14 +236,10 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, int reg, rng, nreg; int num_ebus = 0; - if (!pcibios_present()) + if (!pci_present()) return memory_start; - for (pdev = pci_devices; pdev; pdev = pdev->next) { - if ((pdev->vendor == PCI_VENDOR_ID_SUN) && - (pdev->device == PCI_DEVICE_ID_SUN_EBUS)) - break; - } + pdev = pci_find_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, 0); if (!pdev) { printk("ebus: No EBus's found.\n"); #ifdef PROM_DEBUG @@ -236,11 +269,9 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, ebus->parent = pbm = cookie->pbm; /* Enable BUS Master. */ - pcibios_read_config_word(pdev->bus->number, pdev->devfn, - PCI_COMMAND, &pci_command); + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pdev->bus->number, pdev->devfn, - PCI_COMMAND, pci_command); + pci_write_config_word(pdev, PCI_COMMAND, pci_command); len = prom_getproperty(ebusnd, "reg", (void *)regs, sizeof(regs)); @@ -285,6 +316,7 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, #endif prom_ebus_ranges_init(ebus); + prom_ebus_intmap_init(ebus); nd = prom_getchild(ebusnd); if (!nd) @@ -312,11 +344,8 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, } next_ebus: - for (pdev = pdev->next; pdev; pdev = pdev->next) { - if ((pdev->vendor == PCI_VENDOR_ID_SUN) && - (pdev->device == PCI_DEVICE_ID_SUN_EBUS)) - break; - } + pdev = pci_find_device(PCI_VENDOR_ID_SUN, + PCI_DEVICE_ID_SUN_EBUS, pdev); if (!pdev) break; @@ -335,9 +364,6 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, #ifdef CONFIG_SUN_OPENPROMIO openprom_init(); #endif -#ifdef CONFIG_SUN_MOSTEK_RTC - rtc_init(); -#endif #ifdef CONFIG_SPARCAUDIO sparcaudio_init(); #endif @@ -345,20 +371,15 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, bpp_init(); #endif #ifdef CONFIG_SUN_AUXIO - if (sparc_cpu_model == sun4u) - auxio_probe(); + auxio_probe(); +#endif +#ifdef CONFIG_ENVCTRL + envctrl_init(); #endif #ifdef CONFIG_OBP_FLASH flash_init(); #endif -#ifdef __sparc_v9__ - if (sparc_cpu_model == sun4u) { - extern void sun4u_start_timers(void); - extern void clock_probe(void); - - sun4u_start_timers(); - clock_probe(); - } -#endif + sun4u_start_timers(); + clock_probe(); return memory_start; } diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index 43f950b25..c0531f30a 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -1,9 +1,9 @@ -/* $Id: head.S,v 1.46 1997/08/08 08:33:30 jj Exp $ +/* $Id: head.S,v 1.49 1998/03/03 12:31:17 jj Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 David Sitsky (David.Sitsky@anu.edu.au) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx) */ @@ -26,7 +26,7 @@ /* This section from from _start to sparc64_boot_end should fit into * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space * with bootup_user_stack, which is from 0x0000.0000.0040.4000 to - * 0x0000.0000.0040.6000 and bootup_kernel_stack, which is from + * 0x0000.0000.0040.6000 and empty_bad_page, which is from * 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000. */ @@ -326,6 +326,8 @@ sun4u_init: nop /* Not reached... */ +/* IMPORTANT NOTE: Whenever making changes here, check + * trampoline.S as well. -jj */ .globl setup_tba setup_tba: save %sp, -160, %sp @@ -346,9 +348,11 @@ setup_tba: /* Set up MMU globals */ wrpr %o1, (PSTATE_MG|PSTATE_IE), %pstate - /* PGD/PMD offset mask, used by TLB miss handlers. */ - sethi %hi(0x1ff8), %g2 - or %g2, %lo(0x1ff8), %g2 + /* Set KERN_HIGHBITS used by dTLB miss handler. */ +#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) + sethi %uhi(KERN_HIGHBITS), %g2 + sllx %g2, 32, %g2 +#undef KERN_HIGHBITS /* Kernel PGDIR used by TLB miss handlers. */ mov %i0, %g6 @@ -391,7 +395,8 @@ sparc64_boot_end: .skip 0x2000 + _start - sparc64_boot_end bootup_user_stack_end: -bootup_kernel_stack: + .globl empty_bad_page +empty_bad_page: .skip 0x2000 ! 0x0000000000408000 diff --git a/arch/sparc64/kernel/init_task.c b/arch/sparc64/kernel/init_task.c index 1829daeea..86b6c3dd6 100644 --- a/arch/sparc64/kernel/init_task.c +++ b/arch/sparc64/kernel/init_task.c @@ -6,7 +6,7 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct files * init_fd_array[NR_OPEN] = { NULL, }; +static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 64465663d..17e904f25 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.26 1997/12/15 15:11:02 jj Exp $ +/* $Id: ioctl32.c,v 1.35 1998/04/10 02:01:46 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -23,9 +23,11 @@ #include <linux/netlink.h> #include <linux/vt.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/fd.h> #include <linux/if_ppp.h> #include <linux/mtio.h> +#include <linux/cdrom.h> #include <scsi/scsi.h> /* Ugly hack. */ @@ -64,6 +66,30 @@ static int w_long(unsigned int fd, unsigned int cmd, u32 arg) return err; } +struct timeval32 { + int tv_sec; + int tv_usec; +}; + +static int do_siocgstamp(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct timeval32 *up = (struct timeval32 *)A(arg); + struct timeval ktv; + mm_segment_t old_fs = get_fs(); + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&ktv); + set_fs(old_fs); + if(!err) { + if(!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __put_user(ktv.tv_sec, &up->tv_sec) || + __put_user(ktv.tv_usec, &up->tv_usec)) + err = -EFAULT; + } + return err; +} + struct ifmap32 { u32 mem_start; u32 mem_end; @@ -948,6 +974,90 @@ static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg) return 0; } +struct cdrom_read32 { + int cdread_lba; + __kernel_caddr_t32 cdread_bufaddr; + int cdread_buflen; +}; + +struct cdrom_read_audio32 { + union cdrom_addr addr; + u_char addr_format; + int nframes; + __kernel_caddr_t32 buf; +}; + +static int cdrom_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg) +{ + mm_segment_t old_fs = get_fs(); + struct cdrom_read cdread; + struct cdrom_read_audio cdreadaudio; + __kernel_caddr_t32 addr; + char *data = 0; + void *karg; + int err = 0; + + switch(cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + karg = &cdread; + if (__get_user(cdread.cdread_lba, &((struct cdrom_read32 *)A(arg))->cdread_lba) || + __get_user(addr, &((struct cdrom_read32 *)A(arg))->cdread_bufaddr) || + __get_user(cdread.cdread_buflen, &((struct cdrom_read32 *)A(arg))->cdread_buflen)) + return -EFAULT; + data = kmalloc(cdread.cdread_buflen, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdread.cdread_bufaddr = data; + break; + case CDROMREADAUDIO: + karg = &cdreadaudio; + if (copy_from_user(&cdreadaudio.addr, &((struct cdrom_read_audio32 *)A(arg))->addr, sizeof(cdreadaudio.addr)) || + __get_user(cdreadaudio.addr_format, &((struct cdrom_read_audio32 *)A(arg))->addr_format) || + __get_user(cdreadaudio.nframes, &((struct cdrom_read_audio32 *)A(arg))->nframes) || + __get_user(addr, &((struct cdrom_read_audio32 *)A(arg))->buf)) + return -EFAULT; + data = kmalloc(cdreadaudio.nframes * 2352, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdreadaudio.buf = data; + break; + default: + printk("cdrom_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + if (err) { + if (data) kfree(data); + return err; + } + switch (cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + if (copy_to_user((char *)A(addr), data, cdread.cdread_buflen)) { + kfree(data); + return -EFAULT; + } + break; + case CDROMREADAUDIO: + if (copy_to_user((char *)A(addr), data, cdreadaudio.nframes * 2352)) { + kfree(data); + return -EFAULT; + } + break; + default: + break; + } + if (data) kfree(data); + return 0; +} asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) { @@ -955,10 +1065,7 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) int error = -EBADF; lock_kernel(); - if(fd >= NR_OPEN) - goto out; - - filp = current->files->fd[fd]; + filp = fcheck(fd); if(!filp) goto out; @@ -966,7 +1073,6 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) error = sys_ioctl (fd, cmd, (unsigned long)arg); goto out; } - error = -EFAULT; switch (cmd) { case SIOCGIFCONF: error = dev_ifconf(fd, arg); @@ -1014,6 +1120,11 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) error = -EINVAL; goto out; + case SIOCGSTAMP: + /* Sorry, timeval in the kernel is different now. */ + error = do_siocgstamp(fd, cmd, arg); + goto out; + case HDIO_GETGEO: error = hdio_getgeo(fd, arg); goto out; @@ -1066,6 +1177,15 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) error = mt_ioctl_trans(fd, cmd, arg); goto out; + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + case CDROMREADAUDIO: + case CDROMREADALL: + error = cdrom_ioctl_trans(fd, cmd, arg); + goto out; + /* List here exlicitly which ioctl's are known to have * compatable types passed or none at all... */ @@ -1170,6 +1290,7 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) /* 0x09 */ case REGISTER_DEV: + case REGISTER_DEV_NEW: case START_MD: case STOP_MD: @@ -1219,6 +1340,7 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case SCSI_IOCTL_TAGGED_ENABLE: case SCSI_IOCTL_TAGGED_DISABLE: case SCSI_IOCTL_GET_BUS_NUMBER: + case SCSI_IOCTL_SEND_COMMAND: /* Big V */ case VT_SETMODE: @@ -1267,7 +1389,6 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case FIOGETOWN: case SIOCGPGRP: case SIOCATMARK: - case SIOCGSTAMP: case SIOCSIFLINK: case SIOCSIFENCAP: case SIOCGIFENCAP: @@ -1305,6 +1426,36 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case PPPIOCSNPMODE: case PPPIOCGDEBUG: case PPPIOCSDEBUG: + + /* CDROM stuff */ + case CDROMPAUSE: + case CDROMRESUME: + case CDROMPLAYMSF: + case CDROMPLAYTRKIND: + case CDROMREADTOCHDR: + case CDROMREADTOCENTRY: + case CDROMSTOP: + case CDROMSTART: + case CDROMEJECT: + case CDROMVOLCTRL: + case CDROMSUBCHNL: + case CDROMEJECT_SW: + case CDROMMULTISESSION: + case CDROM_GET_MCN: + case CDROMRESET: + case CDROMVOLREAD: + case CDROMSEEK: + case CDROMPLAYBLK: + case CDROMCLOSETRAY: + case CDROM_SET_OPTIONS: + case CDROM_CLEAR_OPTIONS: + case CDROM_SELECT_SPEED: + case CDROM_SELECT_DISC: + case CDROM_MEDIA_CHANGED: + case CDROM_DRIVE_STATUS: + case CDROM_DISC_STATUS: + case CDROM_CHANGER_NSLOTS: + error = sys_ioctl (fd, cmd, (unsigned long)arg); goto out; @@ -1312,7 +1463,6 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) printk("sys32_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n", (int)fd, (unsigned int)cmd, (unsigned int)arg); error = -EINVAL; - goto out; break; } out: diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index a84fe8eaa..176079643 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,7 +1,8 @@ -/* $Id: irq.c,v 1.47 1998/01/10 18:26:17 ecd Exp $ +/* $Id: irq.c,v 1.52 1998/03/19 00:22:54 ecd Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * - * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) */ #include <linux/config.h> @@ -98,12 +99,22 @@ int get_irq_list(char *buf) { int i, len = 0; struct irqaction *action; +#ifdef __SMP__ + int j; +#endif for(i = 0; i < (NR_IRQS + 1); i++) { if(!(action = *(i + irq_action))) continue; - len += sprintf(buf + len, "%2d: %8d %c %s", - i, kstat.interrupts[i], + len += sprintf(buf + len, "%3d: ", i); +#ifndef __SMP__ + len += sprintf(buf + len, "%10u ", kstat_irqs(i)); +#else + for (j = 0; j < smp_num_cpus; j++) + len += sprintf(buf + len, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#endif + len += sprintf(buf + len, "%c %s", (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for(action = action->next; action; action = action->next) { @@ -113,19 +124,6 @@ int get_irq_list(char *buf) } len += sprintf(buf + len, "\n"); } -#if 0 -#ifdef CONFIG_PCI - { - struct linux_psycho *p; - for (p = psycho_root; p; p = p->next) - len += sprintf(buf + len, - "ISTAT[%d]: PCI[%016lx] OBIO[%016lx]\n", - p->index, - p->psycho_regs->pci_istate, - p->psycho_regs->obio_istate); - } -#endif -#endif return len; } @@ -197,8 +195,7 @@ static unsigned int *sysio_irq_to_imap(unsigned int irq) unsigned long offset; struct sysio_regs *sregs; - if((irq == 14) || - (irq >= NUM_SYSIO_OFFSETS) || + if((irq >= NUM_SYSIO_OFFSETS) || ((offset = sysio_irq_offsets[irq]) == ((unsigned long)-1))) return NULL; sregs = SBus_chain->iommu->sysio_regs; @@ -224,8 +221,8 @@ static unsigned int *sysio_imap_to_iclr(unsigned int *imap) unsigned char psycho_ino_to_pil[] = { 7, 5, 5, 2, /* PCI A slot 0 Int A, B, C, D */ 7, 5, 5, 2, /* PCI A slot 1 Int A, B, C, D */ - 0, 0, 0, 0, - 0, 0, 0, 0, + 7, 5, 5, 2, /* PCI A slot 2 Int A, B, C, D */ + 7, 5, 5, 2, /* PCI A slot 3 Int A, B, C, D */ 6, 4, 3, 1, /* PCI B slot 0 Int A, B, C, D */ 6, 4, 3, 1, /* PCI B slot 1 Int A, B, C, D */ 6, 4, 3, 1, /* PCI B slot 2 Int A, B, C, D */ @@ -255,13 +252,13 @@ unsigned char psycho_ino_to_pil[] = { */ #define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x))) -#define psycho_imap_offset(ino) \ - ((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) : \ +#define psycho_imap_offset(ino) \ + ((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) : \ (psycho_offset(imap_a_slot0) + (((ino) & 0x3c) << 1))) -#define psycho_iclr_offset(ino) \ - ((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) : \ - (psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f) << 3))) +#define psycho_iclr_offset(ino) \ + ((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) : \ + (psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f)<<3))) #endif @@ -529,7 +526,7 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) unsigned long flags; unsigned int *imap, *iclr; void *bus_id = NULL; - int ivindex, ivindex_fixup, cpu_irq = -1, pending; + int ivindex = -1, ivindex_fixup, cpu_irq = -1, pending = 0; if(!handler) return -EINVAL; @@ -537,43 +534,47 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) imap = iclr = NULL; ivindex_fixup = 0; + + if (irq == 0) { + cpu_irq = irq; + irqflags &= ~(SA_IMAP_MASKED); + } else { + irqflags |= SA_IMAP_MASKED; #ifdef CONFIG_PCI - if(PCI_IRQ_P(irq)) { - pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq); - } else + if(PCI_IRQ_P(irq)) { + pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq); + } else #endif - if(irqflags & SA_DCOOKIE) { - if(!dev_id) { - printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n"); - panic("Bogus irq registry."); - } - dcookie = dev_id; - dev_id = dcookie->real_dev_id; - cpu_irq = dcookie->pil; - imap = dcookie->imap; - iclr = dcookie->iclr; - bus_id = dcookie->bus_cookie; - get_irq_translations(&cpu_irq, &ivindex_fixup, &imap, - &iclr, bus_id, irqflags, irq); - } else { - /* XXX NOTE: This code is maintained for compatability until I can - * XXX verify that all drivers sparc64 will use are updated - * XXX to use the new IRQ registry dcookie interface. -DaveM - */ - if(irq == 14) - cpu_irq = irq; - else + if(irqflags & SA_DCOOKIE) { + if(!dev_id) { + printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n"); + panic("Bogus irq registry."); + } + dcookie = dev_id; + dev_id = dcookie->real_dev_id; + cpu_irq = dcookie->pil; + imap = dcookie->imap; + iclr = dcookie->iclr; + bus_id = dcookie->bus_cookie; + get_irq_translations(&cpu_irq, &ivindex_fixup, &imap, + &iclr, bus_id, irqflags, irq); + } else { + /* XXX NOTE: This code is maintained for compatability until I can + * XXX verify that all drivers sparc64 will use are updated + * XXX to use the new IRQ registry dcookie interface. -DaveM + */ cpu_irq = sysio_ino_to_pil[irq]; - imap = sysio_irq_to_imap(irq); - if(!imap) { - printk("request_irq: BAD, null imap for old style " - "irq registry IRQ[%x].\n", irq); - panic("Bad IRQ registery..."); + imap = sysio_irq_to_imap(irq); + if(!imap) { + printk("request_irq: BAD, null imap for old style " + "irq registry IRQ[%x].\n", irq); + panic("Bad IRQ registery..."); + } + iclr = sysio_imap_to_iclr(imap); } - iclr = sysio_imap_to_iclr(imap); + ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); + ivindex += ivindex_fixup; } - ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); - ivindex += ivindex_fixup; action = *(cpu_irq + irq_action); if(action) { @@ -612,26 +613,28 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) return -ENOMEM; } - bucket = add_ino_hash(ivindex, imap, iclr, irqflags); - if(!bucket) { - kfree(action); - restore_flags(flags); - return -ENOMEM; - } + if (irqflags & SA_IMAP_MASKED) { + bucket = add_ino_hash(ivindex, imap, iclr, irqflags); + if(!bucket) { + kfree(action); + restore_flags(flags); + return -ENOMEM; + } - pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0); - ivector_to_mask[ivindex] = (1 << cpu_irq); - if(pending) - ivector_to_mask[ivindex] |= 0x80000000; + pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0); + ivector_to_mask[ivindex] = (1 << cpu_irq); + if(pending) + ivector_to_mask[ivindex] |= 0x80000000; - if(dcookie) { - dcookie->ret_ino = ivindex; - dcookie->ret_pil = cpu_irq; + if(dcookie) { + dcookie->ret_ino = ivindex; + dcookie->ret_pil = cpu_irq; + } } action->mask = (unsigned long) bucket; action->handler = handler; - action->flags = irqflags | SA_IMAP_MASKED; + action->flags = irqflags; action->name = name; action->next = NULL; action->dev_id = dev_id; @@ -664,7 +667,7 @@ void free_irq(unsigned int irq, void *dev_id) unsigned int cpu_irq; int ivindex = -1; - if(irq == 14) { + if(irq == 0) { cpu_irq = irq; } else { #ifdef CONFIG_PCI @@ -951,34 +954,43 @@ void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs) void handler_irq(int irq, struct pt_regs *regs) { struct ino_bucket *bucket = NULL; - struct irqaction *action; + struct irqaction *action, *act; int cpu = smp_processor_id(); +#ifndef __SMP__ + /* + * Check for TICK_INT on level 14 softint. + */ + if ((irq == 14) && get_softint() & (1UL << 0)) + irq = 0; +#endif clear_softint(1 << irq); irq_enter(cpu, irq); action = *(irq + irq_action); - kstat.interrupts[irq]++; + kstat.irqs[cpu][irq]++; if(!action) { unexpected_irq(irq, 0, regs); } else { + act = action; do { - unsigned long *swmask = NULL; - - if(action->flags & SA_IMAP_MASKED) { - bucket = (struct ino_bucket *)action->mask; - - swmask = &ivector_to_mask[bucket->ino]; - if(!(*swmask & 0x80000000)) + if(act->flags & SA_IMAP_MASKED) { + bucket = (struct ino_bucket *)act->mask; + if(!(ivector_to_mask[bucket->ino] & 0x80000000)) continue; } - - action->handler(irq, action->dev_id, regs); - if(swmask) { - *swmask &= ~(0x80000000); + act->handler(irq, act->dev_id, regs); + } while((act = act->next) != NULL); + act = action; + do { + if(act->flags & SA_IMAP_MASKED) { + bucket = (struct ino_bucket *)act->mask; + if(!(ivector_to_mask[bucket->ino] & 0x80000000)) + continue; + ivector_to_mask[bucket->ino] &= ~(0x80000000); *(bucket->iclr) = SYSIO_ICLR_IDLE; } - } while((action = action->next) != NULL); + } while((act = act->next) != NULL); } irq_exit(cpu, irq); } @@ -993,6 +1005,7 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) int cpu = smp_processor_id(); irq_enter(cpu, irq); + kstat.irqs[cpu][irq]++; bucket = (struct ino_bucket *)action->mask; floppy_interrupt(irq, dev_cookie, regs); ivector_to_mask[bucket->ino] &= ~(0x80000000); @@ -1036,13 +1049,19 @@ int request_fast_irq(unsigned int irq, unsigned long flags; unsigned int *imap, *iclr; void *bus_id = NULL; - int ivindex, ivindex_fixup, cpu_irq = -1; + int ivindex = -1, ivindex_fixup, cpu_irq = -1; if(!handler) return -EINVAL; imap = iclr = NULL; ivindex_fixup = 0; + + if ((irq == 0) || (irq == 14)) { + printk("request_fast_irq: Trying to register shared IRQ 0 or 14.\n"); + return -EBUSY; + } + #ifdef CONFIG_PCI if(PCI_IRQ_P(irq)) { pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq); @@ -1066,10 +1085,7 @@ int request_fast_irq(unsigned int irq, * XXX verify that all drivers sparc64 will use are updated * XXX to use the new IRQ registry dcookie interface. -DaveM */ - if(irq == 14) - cpu_irq = irq; - else - cpu_irq = sysio_ino_to_pil[irq]; + cpu_irq = sysio_ino_to_pil[irq]; imap = sysio_irq_to_imap(irq); if(!imap) { printk("request_irq: BAD, null imap for old style " @@ -1153,85 +1169,100 @@ int probe_irq_off(unsigned long mask) return 0; } -struct sun5_timer *linux_timers = NULL; - -/* This is gets the master level10 timer going. */ -void init_timers(void (*cfunc)(int, void *, struct pt_regs *)) +/* This is gets the master TICK_INT timer going. */ +void init_timers(void (*cfunc)(int, void *, struct pt_regs *), + unsigned long *clock) { - struct linux_prom64_registers pregs[3]; - struct devid_cookie dcookie; - unsigned int *imap, *iclr; - u32 pirqs[2]; + unsigned long flags; + unsigned long timer_tick_offset; int node, err; - node = prom_finddevice("/counter-timer"); - if(node == 0 || node == -1) { - 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); - iclr = (((unsigned int *)__va(pregs[1].phys_addr))+1); - imap = (((unsigned int *)__va(pregs[2].phys_addr))+1); - - /* Shut it up first. */ - linux_timers->limit0 = 0; + node = linux_cpus[0].prom_node; + *clock = prom_getint(node, "clock-frequency"); + timer_tick_offset = *clock / HZ; /* Register IRQ handler. */ - dcookie.real_dev_id = NULL; - dcookie.imap = imap; - dcookie.iclr = iclr; - dcookie.pil = 10; - dcookie.bus_cookie = NULL; - - err = request_irq(pirqs[0], cfunc, - (SA_DCOOKIE | SA_INTERRUPT | SA_STATIC_ALLOC), - "timer", &dcookie); + err = request_irq(0, cfunc, (SA_INTERRUPT | SA_STATIC_ALLOC), + "timer", NULL); if(err) { - prom_printf("Serious problem, cannot register timer interrupt\n"); + prom_printf("Serious problem, cannot register TICK_INT\n"); prom_halt(); - } else { - unsigned long flags; + } - save_and_cli(flags); + save_and_cli(flags); - /* 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)); + /* 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"); - restore_flags(flags); - } + __asm__ __volatile__(" + rd %%tick, %%g1 + add %%g1, %0, %%g1 + wr %%g1, 0x0, %%tick_cmpr" + : /* no outputs */ + : "r" (timer_tick_offset) + : "g1"); + restore_flags(flags); sti(); } -struct sun5_timer *prom_timers; +#ifdef __SMP__ +/* Called from smp_commence, when we know how many cpus are in the system + * and can have device IRQ's directed at them. + */ +void distribute_irqs(void) +{ + unsigned long flags; + int cpu, level; + + printk("SMP: redistributing interrupts...\n"); + save_and_cli(flags); + cpu = 0; + for(level = 0; level < NR_IRQS; level++) { + struct irqaction *p = irq_action[level]; + + while(p) { + if(p->flags & SA_IMAP_MASKED) { + struct ino_bucket *bucket = (struct ino_bucket *)p->mask; + unsigned int *imap = bucket->imap; + unsigned int val; + unsigned long tid = __cpu_logical_map[cpu] << 9; + + val = *imap; + *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); + printk("SMP: Redirecting IGN[%x] INO[%x] " + "to cpu %d [%s]\n", + (val & SYSIO_IMAP_IGN) >> 6, + (val & SYSIO_IMAP_INO), cpu, + p->name); + + cpu++; + if (cpu >= NR_CPUS || __cpu_logical_map[cpu] == -1) + cpu = 0; + } + p = p->next; + } + } + restore_flags(flags); + irqs_have_been_distributed = 1; +} +#endif + + +struct sun5_timer *prom_timers; static u64 prom_limit0, prom_limit1; static void map_prom_timers(void) @@ -1245,9 +1276,8 @@ static void map_prom_timers(void) /* Assume if node is not present, PROM uses different tick mechanism * which we should not care about. */ - if(tnode == 0) { + if(tnode == 0 || tnode == -1) { prom_timers = (struct sun5_timer *) 0; - prom_printf("AIEEE, no timers\n"); return; } @@ -1300,52 +1330,6 @@ void enable_prom_timer(void) prom_timers->count0 = 0; } -#ifdef __SMP__ -/* Called from smp_commence, when we know how many cpus are in the system - * and can have device IRQ's directed at them. - */ -void distribute_irqs(void) -{ - unsigned long flags; - int cpu, level; - - printk("SMP: redistributing interrupts...\n"); - save_and_cli(flags); - cpu = 0; - for(level = 0; level < NR_IRQS; level++) { - struct irqaction *p = irq_action[level]; - - while(p) { - if(p->flags & SA_IMAP_MASKED) { - struct ino_bucket *bucket = (struct ino_bucket *)p->mask; - unsigned int *imap = bucket->imap; - unsigned int val; - unsigned long tid = linux_cpus[cpu].mid << 9; - - val = *imap; - *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); - - printk("SMP: Redirecting IGN[%x] INO[%x] " - "to cpu %d [%s]\n", - (val & SYSIO_IMAP_IGN) >> 6, - (val & SYSIO_IMAP_INO), cpu, - p->name); - - cpu += 1; - while(!(cpu_present_map & (1UL << cpu))) { - cpu += 1; - if(cpu >= smp_num_cpus) - cpu = 0; - } - } - p = p->next; - } - } - restore_flags(flags); - irqs_have_been_distributed = 1; -} -#endif - __initfunc(void init_IRQ(void)) { int i; diff --git a/arch/sparc64/kernel/itlb_miss.S b/arch/sparc64/kernel/itlb_miss.S index 9317587a7..94e3f44f6 100644 --- a/arch/sparc64/kernel/itlb_miss.S +++ b/arch/sparc64/kernel/itlb_miss.S @@ -1,42 +1,42 @@ -/* $Id: itlb_miss.S,v 1.11 1997/10/14 01:48:25 davem Exp $ +/* $Id: itlb_miss.S,v 1.12 1998/01/14 17:14:47 jj Exp $ * itlb_miss.S: Instruction TLB miss code, this is included directly * into the trap table. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* Gratuitous comment. */ /* ICACHE line 1 */ /*0x00*/ ldxa [%g0] ASI_IMMU, %g1 ! Get TAG_TARGET - /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset - /*0x08*/ srlx %g1, 48, %g5 ! Shift down CONTEXT bits - /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset - /*0x10*/ sllx %g1, 2, %g4 ! Position PMD offset + /*0x04*/ srlx %g1, 10, %g3 ! Position PGD offset + /*0x08*/ andcc %g1, %g2, %g0 ! Test CONTEXT bits + /*0x0c*/ and %g3, 0xffc, %g3 ! Mask PGD offset + /*0x10*/ and %g1, 0xffe, %g4 ! Mask PMD offset /*0x14*/ ldxa [%g0] ASI_IMMU_TSB_8KB_PTR, %g1 ! For PTE offset - /*0x18*/ brz,pn %g5, 3f ! Context 0 == kernel - /*0x1c*/ and %g4, %g2, %g4 ! Mask PMD offset + /*0x18*/ be,pn %xcc, 3f ! Context 0 == kernel + /*0x1c*/ add %g4, %g4, %g4 ! Position PMD offset /* ICACHE line 2 */ - /*0x20*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load user PGD + /*0x20*/ lduwa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load user PGD /*0x24*/ srlx %g1, 1, %g1 ! PTE offset - /*0x28*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD -2:/*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE + /*0x28*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD + /*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE /*0x30*/ brgez,pn %g5, sparc64_itlb_refbit_catch ! Valid set? /*0x34*/ nop ! delay /*0x38*/ stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load /*0x3c*/ retry ! Trap return 3: /* ICACHE line 3 */ - /*0x40*/ ldxa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD + /*0x40*/ lduwa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD /*0x44*/ srlx %g1, 1, %g1 ! PTE offset - /*0x48*/ ba,pt %xcc, 2b ! Continue above - /*0x4c*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD - /*0x50*/ nop - /*0x54*/ nop - /*0x58*/ nop - /*0x5c*/ nop + /*0x48*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD + /*0x4c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE + /*0x50*/ brgez,pn %g5, sparc64_itlb_refbit_catch ! Valid set? + /*0x54*/ nop ! delay + /*0x58*/ stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load + /*0x5c*/ retry ! Trap return /* ICACHE line 4 */ /*0x60*/ nop diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 4e5ead982..c0058afd9 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.50 1998/01/09 16:39:33 jj Exp $ +/* $Id: process.c,v 1.52 1998/03/29 12:57:53 ecd Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -41,9 +41,6 @@ /* #define VERBOSE_SHOWREGS */ -#define PGTCACHE_HIGH_WATER 50 -#define PGTCACHE_LOW_WATER 25 - #ifndef __SMP__ /* @@ -58,16 +55,7 @@ asmlinkage int sys_idle(void) current->priority = -100; current->counter = -100; for (;;) { - if(pgtable_cache_size > PGTCACHE_LOW_WATER) { - do { - if(pgd_quicklist) - free_page((unsigned long) get_pgd_fast()); - if(pmd_quicklist) - free_page((unsigned long) get_pmd_fast()); - if(pte_quicklist) - free_page((unsigned long) get_pte_fast()); - } while(pgtable_cache_size > PGTCACHE_HIGH_WATER); - } + check_pgt_cache(); run_task_queue(&tq_scheduler); schedule(); } @@ -83,16 +71,7 @@ asmlinkage int cpu_idle(void) { current->priority = -100; while(1) { - if(pgtable_cache_size > PGTCACHE_LOW_WATER) { - do { - if(pgd_quicklist) - free_page((unsigned long) get_pgd_fast()); - if(pmd_quicklist) - free_page((unsigned long) get_pmd_fast()); - if(pte_quicklist) - free_page((unsigned long) get_pte_fast()); - } while(pgtable_cache_size > PGTCACHE_HIGH_WATER); - } + check_pgt_cache(); if(tq_scheduler) { lock_kernel(); run_task_queue(&tq_scheduler); @@ -592,6 +571,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, p->tss.flags |= SPARC_FLAG_KTHREAD; p->tss.current_ds = KERNEL_DS; p->tss.ctx = 0; + __asm__ __volatile__("flushw"); + memcpy((void *)(p->tss.ksp + STACK_BIAS), + (void *)(regs->u_regs[UREG_FP] + STACK_BIAS), + sizeof(struct reg_window)); p->tss.kregs->u_regs[UREG_G6] = (unsigned long) p; } else { if(current->tss.flags & SPARC_FLAG_32BIT) { diff --git a/arch/sparc64/kernel/psycho.c b/arch/sparc64/kernel/psycho.c index b3b403e33..78a69e8df 100644 --- a/arch/sparc64/kernel/psycho.c +++ b/arch/sparc64/kernel/psycho.c @@ -1,4 +1,4 @@ -/* $Id: psycho.c,v 1.31 1998/01/10 18:26:15 ecd Exp $ +/* $Id: psycho.c,v 1.50 1998/04/10 12:29:47 ecd Exp $ * psycho.c: Ultra/AX U2P PCI controller support. * * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu) @@ -8,6 +8,7 @@ #include <linux/config.h> #include <linux/kernel.h> #include <linux/types.h> +#include <linux/init.h> #include <asm/ebus.h> #include <asm/sbus.h> /* for sanity check... */ @@ -15,6 +16,7 @@ #undef PROM_DEBUG #undef FIXUP_REGS_DEBUG #undef FIXUP_IRQ_DEBUG +#undef FIXUP_VMA_DEBUG #ifdef PROM_DEBUG #define dprintf prom_printf @@ -22,6 +24,9 @@ #define dprintf printk #endif +unsigned long pci_dvma_offset = 0x00000000UL; +unsigned long pci_dvma_mask = 0xffffffffUL; + #ifndef CONFIG_PCI int pcibios_present(void) @@ -51,12 +56,12 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, #include <linux/smp.h> #include <linux/smp_lock.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <asm/io.h> #include <asm/oplib.h> #include <asm/pbm.h> +#include <asm/apb.h> #include <asm/uaccess.h> struct linux_psycho *psycho_root = NULL; @@ -95,8 +100,9 @@ static inline unsigned long long_align(unsigned long addr) ~(sizeof(unsigned long) - 1)); } -static unsigned long psycho_iommu_init(struct linux_psycho *psycho, - unsigned long memory_start) +__initfunc(static unsigned long psycho_iommu_init(struct linux_psycho *psycho, + int tsbsize, + unsigned long memory_start)) { unsigned long tsbbase = PAGE_ALIGN(memory_start); unsigned long control, i; @@ -114,10 +120,10 @@ static unsigned long psycho_iommu_init(struct linux_psycho *psycho, control &= ~(IOMMU_CTRL_DENAB); psycho->psycho_regs->iommu_control = control; - memory_start = (tsbbase + ((32 * 1024) * 8)); + memory_start = (tsbbase + ((tsbsize * 1024) * 8)); iopte = (unsigned long *)tsbbase; - for(i = 0; i < (32 * 1024); i++) { + for(i = 0; i < (tsbsize * 1024); i++) { *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE); *iopte |= (i << 16); @@ -128,15 +134,215 @@ static unsigned long psycho_iommu_init(struct linux_psycho *psycho, control = psycho->psycho_regs->iommu_control; control &= ~(IOMMU_CTRL_TSBSZ); - control |= (IOMMU_TSBSZ_32K | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB); + control |= (IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB); + switch(tsbsize) { + case 8: + pci_dvma_mask = 0x1fffffffUL; + control |= IOMMU_TSBSZ_8K; + break; + case 16: + pci_dvma_mask = 0x3fffffffUL; + control |= IOMMU_TSBSZ_16K; + break; + case 32: + pci_dvma_mask = 0x7fffffffUL; + control |= IOMMU_TSBSZ_32K; + break; + default: + prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize); + prom_halt(); + break; + } psycho->psycho_regs->iommu_control = control; return memory_start; } extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm); +extern void prom_pbm_intmap_init(int node, struct linux_pbm_info *pbm); + +/* + * Poor man's PCI... + */ +__initfunc(unsigned long sabre_init(int pnode, unsigned long memory_start)) +{ + struct linux_prom64_registers pr_regs[2]; + struct linux_psycho *sabre; + unsigned long ctrl; + int tsbsize, node, err; + u32 busrange[2]; + u32 vdma[2]; + u32 portid; + int bus; + + sabre = (struct linux_psycho *)memory_start; + memory_start = long_align(memory_start + sizeof(struct linux_psycho)); + + portid = prom_getintdefault(pnode, "upa-portid", 0xff); + + memset(sabre, 0, sizeof(*sabre)); + + sabre->next = psycho_root; + psycho_root = sabre; + + sabre->upa_portid = portid; + sabre->index = linux_num_psycho++; + + /* + * Map in SABRE register set and report the presence of this SABRE. + */ + err = prom_getproperty(pnode, "reg", + (char *)&pr_regs[0], sizeof(pr_regs)); + if(err == 0 || err == -1) { + prom_printf("SABRE: Error, cannot get U2P registers " + "from PROM.\n"); + prom_halt(); + } + + /* + * First REG in property is base of entire SABRE register space. + */ + sabre->psycho_regs = + sparc_alloc_io((pr_regs[0].phys_addr & 0xffffffff), + NULL, sizeof(struct psycho_regs), + "SABRE Registers", + (pr_regs[0].phys_addr >> 32), 0); + if(sabre->psycho_regs == NULL) { + prom_printf("SABRE: Error, cannot map SABRE main registers.\n"); + prom_halt(); + } + + printk("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs); +#ifdef PROM_DEBUG + dprintf("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs); +#endif + + ctrl = sabre->psycho_regs->pci_a_control; + ctrl = (1UL << 36) | (1UL << 34) | (1UL << 21) | (1UL << 8) | 0x0fUL; + sabre->psycho_regs->pci_a_control = ctrl; + + /* Now map in PCI config space for entire SABRE. */ + sabre->pci_config_space = + sparc_alloc_io(((pr_regs[0].phys_addr & 0xffffffff) + + 0x01000000), + NULL, 0x01000000, + "PCI Config Space", + (pr_regs[0].phys_addr >> 32), 0); + if(sabre->pci_config_space == NULL) { + prom_printf("SABRE: Error, cannot map PCI config space.\n"); + prom_halt(); + } + + /* Report some more info. */ + printk("SABRE: PCI config space at %p\n", sabre->pci_config_space); +#ifdef PROM_DEBUG + dprintf("SABRE: PCI config space at %p\n", sabre->pci_config_space); +#endif + + err = prom_getproperty(pnode, "virtual-dma", + (char *)&vdma[0], sizeof(vdma)); + if(err == 0 || err == -1) { + prom_printf("SABRE: Error, cannot get virtual-dma property " + "from PROM.\n"); + prom_halt(); + } + + switch(vdma[1]) { + case 0x20000000: + tsbsize = 8; + break; + case 0x40000000: + tsbsize = 16; + break; + case 0x80000000: + tsbsize = 32; + break; + default: + prom_printf("SABRE: strange virtual-dma size.\n"); + prom_halt(); + } + + memory_start = psycho_iommu_init(sabre, tsbsize, memory_start); + pci_dvma_offset = vdma[0]; + + printk("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]); +#ifdef PROM_DEBUG + dprintf("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]); +#endif + + err = prom_getproperty(pnode, "bus-range", + (char *)&busrange[0], sizeof(busrange)); + if(err == 0 || err == -1) { + prom_printf("SIMBA: Error, cannot get PCI bus-range " + " from PROM.\n"); + prom_halt(); + } + + sabre->pci_first_busno = busrange[0]; + sabre->pci_last_busno = busrange[1]; + sabre->pci_bus = &pci_root; + + /* + * Handle config space reads through any Simba on APB. + */ + for (bus = sabre->pci_first_busno; bus <= sabre->pci_last_busno; bus++) + bus2pbm[bus] = &sabre->pbm_A; + + /* + * Look for APB underneath. + */ + node = prom_getchild(pnode); + while ((node = prom_searchsiblings(node, "pci"))) { + struct linux_pbm_info *pbm; + char namebuf[128]; + + err = prom_getproperty(node, "model", namebuf, sizeof(namebuf)); + if ((err <= 0) || strncmp(namebuf, "SUNW,simba", err)) + goto next_pci; + + err = prom_getproperty(node, "bus-range", + (char *)&busrange[0], sizeof(busrange)); + if(err == 0 || err == -1) { + prom_printf("SIMBA: Error, cannot get PCI bus-range " + " from PROM.\n"); + prom_halt(); + } + + if (busrange[0] == 1) + pbm = &sabre->pbm_B; + else + pbm = &sabre->pbm_A; + + pbm->parent = sabre; + pbm->IO_assignments = NULL; + pbm->MEM_assignments = NULL; + pbm->prom_node = node; + + prom_getstring(node, "name", namebuf, sizeof(namebuf)); + strcpy(pbm->prom_name, namebuf); + + /* Now the ranges. */ + prom_pbm_ranges_init(pnode, pbm); + prom_pbm_intmap_init(node, pbm); + + pbm->pci_first_busno = busrange[0]; + pbm->pci_last_busno = busrange[1]; + memset(&pbm->pci_bus, 0, sizeof(struct pci_bus)); + + for (bus = pbm->pci_first_busno; + bus <= pbm->pci_last_busno; bus++) + bus2pbm[bus] = pbm; -unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) + next_pci: + node = prom_getsibling(node); + if (!node) + break; + } + + return memory_start; +} + +__initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)) { struct linux_prom64_registers pr_regs[3]; struct linux_psycho *psycho; @@ -144,9 +350,9 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) u32 portid; int node; - printk("PSYCHO: Probing for controllers.\n"); + printk("PCI: Probing for controllers.\n"); #ifdef PROM_DEBUG - dprintf("PSYCHO: Probing for controllers.\n"); + dprintf("PCI: Probing for controllers.\n"); #endif memory_start = long_align(memory_start); @@ -157,6 +363,12 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) u32 busrange[2]; int err, is_pbm_a; + err = prom_getproperty(node, "model", namebuf, sizeof(namebuf)); + if ((err > 0) && !strncmp(namebuf, "SUNW,sabre", err)) { + memory_start = sabre_init(node, memory_start); + goto next_pci; + } + psycho = (struct linux_psycho *)memory_start; portid = prom_getintdefault(node, "upa-portid", 0xff); @@ -200,34 +412,30 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) * Third REG in property is base of entire PSYCHO * register space. */ - psycho->psycho_regs = sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff), - NULL, sizeof(struct psycho_regs), - "PSYCHO Registers", - (pr_regs[2].phys_addr >> 32), 0); + psycho->psycho_regs = + sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff), + NULL, sizeof(struct psycho_regs), + "PSYCHO Registers", + (pr_regs[2].phys_addr >> 32), 0); if(psycho->psycho_regs == NULL) { prom_printf("PSYCHO: Error, cannot map PSYCHO " "main registers.\n"); prom_halt(); } - printk("PSYCHO: Found controller, main regs at %p\n", + printk("PCI: Found PSYCHO, main regs at %p\n", psycho->psycho_regs); #ifdef PROM_DEBUG - dprintf("PSYCHO: Found controller, main regs at %p\n", + dprintf("PCI: Found PSYCHO, main regs at %p\n", psycho->psycho_regs); #endif psycho->psycho_regs->irq_retry = 0xff; -#if 0 - psycho->psycho_regs->ecc_control |= 1; - psycho->psycho_regs->sbuf_a_control = 0; - psycho->psycho_regs->sbuf_b_control = 0; -#endif - /* Now map in PCI config space for entire PSYCHO. */ psycho->pci_config_space = - sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)+0x01000000), + sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff) + + 0x01000000), NULL, 0x01000000, "PCI Config Space", (pr_regs[2].phys_addr >> 32), 0); @@ -244,7 +452,8 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) psycho->pci_config_space); #endif - memory_start = psycho_iommu_init(psycho, memory_start); + memory_start = psycho_iommu_init(psycho, 32, memory_start); + pci_dvma_offset = 0x80000000UL; is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); @@ -268,6 +477,7 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) /* Now the ranges. */ prom_pbm_ranges_init(node, pbm); + prom_pbm_intmap_init(node, pbm); /* Finally grab the pci bus root array for this pbm after * having found the bus range existing under it. @@ -282,6 +492,7 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) pbm->pci_last_busno = busrange[1]; memset(&pbm->pci_bus, 0, sizeof(struct pci_bus)); + next_pci: node = prom_getsibling(node); if(!node) break; @@ -308,54 +519,18 @@ int pcibios_present(void) return psycho_root != NULL; } -int pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - -int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - static inline struct pci_vma *pci_find_vma(struct linux_pbm_info *pbm, unsigned long start, - int io) + unsigned int offset, int io) { struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments); - while(vp) { - if(vp->end > start) + while (vp) { + if (offset && (vp->offset != offset)) + goto next; + if (vp->end >= start) break; + next: vp = vp->next; } return vp; @@ -391,7 +566,7 @@ static inline void pci_add_vma(struct linux_pbm_info *pbm, struct pci_vma *new, /* Check for programming errors. */ if(vp && ((vp->start >= new->start && vp->start < new->end) || - ((vp->end - 1) >= new->start && (vp->end - 1) < new->end))) { + (vp->end >= new->start && vp->end < new->end))) { prom_printf("pci_add_vma: Wheee, overlapping %s PCI vma's\n", io ? "IO" : "MEM"); prom_printf("pci_add_vma: vp[%016lx:%016lx] " @@ -414,7 +589,7 @@ static inline void pci_init_alloc_fini(void) pci_alloc_arena = NULL; } -static void *pci_init_alloc(int size) +__initfunc(static void *pci_init_alloc(int size)) { unsigned long start = long_align(*pci_alloc_arena); void *mp = (void *)start; @@ -439,8 +614,8 @@ static inline struct pcidev_cookie *pci_devcookie_alloc(void) } -static void -pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus) +__initfunc(static void +pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)) { unsigned int devfn, l, class; unsigned char hdr_type = 0; @@ -487,7 +662,7 @@ pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus) } } -static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus) +__initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)) { unsigned int nbus; @@ -513,7 +688,79 @@ static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus) } -static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart) +__initfunc(static void apb_init(struct linux_psycho *sabre)) +{ + struct pci_dev *pdev; + unsigned short stmp; + unsigned int itmp; + + for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) { + if (pdev->vendor == PCI_VENDOR_ID_SUN && + pdev->device == PCI_DEVICE_ID_SUN_SIMBA) { + + pci_read_config_word(pdev, PCI_COMMAND, &stmp); + stmp |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | + PCI_COMMAND_IO; + pci_write_config_word(pdev, PCI_COMMAND, stmp); + + pci_write_config_word(pdev, PCI_STATUS, 0xffff); + pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff); + + pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &stmp); + stmp = PCI_BRIDGE_CTL_MASTER_ABORT | + PCI_BRIDGE_CTL_SERR | + PCI_BRIDGE_CTL_PARITY; + pci_write_config_word(pdev, PCI_BRIDGE_CONTROL, stmp); + + pci_read_config_dword(pdev, APB_PCI_CONTROL_HIGH, &itmp); + itmp = APB_PCI_CTL_HIGH_SERR | + APB_PCI_CTL_HIGH_ARBITER_EN; + pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp); + + pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp); + itmp = APB_PCI_CTL_LOW_ARB_PARK | + APB_PCI_CTL_LOW_ERRINT_EN | 0x0f; + pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp); + + /* + * Setup Registers for Guaranteed Completion. + */ + pci_write_config_byte(pdev, APB_PRIMARY_MASTER_RETRY_LIMIT, 0); + pci_write_config_byte(pdev, APB_SECONDARY_MASTER_RETRY_LIMIT, 0); + pci_write_config_byte(pdev, APB_PIO_TARGET_RETRY_LIMIT, 0x80); + pci_write_config_byte(pdev, APB_PIO_TARGET_LATENCY_TIMER, 0); + pci_write_config_byte(pdev, APB_DMA_TARGET_RETRY_LIMIT, 0x80); + pci_write_config_byte(pdev, APB_DMA_TARGET_LATENCY_TIMER, 0); + } + } +} + +__initfunc(static void sabre_probe(struct linux_psycho *sabre, + unsigned long *mstart)) +{ + struct pci_bus *pbus = sabre->pci_bus; + static unsigned char busno = 0; + + pbus->number = pbus->secondary = busno; + pbus->sysdata = sabre; + + pbus->subordinate = pci_scan_bus(pbus, mstart); + busno = pbus->subordinate + 1; + + for(pbus = pbus->children; pbus; pbus = pbus->next) { + if (pbus->number == sabre->pbm_A.pci_first_busno) + memcpy(&sabre->pbm_A.pci_bus, pbus, sizeof(*pbus)); + if (pbus->number == sabre->pbm_B.pci_first_busno) + memcpy(&sabre->pbm_B.pci_bus, pbus, sizeof(*pbus)); + } + + apb_init(sabre); +} + + +__initfunc(static void pbm_probe(struct linux_pbm_info *pbm, + unsigned long *mstart)) { static struct pci_bus *pchain = NULL; struct pci_bus *pbus = &pbm->pci_bus; @@ -552,9 +799,9 @@ static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart) } } -static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, - struct pci_dev *pdev, - int node) +__initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + int node)) { struct linux_prom_pci_registers pregs[PROMREG_MAX]; int err; @@ -583,7 +830,7 @@ static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, return 0; } -static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev) +__initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev)) { struct pcidev_cookie *pcp; int node = prom_getchild(pbm->prom_node); @@ -597,24 +844,37 @@ static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev) pdev->sysdata = pcp; } -static void fill_in_pbm_cookies(struct linux_pbm_info *pbm) +__initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus, + struct linux_pbm_info *pbm)) { - struct pci_bus *pbtmp, *pbus = &pbm->pci_bus; struct pci_dev *pdev; - for(pbtmp = pbus->children; pbtmp; pbtmp = pbtmp->children) - pbtmp->sysdata = pbm; + pbus->sysdata = pbm; + + for(pdev = pbus->devices; pdev; pdev = pdev->sibling) + pdev_cookie_fillin(pbm, pdev); + + for(pbus = pbus->children; pbus; pbus = pbus->next) + fill_in_pbm_cookies(pbus, pbm); +} + +__initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre)) +{ + struct pci_bus *pbus = sabre->pci_bus; - for( ; pbus; pbus = pbus->children) - for(pdev = pbus->devices; pdev; pdev = pdev->sibling) - pdev_cookie_fillin(pbm, pdev); + for(pbus = pbus->children; pbus; pbus = pbus->next) { + if (pbus->number == sabre->pbm_A.pci_first_busno) + pdev_cookie_fillin(&sabre->pbm_A, pbus->self); + else if (pbus->number == sabre->pbm_B.pci_first_busno) + pdev_cookie_fillin(&sabre->pbm_B, pbus->self); + } } /* Walk PROM device tree under PBM, looking for 'assigned-address' * properties, and recording them in pci_vma's linked in via * PBM->assignments. */ -static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs) +__initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)) { struct linux_prom_ebus_ranges erng[PROMREG_MAX]; int err, iter; @@ -632,18 +892,26 @@ static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *are ap->phys_hi = ep->parent_phys_hi; ap->phys_mid = ep->parent_phys_mid; ap->phys_lo = ep->parent_phys_lo; + + ap->size_hi = 0; + ap->size_lo = ep->size; } return err; } -static void assignment_process(struct linux_pbm_info *pbm, int node) +__initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node)) { struct linux_prom_pci_registers aregs[PROMREG_MAX]; char pname[256]; int err, iter, numa; err = prom_getproperty(node, "name", (char *)&pname[0], sizeof(pname)); - if(strncmp(pname, "ebus", 4) == 0) { + if (err > 0) + pname[err] = 0; +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: %s\n", __FUNCTION__, err > 0 ? pname : "???"); +#endif + if(strcmp(pname, "ebus") == 0) { numa = gimme_ebus_assignments(node, &aregs[0]); } else { err = prom_getproperty(node, "assigned-addresses", @@ -653,7 +921,7 @@ static void assignment_process(struct linux_pbm_info *pbm, int node) if(err == 0 || err == -1) return; - numa = (err / sizeof(struct linux_prom_pci_ranges)); + numa = (err / sizeof(struct linux_prom_pci_registers)); } for(iter = 0; iter < numa; iter++) { @@ -667,8 +935,6 @@ static void assignment_process(struct linux_pbm_info *pbm, int node) io = (space == 1); breg = (ap->phys_hi & 0xff); - if(breg == PCI_ROM_ADDRESS) - continue; vp = pci_vma_alloc(); @@ -677,21 +943,20 @@ static void assignment_process(struct linux_pbm_info *pbm, int node) * XXX either due to it's layout so... */ vp->start = ap->phys_lo; - vp->end = vp->start + ap->size_lo; - vp->base_reg = breg; - - /* Sanity */ - if(io && (vp->end & ~(0xffff))) { - prom_printf("assignment_process: Out of range PCI I/O " - "[%08lx:%08lx]\n", vp->start, vp->end); - prom_halt(); - } + vp->end = vp->start + ap->size_lo - 1; + vp->offset = (ap->phys_hi & 0xffffff); pci_add_vma(pbm, vp, io); + +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: BaseReg %02x", pname, breg); + dprintf(" %s vma [%08x,%08x]\n", + io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end); +#endif } } -static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node) +__initfunc(static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)) { while(node) { int child = prom_getchild(node); @@ -704,17 +969,112 @@ static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node) } } -static void record_assignments(struct linux_pbm_info *pbm) +static inline void record_assignments(struct linux_pbm_info *pbm) { + struct pci_vma *vp; + + if (pbm->parent->pci_bus) { + /* + * Disallow anything that is not in our IO/MEM map on SIMBA. + */ + struct pci_bus *pbus = pbm->parent->pci_bus; + struct pci_dev *pdev; + unsigned char map; + int bit; + + for (pdev = pbus->devices; pdev; pdev = pdev->sibling) { + struct pcidev_cookie *pcp = pdev->sysdata; + if (!pcp) { + prom_printf("record_assignments: " + "no pcidev_cookie for pdev %02x\n", + pdev->devfn); + prom_halt(); + } + if (pcp->pbm == pbm) + break; + } + + if (!pdev) { + prom_printf("record_assignments: no pdev for PBM\n"); + prom_halt(); + } + + pci_read_config_byte(pdev, APB_IO_ADDRESS_MAP, &map); +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: IO %02x\n", __FUNCTION__, map); +#endif + for (bit = 0; bit < 8; bit++) { + if (!(map & (1 << bit))) { + vp = pci_vma_alloc(); + vp->start = (bit << 21); + vp->end = vp->start + (1 << 21) - 1; + vp->offset = 0; + pci_add_vma(pbm, vp, 1); +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: IO prealloc vma [%08x,%08x]\n", + __FUNCTION__, vp->start, vp->end); +#endif + } + } + pci_read_config_byte(pdev, APB_MEM_ADDRESS_MAP, &map); +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: MEM %02x\n", __FUNCTION__, map); +#endif + for (bit = 0; bit < 8; bit++) { + if (!(map & (1 << bit))) { + vp = pci_vma_alloc(); + vp->start = (bit << 29); + vp->end = vp->start + (1 << 29) - 1; + vp->offset = 0; + pci_add_vma(pbm, vp, 0); +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: MEM prealloc vma [%08x,%08x]\n", + __FUNCTION__, vp->start, vp->end); +#endif + } + } + } + assignment_walk_siblings(pbm, prom_getchild(pbm->prom_node)); + + /* + * Protect ISA IO space from being used. + */ + vp = pci_find_vma(pbm, 0, 0, 1); + if (!vp || 0x400 <= vp->start) { + vp = pci_vma_alloc(); + vp->start = 0; + vp->end = vp->start + 0x400 - 1; + vp->offset = 0; + pci_add_vma(pbm, vp, 1); + } + +#ifdef FIXUP_VMA_DEBUG + dprintf("PROM IO assignments for PBM %s:\n", + pbm == &pbm->parent->pbm_A ? "A" : "B"); + vp = pbm->IO_assignments; + while (vp) { + dprintf(" [%08x,%08x] (%s)\n", vp->start, vp->end, + vp->offset ? "Register" : "Unmapped"); + vp = vp->next; + } + dprintf("PROM MEM assignments for PBM %s:\n", + pbm == &pbm->parent->pbm_A ? "A" : "B"); + vp = pbm->MEM_assignments; + while (vp) { + dprintf(" [%08x,%08x] (%s)\n", vp->start, vp->end, + vp->offset ? "Register" : "Unmapped"); + vp = vp->next; + } +#endif } -static void fixup_regs(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - struct linux_prom_pci_registers *pregs, - int nregs, - struct linux_prom_pci_registers *assigned, - int numaa) +__initfunc(static void fixup_regs(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *pregs, + int nregs, + struct linux_prom_pci_registers *assigned, + int numaa)) { int preg, rng; int IO_seen = 0; @@ -724,34 +1084,39 @@ static void fixup_regs(struct pci_dev *pdev, struct linux_prom_pci_registers *ap = NULL; int bustype = (pregs[preg].phys_hi >> 24) & 0x3; int bsreg, brindex; + unsigned int rtmp; u64 pci_addr; if(bustype == 0) { /* Config space cookie, nothing to do. */ if(preg != 0) - printk("%s: strange, config space not 0\n", - __FUNCTION__); + printk("%s %02x.%02x [%04x,%04x]: " + "strange, config space not 0\n", + __FUNCTION__, + pdev->bus->number, pdev->devfn, + pdev->vendor, pdev->device); continue; } else if(bustype == 3) { /* XXX add support for this... */ - printk("%s: Warning, ignoring 64-bit PCI memory space, " + printk("%s %02x.%02x [%04x,%04x]: " + "Warning, ignoring 64-bit PCI memory space, " "tell Eddie C. Dost (ecd@skynet.be).\n", - __FUNCTION__); + __FUNCTION__, + pdev->bus->number, pdev->devfn, + pdev->vendor, pdev->device); continue; } - bsreg = (pregs[preg].phys_hi & 0xff); - /* We can safely ignore these. */ - if(bsreg == PCI_ROM_ADDRESS) - continue; + bsreg = (pregs[preg].phys_hi & 0xff); /* Sanity */ if((bsreg < PCI_BASE_ADDRESS_0) || - (bsreg > (PCI_BASE_ADDRESS_5 + 4)) || + ((bsreg > (PCI_BASE_ADDRESS_5 + 4)) && (bsreg != PCI_ROM_ADDRESS)) || (bsreg & 3)) { - printk("%s: [%04x:%04x]: " + printk("%s %02x.%02x [%04x:%04x]: " "Warning, ignoring bogus basereg [%x]\n", - __FUNCTION__, pdev->vendor, pdev->device, bsreg); + __FUNCTION__, pdev->bus->number, pdev->devfn, + pdev->vendor, pdev->device, bsreg); printk(" PROM reg: %08x.%08x.%08x %08x.%08x\n", pregs[preg].phys_hi, pregs[preg].phys_mid, pregs[preg].phys_lo, pregs[preg].size_hi, @@ -798,7 +1163,16 @@ static void fixup_regs(struct pci_dev *pdev, /* AIEEE */ prom_printf("fixup_doit: YIEEE, cannot find PBM ranges\n"); } - pdev->base_address[brindex] = (unsigned long)__va(pci_addr); + if (bsreg == PCI_ROM_ADDRESS) { + pdev->rom_address = (unsigned long)__va(pci_addr); + pdev->rom_address |= 1; + /* + * Enable access to the ROM. + */ + pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &rtmp); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp | 1); + } else + pdev->base_address[brindex] = (unsigned long)__va(pci_addr); /* Preserve I/O space bit. */ if(bustype == 0x1) { @@ -811,15 +1185,18 @@ static void fixup_regs(struct pci_dev *pdev, /* Now handle assignments PROM did not take care of. */ if(nregs) { + unsigned int rtmp, ridx; + unsigned int offset, base; + struct pci_vma *vp; + u64 pci_addr; int breg; for(breg = PCI_BASE_ADDRESS_0; breg <= PCI_BASE_ADDRESS_5; breg += 4) { - unsigned int rtmp, ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2); - unsigned int base = (unsigned int)pdev->base_address[ridx]; - struct pci_vma *vp; - u64 pci_addr; int io; + ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2); + base = (unsigned int)pdev->base_address[ridx]; + if(pdev->base_address[ridx] > PAGE_OFFSET) continue; @@ -827,19 +1204,14 @@ static void fixup_regs(struct pci_dev *pdev, base &= ~((io ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)); - vp = pci_find_vma(pbm, base, io); + offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg; + vp = pci_find_vma(pbm, base, offset, io); if(!vp || vp->start > base) { unsigned int size, new_base; - pcibios_read_config_dword(pdev->bus->number, - pdev->devfn, - breg, &rtmp); - pcibios_write_config_dword(pdev->bus->number, - pdev->devfn, - breg, 0xffffffff); - pcibios_read_config_dword(pdev->bus->number, - pdev->devfn, - breg, &size); + pci_read_config_dword(pdev, breg, &rtmp); + pci_write_config_dword(pdev, breg, 0xffffffff); + pci_read_config_dword(pdev, breg, &size); if(io) size &= ~1; size = (~(size) + 1); @@ -847,7 +1219,8 @@ static void fixup_regs(struct pci_dev *pdev, continue; new_base = 0; - for(vp=pci_find_vma(pbm,new_base,io); ; vp=vp->next) { + for(vp = pci_find_vma(pbm, new_base, 0, io); ; + vp = vp->next) { if(!vp || new_base + size <= vp->start) break; new_base = (vp->end + (size - 1)) & ~(size-1); @@ -859,26 +1232,27 @@ static void fixup_regs(struct pci_dev *pdev, } vp = pci_vma_alloc(); vp->start = new_base; - vp->end = vp->start + size; - vp->base_reg = breg; - - /* Sanity */ - if(io && vp->end & ~(0xffff)) { - prom_printf("PCI: Out of range PCI I/O " - "[%08lx:%08lx] during fixup\n", - vp->start, vp->end); - prom_halt(); - } + vp->end = vp->start + size - 1; + vp->offset = offset; + pci_add_vma(pbm, vp, io); +#ifdef FIXUP_VMA_DEBUG + dprintf("%02x.%02x.%x: BaseReg %02x", + pdev->bus->number, + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), + breg); + dprintf(" %s vma [%08x,%08x]\n", + io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end); +#endif rtmp = new_base; + pci_read_config_dword(pdev, breg, &base); if(io) - rtmp |= (rtmp & PCI_BASE_ADDRESS_IO_MASK); + rtmp |= (base & ~PCI_BASE_ADDRESS_IO_MASK); else - rtmp |= (rtmp & PCI_BASE_ADDRESS_MEM_MASK); - pcibios_write_config_dword(pdev->bus->number, - pdev->devfn, - breg, rtmp); + rtmp |= (base & ~PCI_BASE_ADDRESS_MEM_MASK); + pci_write_config_dword(pdev, breg, rtmp); /* Apply PBM ranges and update pci_dev. */ pci_addr = new_base; @@ -912,13 +1286,93 @@ static void fixup_regs(struct pci_dev *pdev, } } } + + /* + * Handle PCI_ROM_ADDRESS. + */ + breg = PCI_ROM_ADDRESS; + base = (unsigned int)pdev->rom_address; + + if(pdev->rom_address > PAGE_OFFSET) + goto rom_address_done; + + base &= PCI_ROM_ADDRESS_MASK; + offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg; + vp = pci_find_vma(pbm, base, offset, 0); + if(!vp || vp->start > base) { + unsigned int size, new_base; + + pci_read_config_dword(pdev, breg, &rtmp); + pci_write_config_dword(pdev, breg, 0xffffffff); + pci_read_config_dword(pdev, breg, &size); + size &= ~1; + size = (~(size) + 1); + if(!size) + goto rom_address_done; + + new_base = 0; + for(vp = pci_find_vma(pbm, new_base, 0, 0); ; vp = vp->next) { + if(!vp || new_base + size <= vp->start) + break; + new_base = (vp->end + (size - 1)) & ~(size-1); + } + if(vp && (new_base + size > vp->start)) { + prom_printf("PCI: Impossible full MEM space.\n"); + prom_halt(); + } + vp = pci_vma_alloc(); + vp->start = new_base; + vp->end = vp->start + size - 1; + vp->offset = offset; + + pci_add_vma(pbm, vp, 0); + +#ifdef FIXUP_VMA_DEBUG + dprintf("%02x.%02x.%x: BaseReg %02x", + pdev->bus->number, + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), + breg); + dprintf(" %s vma [%08x,%08x]\n", + "ROM", vp->start, vp->end); +#endif + + rtmp = new_base; + pci_read_config_dword(pdev, breg, &base); + rtmp |= (base & ~PCI_ROM_ADDRESS_MASK); + pci_write_config_dword(pdev, breg, rtmp); + + /* Apply PBM ranges and update pci_dev. */ + pci_addr = new_base; + for(rng = 0; rng < pbm->num_pbm_ranges; rng++) { + struct linux_prom_pci_ranges *rp; + int rspace; + + rp = &pbm->pbm_ranges[rng]; + rspace = (rp->child_phys_hi >> 24) & 3; + if(rspace != 2) + continue; + pci_addr += ((u64)rp->parent_phys_lo); + pci_addr += (((u64)rp->parent_phys_hi)<<32UL); + break; + } + if(rng == pbm->num_pbm_ranges) { + /* AIEEE */ + prom_printf("fixup_doit: YIEEE, cannot find " + "PBM ranges\n"); + } + pdev->rom_address = (unsigned long)__va(pci_addr); + + pdev->rom_address |= (base & ~PCI_ROM_ADDRESS_MASK); + MEM_seen = 1; + } + rom_address_done: + } if(IO_seen || MEM_seen) { unsigned int l; - pcibios_read_config_dword(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, &l); + pci_read_config_dword(pdev, PCI_COMMAND, &l); #ifdef FIXUP_REGS_DEBUG dprintf("["); #endif @@ -937,9 +1391,7 @@ static void fixup_regs(struct pci_dev *pdev, #ifdef FIXUP_REGS_DEBUG dprintf("]"); #endif - pcibios_write_config_dword(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, l); + pci_write_config_dword(pdev, PCI_COMMAND, l); } #ifdef FIXUP_REGS_DEBUG @@ -955,7 +1407,7 @@ static void fixup_regs(struct pci_dev *pdev, #define imap_offset(__member) \ ((unsigned long)(&(((struct psycho_regs *)0)->__member))) -static unsigned long psycho_pcislot_imap_offset(unsigned long ino) +__initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino)) { unsigned int bus, slot; @@ -963,17 +1415,20 @@ static unsigned long psycho_pcislot_imap_offset(unsigned long ino) slot = (ino & 0x0c) >> 2; if(bus == 0) { - /* Perform a sanity check, we might as well. - * PBM A only has 2 PCI slots. - */ - if(slot > 1) { - prom_printf("pcislot_imap: Bogus slot on PBM A (%ld)\n", slot); - prom_halt(); - } - if(slot == 0) + switch(slot) { + case 0: return imap_offset(imap_a_slot0); - else + case 1: return imap_offset(imap_a_slot1); + case 2: + return imap_offset(imap_a_slot2); + case 3: + return imap_offset(imap_a_slot3); + default: + prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n", + bus, slot); + prom_halt(); + } } else { switch(slot) { case 0: @@ -988,13 +1443,12 @@ static unsigned long psycho_pcislot_imap_offset(unsigned long ino) prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n", bus, slot); prom_halt(); - return 0; /* Make gcc happy */ - }; + } } } /* Exported for EBUS probing layer. */ -unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino) +__initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino)) { unsigned long imap_off, ign, ino; @@ -1089,9 +1543,62 @@ unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino) return pci_irq_encode(imap_off, pbm->parent->index, ign, ino); } -static void fixup_irq(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - int node) +__initfunc(static int pbm_intmap_match(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + struct linux_prom_pci_registers *preg, + unsigned int *interrupt)) +{ + struct linux_prom_pci_registers ppreg; + unsigned int hi, mid, lo, irq; + int i; + + if (!pbm->num_pbm_intmap) + return 0; + + /* + * Underneath a bridge, use register of parent bridge. + */ + if (pdev->bus->number != pbm->pci_first_busno) { + struct pcidev_cookie *pcp = pdev->bus->self->sysdata; + int node; + + if (!pcp) + goto out; + + node = pcp->prom_node; + + i = prom_getproperty(node, "reg", (char*)&ppreg, sizeof(ppreg)); + if(i == 0 || i == -1) + goto out; + + preg = &ppreg; + } + + hi = preg->phys_hi & pbm->pbm_intmask.phys_hi; + mid = preg->phys_mid & pbm->pbm_intmask.phys_mid; + lo = preg->phys_lo & pbm->pbm_intmask.phys_lo; + irq = *interrupt & pbm->pbm_intmask.interrupt; + for (i = 0; i < pbm->num_pbm_intmap; i++) { + if ((pbm->pbm_intmap[i].phys_hi == hi) && + (pbm->pbm_intmap[i].phys_mid == mid) && + (pbm->pbm_intmap[i].phys_lo == lo) && + (pbm->pbm_intmap[i].interrupt == irq)) { + *interrupt = pbm->pbm_intmap[i].cinterrupt; + return *interrupt; + } + } + +out: + prom_printf("pbm_intmap_match: IRQ [%08x.%08x.%08x.%08x] " + "not found in interrupt-map\n", preg->phys_hi, + preg->phys_mid, preg->phys_lo, *interrupt); + prom_halt(); +} + +__initfunc(static void fixup_irq(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *preg, + int node)) { unsigned int prom_irq, portid = pbm->parent->upa_portid; unsigned char pci_irq_line = pdev->irq; @@ -1102,13 +1609,24 @@ static void fixup_irq(struct pci_dev *pdev, #endif err = prom_getproperty(node, "interrupts", (void *)&prom_irq, sizeof(prom_irq)); if(err == 0 || err == -1) { - prom_printf("fixup_irq: No interrupts property for dev[%04x:%04x]\n", - pdev->vendor, pdev->device); - prom_halt(); +#ifdef FIXUP_IRQ_DEBUG + dprintf("No interrupts property.\n"); +#endif + pdev->irq = 0; + return; } + /* See if we find a matching interrupt-map entry. */ + if (pbm_intmap_match(pbm, pdev, preg, &prom_irq)) { + pdev->irq = psycho_irq_build(pbm, + (pbm->parent->upa_portid << 6) + | prom_irq); +#ifdef FIXUP_IRQ_DEBUG + dprintf("interrupt-map specified prom_irq[%x] pdev->irq[%x]", + prom_irq, pdev->irq); +#endif /* See if fully specified already (ie. for onboard devices like hme) */ - if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) { + } else if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) { pdev->irq = psycho_irq_build(pbm, prom_irq); #ifdef FIXUP_IRQ_DEBUG dprintf("fully specified prom_irq[%x] pdev->irq[%x]", @@ -1147,7 +1665,7 @@ static void fixup_irq(struct pci_dev *pdev, slot = (pdev->bus->self->devfn >> 3) - 2; /* Use low slot number bits of child as IRQ line. */ - line = (line + ((pdev->devfn >> 3) - 4)) % 4; + line = (pdev->devfn >> 3) & 0x03; } slot = (slot << 2); @@ -1159,16 +1677,10 @@ static void fixup_irq(struct pci_dev *pdev, do { unsigned char iline, ipin; - (void)pcibios_read_config_byte(pdev->bus->number, - pdev->devfn, - PCI_INTERRUPT_PIN, - &ipin); - (void)pcibios_read_config_byte(pdev->bus->number, - pdev->devfn, - PCI_INTERRUPT_LINE, - &iline); - dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] " - "iline[%x] ipin[%x] prom_irq[%x]", + pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &ipin); + pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &iline); + dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] " + "irq[%x] iline[%x] ipin[%x] prom_irq[%x]", portid, bus>>4, slot>>2, line, pdev->irq, iline, ipin, prom_irq); } while(0); @@ -1178,21 +1690,19 @@ static void fixup_irq(struct pci_dev *pdev, /* * Write the INO to config space PCI_INTERRUPT_LINE. */ - (void)pcibios_write_config_byte(pdev->bus->number, - pdev->devfn, - PCI_INTERRUPT_LINE, - pdev->irq & PCI_IRQ_INO); + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, + pdev->irq & PCI_IRQ_INO); #ifdef FIXUP_IRQ_DEBUG dprintf("\n"); #endif } -static void fixup_doit(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - struct linux_prom_pci_registers *pregs, - int nregs, - int node) +__initfunc(static void fixup_doit(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *pregs, + int nregs, + int node)) { struct linux_prom_pci_registers assigned[PROMREG_MAX]; int numaa, err; @@ -1209,12 +1719,12 @@ static void fixup_doit(struct pci_dev *pdev, fixup_regs(pdev, pbm, pregs, nregs, &assigned[0], numaa); /* Next, fixup interrupt numbers. */ - fixup_irq(pdev, pbm, node); + fixup_irq(pdev, pbm, &pregs[0], node); } -static void fixup_pci_dev(struct pci_dev *pdev, - struct pci_bus *pbus, - struct linux_pbm_info *pbm) +__initfunc(static void fixup_pci_dev(struct pci_dev *pdev, + struct pci_bus *pbus, + struct linux_pbm_info *pbm)) { struct linux_prom_pci_registers pregs[PROMREG_MAX]; struct pcidev_cookie *pcp = pdev->sysdata; @@ -1225,18 +1735,12 @@ static void fixup_pci_dev(struct pci_dev *pdev, unsigned short cmd; /* First, enable bus mastering. */ - pcibios_read_config_word(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, &cmd); + pci_read_config_word(pdev, PCI_COMMAND, &cmd); cmd |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, cmd); + pci_write_config_word(pdev, PCI_COMMAND, cmd); /* Now, set cache line size to 64-bytes. */ - pcibios_write_config_byte(pdev->bus->number, - pdev->devfn, - PCI_CACHE_LINE_SIZE, 64); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64); } /* Ignore if this is one of the PBM's, EBUS, or a @@ -1246,8 +1750,17 @@ static void fixup_pci_dev(struct pci_dev *pdev, if((pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) || (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) || (pdev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) || - (pcp == NULL)) + (pcp == NULL)) { + /* + * Prevent access to PCI_ROM_ADDRESS, in case present + * as we don't fixup the address. + */ + if (pdev->rom_address) { + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, 0); + pdev->rom_address = 0; + } return; + } node = pcp->prom_node; @@ -1260,20 +1773,30 @@ static void fixup_pci_dev(struct pci_dev *pdev, nregs = (err / sizeof(pregs[0])); fixup_doit(pdev, pbm, &pregs[0], nregs, node); + + /* Enable bus mastering on IDE interfaces. */ + if ((pdev->class >> 8 == PCI_CLASS_STORAGE_IDE) + && (pdev->class & 0x80)) { + unsigned short cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } } -static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm) +__initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)) { struct pci_dev *pdev; for(pdev = pbus->devices; pdev; pdev = pdev->sibling) fixup_pci_dev(pdev, pbus, pbm); - for(pbus = pbus->children; pbus; pbus = pbus->children) + for(pbus = pbus->children; pbus; pbus = pbus->next) fixup_pci_bus(pbus, pbm); } -static void fixup_addr_irq(struct linux_pbm_info *pbm) +__initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm)) { struct pci_bus *pbus = &pbm->pci_bus; @@ -1286,14 +1809,16 @@ static void fixup_addr_irq(struct linux_pbm_info *pbm) /* Walk all PCI devices probes, fixing up base registers and IRQ registers. * We use OBP for most of this work. */ -static void psycho_final_fixup(struct linux_psycho *psycho) +__initfunc(static void psycho_final_fixup(struct linux_psycho *psycho)) { /* Second, fixup base address registers and IRQ lines... */ - fixup_addr_irq(&psycho->pbm_A); - fixup_addr_irq(&psycho->pbm_B); + if (psycho->pbm_A.parent) + fixup_addr_irq(&psycho->pbm_A); + if (psycho->pbm_B.parent) + fixup_addr_irq(&psycho->pbm_B); } -unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end) +__initfunc(unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end)) { struct linux_psycho *psycho; @@ -1312,11 +1837,16 @@ unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end */ for (psycho = psycho_root; psycho; psycho = psycho->next) { - /* Probe busses under PBM B. */ - pbm_probe(&psycho->pbm_B, &memory_start); - - /* Probe busses under PBM A. */ - pbm_probe(&psycho->pbm_A, &memory_start); + /* Probe bus on builtin PCI. */ + if (psycho->pci_bus) + sabre_probe(psycho, &memory_start); + else { + /* Probe busses under PBM B. */ + pbm_probe(&psycho->pbm_B, &memory_start); + + /* Probe busses under PBM A. */ + pbm_probe(&psycho->pbm_A, &memory_start); + } } pci_init_alloc_init(&memory_start); @@ -1327,8 +1857,13 @@ unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end * a pci_dev cookie (PBM+PROM_NODE, for pci_dev's). */ for (psycho = psycho_root; psycho; psycho = psycho->next) { - fill_in_pbm_cookies(&psycho->pbm_A); - fill_in_pbm_cookies(&psycho->pbm_B); + if (psycho->pci_bus) + sabre_cookie_fillin(psycho); + + fill_in_pbm_cookies(&psycho->pbm_A.pci_bus, + &psycho->pbm_A); + fill_in_pbm_cookies(&psycho->pbm_B.pci_bus, + &psycho->pbm_B); /* See what OBP has taken care of already. */ record_assignments(&psycho->pbm_A); @@ -1373,11 +1908,157 @@ pci_mkaddr(struct linux_pbm_info *pbm, unsigned char bus, static inline int out_of_range(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn) { - return (((pbm == &pbm->parent->pbm_B) && PCI_SLOT(devfn) > 4) || - ((pbm == &pbm->parent->pbm_A) && PCI_SLOT(devfn) > 6) || + return ((pbm->parent == 0) || + ((pbm == &pbm->parent->pbm_B) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 4) || + ((pbm == &pbm->parent->pbm_A) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 6) || (pci_probe_enable == 0)); } +static inline int +sabre_out_of_range(unsigned char devfn) +{ + return ((PCI_SLOT(devfn) == 0) && (PCI_FUNC(devfn) > 0)) || + ((PCI_SLOT(devfn) == 1) && (PCI_FUNC(devfn) > 1)) || + (PCI_SLOT(devfn) > 1); +} + +static int +sabre_read_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char *value) +{ + if (bus) + return pbm_read_config_byte(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) { + *value = 0xff; + return PCIBIOS_SUCCESSFUL; + } + + if (where < 8) { + unsigned short tmp; + + pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp); + if (where & 1) + *value = tmp >> 8; + else + *value = tmp & 0xff; + return PCIBIOS_SUCCESSFUL; + } else + return pbm_read_config_byte(pbm, bus, devfn, where, value); +} + +static int +sabre_read_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short *value) +{ + if (bus) + return pbm_read_config_word(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) { + *value = 0xffff; + return PCIBIOS_SUCCESSFUL; + } + + if (where < 8) + return pbm_read_config_word(pbm, bus, devfn, where, value); + else { + unsigned char tmp; + + pbm_read_config_byte(pbm, bus, devfn, where, &tmp); + *value = tmp; + pbm_read_config_byte(pbm, bus, devfn, where + 1, &tmp); + *value |= tmp << 8; + return PCIBIOS_SUCCESSFUL; + } +} + +static int +sabre_read_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int *value) +{ + unsigned short tmp; + + if (bus) + return pbm_read_config_dword(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) { + *value = 0xffffffff; + return PCIBIOS_SUCCESSFUL; + } + + sabre_read_config_word(pbm, bus, devfn, where, &tmp); + *value = tmp; + sabre_read_config_word(pbm, bus, devfn, where + 2, &tmp); + *value |= tmp << 16; + return PCIBIOS_SUCCESSFUL; +} + +static int +sabre_write_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char value) +{ + if (bus) + return pbm_write_config_byte(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where < 8) { + unsigned short tmp; + + pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp); + if (where & 1) { + value &= 0x00ff; + value |= tmp << 8; + } else { + value &= 0xff00; + value |= tmp; + } + return pbm_write_config_word(pbm, bus, devfn, where & ~1, tmp); + } else + return pbm_write_config_byte(pbm, bus, devfn, where, value); +} + +static int +sabre_write_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short value) +{ + if (bus) + return pbm_write_config_word(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where < 8) + return pbm_write_config_word(pbm, bus, devfn, where, value); + else { + pbm_write_config_byte(pbm, bus, devfn, where, value & 0xff); + pbm_write_config_byte(pbm, bus, devfn, where + 1, value >> 8); + return PCIBIOS_SUCCESSFUL; + } +} + +static int +sabre_write_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int value) +{ + if (bus) + return pbm_write_config_dword(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) + return PCIBIOS_SUCCESSFUL; + + sabre_write_config_word(pbm, bus, devfn, where, value & 0xffff); + sabre_write_config_word(pbm, bus, devfn, where + 2, value >> 16); + return PCIBIOS_SUCCESSFUL; +} + static int pbm_read_config_byte(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn, @@ -1574,36 +2255,60 @@ pbm_write_config_dword(struct linux_pbm_info *pbm, int pcibios_read_config_byte (unsigned char bus, unsigned char devfn, unsigned char where, unsigned char *value) { - return pbm_read_config_byte(bus2pbm[bus], bus, devfn, where, value); + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_read_config_byte(pbm, bus, devfn, where, value); + return pbm_read_config_byte(pbm, bus, devfn, where, value); } int pcibios_read_config_word (unsigned char bus, unsigned char devfn, unsigned char where, unsigned short *value) { - return pbm_read_config_word(bus2pbm[bus], bus, devfn, where, value); + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_read_config_word(pbm, bus, devfn, where, value); + return pbm_read_config_word(pbm, bus, devfn, where, value); } int pcibios_read_config_dword (unsigned char bus, unsigned char devfn, unsigned char where, unsigned int *value) { - return pbm_read_config_dword(bus2pbm[bus], bus, devfn, where, value); + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_read_config_dword(pbm, bus, devfn, where, value); + return pbm_read_config_dword(pbm, bus, devfn, where, value); } int pcibios_write_config_byte (unsigned char bus, unsigned char devfn, unsigned char where, unsigned char value) { - return pbm_write_config_byte(bus2pbm[bus], bus, devfn, where, value); + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_write_config_byte(pbm, bus, devfn, where, value); + return pbm_write_config_byte(pbm, bus, devfn, where, value); } int pcibios_write_config_word (unsigned char bus, unsigned char devfn, unsigned char where, unsigned short value) { + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_write_config_word(pbm, bus, devfn, where, value); return pbm_write_config_word(bus2pbm[bus], bus, devfn, where, value); } int pcibios_write_config_dword (unsigned char bus, unsigned char devfn, unsigned char where, unsigned int value) { + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_write_config_dword(pbm, bus, devfn, where, value); return pbm_write_config_dword(bus2pbm[bus], bus, devfn, where, value); } @@ -1692,4 +2397,9 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, return err; } +__initfunc(char *pcibios_setup(char *str)) +{ + return str; +} + #endif diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 932addbb4..cdc8f47de 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.18 1997/12/18 02:43:00 ecd Exp $ +/* $Id: setup.c,v 1.20 1998/02/24 17:02:39 jj Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -55,11 +55,6 @@ struct screen_info screen_info = { unsigned int phys_bytes_of_ram, end_of_phys_memory; -unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) -{ - return memory_start; -} - /* Typing sync at the prom prompt calls the function pointed to by * the sync callback which I set to the following function. * This should sync all filesystems and return, for now it just @@ -413,14 +408,17 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) extern char *sparc_cpu_type[]; extern char *sparc_fpu_type[]; -extern char *smp_info(void); -extern char *mmu_info(void); +extern int smp_info(char *); +extern int smp_bogo(char *); +extern int mmu_info(char *); int get_cpuinfo(char *buffer) { int cpuid=smp_processor_id(); + int len; - return sprintf(buffer, "cpu\t\t: %s\n" + len = sprintf(buffer, + "cpu\t\t: %s\n" "fpu\t\t: %s\n" "promlib\t\t: Version 3 Revision %d\n" "prom\t\t: %d.%d.%d\n" @@ -429,33 +427,22 @@ int get_cpuinfo(char *buffer) "ncpus active\t: %d\n" #ifndef __SMP__ "BogoMips\t: %lu.%02lu\n" -#else - "Cpu0Bogo\t: %lu.%02lu\n" - "Cpu1Bogo\t: %lu.%02lu\n" - "Cpu2Bogo\t: %lu.%02lu\n" - "Cpu3Bogo\t: %lu.%02lu\n" -#endif - "%s" -#ifdef __SMP__ - "%s" #endif , sparc_cpu_type[cpuid], sparc_fpu_type[cpuid], prom_rev, prom_prev >> 16, (prom_prev >> 8) & 0xff, prom_prev & 0xff, - linux_num_cpus, smp_num_cpus, + linux_num_cpus, smp_num_cpus #ifndef __SMP__ - loops_per_sec/500000, (loops_per_sec/5000) % 100, -#else - cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100, - cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100, - cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100, - cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100, + , loops_per_sec/500000, (loops_per_sec/5000) % 100 #endif - mmu_info() + ); #ifdef __SMP__ - , smp_info() + len += smp_bogo(buffer + len); #endif - ); - + len += mmu_info(buffer + len); +#ifdef __SMP__ + len += smp_info(buffer + len); +#endif + return len; } diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 8ca15c80b..269ff413d 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.34 1997/12/15 15:04:49 jj Exp $ +/* $Id: signal32.c,v 1.35 1998/04/01 07:00:43 davem Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -1023,6 +1023,7 @@ static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka, spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,signr); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); } } diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 932534c05..ca2aa4360 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -12,6 +12,7 @@ #include <linux/interrupt.h> #include <linux/kernel_stat.h> #include <linux/delay.h> +#include <linux/init.h> #include <asm/head.h> #include <asm/ptrace.h> @@ -25,12 +26,14 @@ #include <asm/hardirq.h> #include <asm/softirq.h> #include <asm/uaccess.h> +#include <asm/timer.h> #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> extern int linux_num_cpus; extern void calibrate_delay(void); +extern unsigned prom_cpu_nodes[]; volatile int smp_processors_ready = 0; unsigned long cpu_present_map = 0; @@ -39,35 +42,46 @@ int smp_threads_ready = 0; struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64))); -static unsigned char boot_cpu_id = 0; +static unsigned char boot_cpu_id __initdata = 0; static int smp_activated = 0; volatile int cpu_number_map[NR_CPUS]; -volatile int cpu_logical_map[NR_CPUS]; +volatile int __cpu_logical_map[NR_CPUS]; struct klock_info klock_info = { KLOCK_CLEAR, 0 }; -void smp_setup(char *str, int *ints) +__initfunc(void smp_setup(char *str, int *ints)) { /* XXX implement me XXX */ } -static char smp_buf[512]; +int smp_info(char *buf) +{ + int len = 7, i; + + strcpy(buf, "State:\n"); + for (i = 0; i < NR_CPUS; i++) + if(cpu_present_map & (1UL << i)) + len += sprintf(buf + len, + "CPU%d:\t\t%s\n", + i, klock_info.akp == i ? "akp" : "online"); + return len; +} -char *smp_info(void) +int smp_bogo(char *buf) { - /* XXX not SMP safe and need to support up to 64 penguins */ - sprintf(smp_buf, -" CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n" -"State: %s\t\t%s\t\t%s\t\t%s\n", -(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline", -(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline", -(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline", -(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline"); - return smp_buf; + int len = 0, i; + + for (i = 0; i < NR_CPUS; i++) + if(cpu_present_map & (1UL << i)) + len += sprintf(buf + len, + "Cpu%dBogo\t: %lu.%02lu\n", + i, cpu_data[i].udelay_val / 500000, + (cpu_data[i].udelay_val / 5000) % 100); + return len; } -void smp_store_cpu_info(int id) +__initfunc(void smp_store_cpu_info(int id)) { cpu_data[id].udelay_val = loops_per_sec; cpu_data[id].irq_count = 0; @@ -80,7 +94,7 @@ void smp_store_cpu_info(int id) extern void distribute_irqs(void); -void smp_commence(void) +__initfunc(void smp_commence(void)) { distribute_irqs(); } @@ -92,7 +106,7 @@ static volatile unsigned long callin_flag = 0; extern void inherit_locked_prom_mappings(int save_p); extern void cpu_probe(void); -void smp_callin(void) +__initfunc(void smp_callin(void)) { int cpuid = hard_smp_processor_id(); @@ -156,22 +170,24 @@ extern struct prom_cpuinfo linux_cpus[NR_CPUS]; extern unsigned long smp_trampoline; -void smp_boot_cpus(void) +__initfunc(void smp_boot_cpus(void)) { int cpucount = 0, i; printk("Entering UltraSMPenguin Mode...\n"); + boot_cpu_id = hard_smp_processor_id(); smp_tickoffset_init(); __sti(); cpu_present_map = 0; for(i = 0; i < linux_num_cpus; i++) - cpu_present_map |= (1UL << i); + cpu_present_map |= (1UL << linux_cpus[i].mid); for(i = 0; i < NR_CPUS; i++) { cpu_number_map[i] = -1; - cpu_logical_map[i] = -1; + __cpu_logical_map[i] = -1; } cpu_number_map[boot_cpu_id] = 0; - cpu_logical_map[0] = boot_cpu_id; + prom_cpu_nodes[boot_cpu_id] = linux_cpus[0].prom_node; + __cpu_logical_map[0] = boot_cpu_id; klock_info.akp = boot_cpu_id; current->processor = boot_cpu_id; smp_store_cpu_info(boot_cpu_id); @@ -188,13 +204,18 @@ void smp_boot_cpus(void) unsigned long entry = (unsigned long)(&smp_trampoline); struct task_struct *p; int timeout; + int no; + extern unsigned long phys_base; - entry -= KERNBASE; + entry += phys_base - KERNBASE; kernel_thread(start_secondary, NULL, CLONE_PID); p = task[++cpucount]; p->processor = i; callin_flag = 0; - prom_startcpu(linux_cpus[i].prom_node, + for (no = 0; no < linux_num_cpus; no++) + if (linux_cpus[no].mid == i) + break; + prom_startcpu(linux_cpus[no].prom_node, entry, ((unsigned long)p)); for(timeout = 0; timeout < 5000000; timeout++) { if(callin_flag) @@ -202,8 +223,9 @@ void smp_boot_cpus(void) udelay(100); } if(callin_flag) { - cpu_number_map[i] = i; - cpu_logical_map[i] = i; + cpu_number_map[i] = cpucount; + prom_cpu_nodes[i] = linux_cpus[no].prom_node; + __cpu_logical_map[cpucount] = i; } else { cpucount--; printk("Processor %d is stuck.\n", i); @@ -248,9 +270,9 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) /* #define XCALL_DEBUG */ -static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, int cpu) +static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu) { - u64 result, target = (((unsigned long)linux_cpus[cpu].mid) << 14) | 0x70; + u64 result, target = (cpu << 14) | 0x70; int stuck; #ifdef XCALL_DEBUG @@ -307,12 +329,15 @@ void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2) if(smp_processors_ready) { unsigned long mask = (cpu_present_map & ~(1UL<<smp_processor_id())); u64 pstate, data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff)); - int i, ncpus = smp_num_cpus; + int i, ncpus = smp_num_cpus - 1; __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); - for(i = 0; i < ncpus; i++) { - if(mask & (1UL << i)) + for(i = 0; i < NR_CPUS; i++) { + if(mask & (1UL << i)) { xcall_deliver(data0, data1, data2, pstate, i); + ncpus--; + } + if (!ncpus) break; } /* NOTE: Caller runs local copy on master. */ } @@ -489,13 +514,14 @@ static inline void sparc64_do_profile(unsigned long pc) #endif } -static unsigned long real_tick_offset, current_tick_offset; +static unsigned long current_tick_offset; #define prof_multiplier(__cpu) cpu_data[(__cpu)].multiplier #define prof_counter(__cpu) cpu_data[(__cpu)].counter extern void update_one_process(struct task_struct *p, unsigned long ticks, - unsigned long user, unsigned long system); + unsigned long user, unsigned long system, + int cpu); void smp_percpu_timer_interrupt(struct pt_regs *regs) { @@ -503,32 +529,62 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) int cpu = smp_processor_id(); int user = user_mode(regs); + /* + * Check for level 14 softint. + */ + if (!(get_softint() & (1UL << 0))) { + extern void handler_irq(int, struct pt_regs *); + + handler_irq(14, regs); + return; + } + clear_softint((1UL << 0)); do { if(!user) sparc64_do_profile(regs->tpc); if(!--prof_counter(cpu)) { + + if (cpu == boot_cpu_id) { + extern void irq_enter(int, int); + extern void irq_exit(int, int); + + irq_enter(cpu, 0); + kstat.irqs[cpu][0]++; + + timer_tick_interrupt(regs); + + irq_exit(cpu, 0); + } + if(current->pid) { - unsigned int *inc_me; + unsigned int *inc, *inc2; - update_one_process(current, 1, user, !user); + update_one_process(current, 1, user, !user, cpu); if(--current->counter < 0) { current->counter = 0; need_resched = 1; } if(user) { - if(current->priority < DEF_PRIORITY) - inc_me = &kstat.cpu_nice; - else - inc_me = &kstat.cpu_user; + if(current->priority < DEF_PRIORITY) { + inc = &kstat.cpu_nice; + inc2 = &kstat.per_cpu_nice[cpu]; + } else { + inc = &kstat.cpu_user; + inc2 = &kstat.per_cpu_user[cpu]; + } } else { - inc_me = &kstat.cpu_system; + inc = &kstat.cpu_system; + inc2 = &kstat.per_cpu_system[cpu]; } - atomic_inc((atomic_t *)inc_me); + atomic_inc((atomic_t *)inc); + atomic_inc((atomic_t *)inc2); } + prof_counter(cpu) = prof_multiplier(cpu); } + __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" "add %0, %2, %0\n\t" "wr %0, 0x0, %%tick_cmpr\n\t" @@ -538,12 +594,55 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) } while (tick >= compare); } -static void smp_setup_percpu_timer(void) +__initfunc(static void smp_setup_percpu_timer(void)) { int cpu = smp_processor_id(); prof_counter(cpu) = prof_multiplier(cpu) = 1; + if (cpu == boot_cpu_id) { + extern unsigned long tl0_itick; + extern unsigned long tl0_smp_itick; + unsigned long flags; + + save_flags(flags); cli(); + + /* + * Steal TICK_INT interrupts from timer_interrupt(). + */ + __asm__ __volatile__(" + .globl tl0_smp_itick + b,pt %%xcc, 1f + nop + + tl0_smp_itick: + rdpr %%pil, %%g2 + wrpr %%g0, 15, %%pil + b,pt %%xcc, etrap_irq + rd %%pc, %%g7 + call smp_percpu_timer_interrupt + add %%sp, %0, %%o0 + b,pt %%xcc, rtrap + clr %%l6 + + 1:" + : /* no outputs */ + : "i" (STACK_BIAS + REGWIN_SZ)); + + memcpy(&tl0_itick, &tl0_smp_itick, 8 * 4); + + __asm__ __volatile__(" + membar #StoreStore + flush %0 + 0x00 + flush %0 + 0x08 + flush %0 + 0x10 + flush %0 + 0x18" + : /* no outputs */ + : "r" (&tl0_itick)); + + restore_flags(flags); + } + __asm__ __volatile__("rd %%tick, %%g1\n\t" "add %%g1, %0, %%g1\n\t" "wr %%g1, 0x0, %%tick_cmpr" @@ -552,22 +651,17 @@ static void smp_setup_percpu_timer(void) : "g1"); } -static void smp_tickoffset_init(void) +__initfunc(static void smp_tickoffset_init(void)) { - int node; - - node = linux_cpus[0].prom_node; - real_tick_offset = prom_getint(node, "clock-frequency"); - real_tick_offset = real_tick_offset / HZ; - current_tick_offset = real_tick_offset; + current_tick_offset = timer_tick_offset; } -int setup_profiling_timer(unsigned int multiplier) +__initfunc(int setup_profiling_timer(unsigned int multiplier)) { unsigned long flags; int i; - if((!multiplier) || (real_tick_offset / multiplier) < 1000) + if((!multiplier) || (timer_tick_offset / multiplier) < 1000) return -EINVAL; save_and_cli(flags); @@ -575,7 +669,7 @@ int setup_profiling_timer(unsigned int multiplier) if(cpu_present_map & (1UL << i)) prof_multiplier(i) = multiplier; } - current_tick_offset = (real_tick_offset / multiplier); + current_tick_offset = (timer_tick_offset / multiplier); restore_flags(flags); return 0; diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index b776ea06e..0eb16d7bb 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc64_ksyms.c,v 1.27 1997/11/19 07:57:46 jj Exp $ +/* $Id: sparc64_ksyms.c,v 1.33 1998/04/06 16:09:40 jj Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -50,6 +50,7 @@ struct poll { short revents; }; +extern unsigned prom_cpu_nodes[NR_CPUS]; extern void die_if_kernel(char *str, struct pt_regs *regs); extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); @@ -112,6 +113,8 @@ EXPORT_SYMBOL_PRIVATE(global_restore_flags); #else EXPORT_SYMBOL(local_irq_count); #endif +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL_PRIVATE(_lock_kernel); EXPORT_SYMBOL_PRIVATE(_unlock_kernel); @@ -134,7 +137,9 @@ EXPORT_SYMBOL(dma_chain); #endif #if CONFIG_PCI EXPORT_SYMBOL(ebus_chain); -EXPORT_SYMBOL(pci_devices); +EXPORT_SYMBOL(pci_dvma_offset); +EXPORT_SYMBOL(pci_dvma_mask); +EXPORT_SYMBOL(empty_zero_page); #endif /* Solaris/SunOS binary compatibility */ @@ -171,7 +176,6 @@ EXPORT_SYMBOL(__prom_getsibling); /* sparc library symbols */ EXPORT_SYMBOL(bcopy); -EXPORT_SYMBOL(memscan); EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strnlen); EXPORT_SYMBOL(strcpy); @@ -179,7 +183,6 @@ EXPORT_SYMBOL(strncpy); EXPORT_SYMBOL(strcat); EXPORT_SYMBOL(strncat); EXPORT_SYMBOL(strcmp); -EXPORT_SYMBOL(strncmp); EXPORT_SYMBOL(strchr); EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(strpbrk); @@ -201,15 +204,11 @@ EXPORT_SYMBOL(sys_sigsuspend); EXPORT_SYMBOL(sys_getppid); EXPORT_SYMBOL(svr4_getcontext); EXPORT_SYMBOL(svr4_setcontext); -EXPORT_SYMBOL(linux_cpus); +EXPORT_SYMBOL(prom_cpu_nodes); EXPORT_SYMBOL(sys_ioctl); EXPORT_SYMBOL(sys32_ioctl); #endif -#ifdef CONFIG_MATHEMU_MODULE -EXPORT_SYMBOL(handle_mathemu); -#endif - /* Special internal versions of library functions. */ EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(__memset); diff --git a/arch/sparc64/kernel/sunos_ioctl32.c b/arch/sparc64/kernel/sunos_ioctl32.c index d94a7c6d6..a0bfb47ef 100644 --- a/arch/sparc64/kernel/sunos_ioctl32.c +++ b/arch/sparc64/kernel/sunos_ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: sunos_ioctl32.c,v 1.5 1997/09/18 10:37:57 rth Exp $ +/* $Id: sunos_ioctl32.c,v 1.9 1998/03/29 10:10:53 davem Exp $ * sunos_ioctl32.c: SunOS ioctl compatability on sparc64. * * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -18,6 +18,7 @@ #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/mm.h> #include <linux/smp.h> #include <linux/smp_lock.h> @@ -88,15 +89,12 @@ extern asmlinkage int sys_setsid(void); asmlinkage int sunos_ioctl (int fd, u32 cmd, u32 arg) { - struct file *filp; int ret = -EBADF; lock_kernel(); if(fd >= SUNOS_NR_OPEN) goto out; - - filp = current->files->fd[fd]; - if(!filp) + if(!fcheck(fd)) goto out; if(cmd == TIOCSETD) { @@ -168,7 +166,7 @@ asmlinkage int sunos_ioctl (int fd, u32 cmd, u32 arg) ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg); goto out; case _IOW('i', 24, struct ifreq32): - ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg); + ret = sys32_ioctl(fd, SIOCSIFBRDADDR, arg); goto out; case _IOWR('i', 25, struct ifreq32): ret = sys32_ioctl(fd, SIOCGIFNETMASK, arg); diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc64/kernel/sys32.S index 6fb6f739b..44f17ca01 100644 --- a/arch/sparc64/kernel/sys32.S +++ b/arch/sparc64/kernel/sys32.S @@ -1,4 +1,4 @@ -/* $Id: sys32.S,v 1.4 1997/09/09 17:13:29 jj Exp $ +/* $Id: sys32.S,v 1.5 1998/03/24 05:57:56 ecd Exp $ * sys32.S: I-cache tricks for 32-bit compatability layer simple * conversions. * @@ -24,7 +24,7 @@ sys32_mmap: .align 32 .globl sys32_lseek - .globl sys32_chmod, sys32_chown, sys32_mknod + .globl sys32_chmod, sys32_chown, sys32_lchown, sys32_mknod sys32_lseek: sra %o1, 0, %o1 mov %o7, %g1 @@ -46,6 +46,15 @@ sys32_chown: srl %o2, 16, %o2 call sys_chown mov %g1, %o7 +sys32_lchown: + sll %o1, 16, %o1 + mov %o7, %g1 + sll %o2, 16, %o2 + srl %o0, 0, %o0 + srl %o1, 16, %o1 + srl %o2, 16, %o2 + call sys_lchown + mov %g1, %o7 sys32_mknod: sll %o2, 16, %o2 mov %o7, %g1 diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index e0e69abd9..b5198074d 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.9 1997/12/11 15:15:44 jj Exp $ +/* $Id: sys_sparc.c,v 1.13 1998/03/29 10:10:52 davem Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -10,6 +10,7 @@ #include <linux/types.h> #include <linux/sched.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/mm.h> #include <linux/sem.h> #include <linux/msg.h> @@ -40,7 +41,7 @@ asmlinkage unsigned long sparc_brk(unsigned long brk) unsigned long ret; lock_kernel(); - if(brk >= 0x80000000000ULL) { /* VM hole */ + if(brk >= 0x80000000000UL) { /* VM hole */ ret = current->mm->brk; goto out; } @@ -128,6 +129,16 @@ asmlinkage int sys_ipc (unsigned call, int first, int second, unsigned long thir if (call <= SHMCTL) switch (call) { case SHMAT: + if (first >= 0) { + extern struct shmid_ds *shm_segs[]; + struct shmid_ds *shp = shm_segs[(unsigned int) first % SHMMNI]; + if (shp == IPC_UNUSED || shp == IPC_NOID) { + err = -ENOMEM; + if ((unsigned long)ptr >= 0x80000000000UL - shp->shm_segsz && + (unsigned long)ptr < 0xfffff80000000000UL) + goto out; /* Somebody is trying to fool us */ + } + } err = sys_shmat (first, (char *) ptr, second, (ulong *) third); goto out; case SHMDT: @@ -162,29 +173,39 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, lock_kernel(); if (!(flags & MAP_ANONYMOUS)) { - if (fd >= NR_OPEN || !(file = current->files->fd[fd])){ + file = fget(fd); + if (!file) goto out; - } } retval = -ENOMEM; + len = PAGE_ALIGN(len); if(!(flags & MAP_FIXED) && !addr) { addr = get_unmapped_area(addr, len); - if(!addr){ - goto out; - } + if(!addr) + goto out_putf; } - /* See asm-sparc64/uaccess.h */ retval = -EINVAL; - if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE))) - goto out; - - if(addr >= 0x80000000000ULL) { - retval = current->mm->brk; - goto out; + if (current->tss.flags & SPARC_FLAG_32BIT) { + if (len > 0xf0000000UL || addr > 0xf0000000UL - len) + goto out_putf; + } else { + if (len >= 0x80000000000UL || + (addr < 0x80000000000UL && + addr > 0x80000000000UL-len)) + goto out_putf; + if (addr >= 0x80000000000ULL && addr < 0xfffff80000000000UL) { + /* VM hole */ + retval = current->mm->brk; + goto out_putf; + } } retval = do_mmap(file, addr, len, prot, flags, off); + +out_putf: + if (file) + fput(file); out: unlock_kernel(); return retval; diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 66993ebcb..2844a4bf2 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,7 +1,7 @@ -/* $Id: sys_sparc32.c,v 1.71 1997/12/11 15:15:11 jj Exp $ +/* $Id: sys_sparc32.c,v 1.77 1998/03/29 10:10:50 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) * * These routines maintain argument size conversion between 32bit and 64bit @@ -10,11 +10,12 @@ #include <linux/config.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/signal.h> #include <linux/utime.h> #include <linux/resource.h> -#include <linux/sched.h> #include <linux/times.h> #include <linux/utime.h> #include <linux/utsname.h> @@ -41,6 +42,7 @@ #include <linux/module.h> #include <linux/poll.h> #include <linux/personality.h> +#include <linux/stat.h> #include <asm/types.h> #include <asm/ipc.h> @@ -59,7 +61,6 @@ extern char * getname_quicklist; extern int getname_quickcount; extern struct semaphore getname_quicklock; -extern int kerneld_msqid; /* Tuning: increase locality by reusing same pages again... * if getname_quicklist becomes too long on low memory machines, either a limit @@ -324,19 +325,9 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u if (!p) err = -ENOMEM; else { err = 0; - if (first == kerneld_msqid) { - *(int *)p->mtext = 0; - if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_from_user(&p->mtext[4], &(((struct msgbuf32 *)A(ptr))->mtext[0]), 4) || - __copy_from_user(&p->mtext[8], &(((struct msgbuf32 *)A(ptr))->mtext[4]), second-4)) - err = -EFAULT; - else - second += 4; - } else { - if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second)) - err = -EFAULT; - } + if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || + __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second)) + err = -EFAULT; if (!err) { mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); @@ -379,18 +370,9 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u if (err < 0) goto out; - if (first == kerneld_msqid) { - if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[0]), &p->mtext[4], 4) || - __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[4]), &p->mtext[8], err-8)) - err = -EFAULT; - else - err -= 4; - } else { - if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err)) - err = -EFAULT; - } + if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || + __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err)) + err = -EFAULT; kfree (p); goto out; } @@ -939,14 +921,12 @@ asmlinkage int old32_readdir(unsigned int fd, u32 dirent, unsigned int count) { int error = -EBADF; struct file * file; + struct inode * inode; struct readdir_callback32 buf; lock_kernel(); - if(fd >= NR_OPEN) - goto out; - - file = current->files->fd[fd]; - if(!file) + file = fget(fd); + if (!file) goto out; buf.count = 0; @@ -954,12 +934,18 @@ asmlinkage int old32_readdir(unsigned int fd, u32 dirent, unsigned int count) error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; - + goto out_putf; + + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, fillonedir); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; error = buf.count; + +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -1006,16 +992,14 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count) { struct file * file; + struct inode * inode; struct linux_dirent32 * lastdirent; struct getdents_callback32 buf; int error = -EBADF; lock_kernel(); - if(fd >= NR_OPEN) - goto out; - - file = current->files->fd[fd]; - if(!file) + file = fget(fd); + if (!file) goto out; buf.current_dir = (struct linux_dirent32 *) A(dirent); @@ -1025,17 +1009,22 @@ asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count) error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; + goto out_putf; + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, filldir); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; lastdirent = buf.previous; error = buf.error; if(lastdirent) { put_user(file->f_pos, &lastdirent->d_off); error = count - buf.count; } +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -1258,6 +1247,27 @@ asmlinkage int sys32_newfstat(unsigned int fd, u32 statbuf) return ret; } +extern asmlinkage int sys_xstat(int ver, char *filename, struct stat64 * statbuf); + +asmlinkage int sys32_xstat(int ver, u32 file, u32 statbuf) +{ + switch (ver & __XSTAT_VER_MASK) { + case __XSTAT_VER_1: + switch (ver & __XSTAT_VER_TYPEMASK) { + case __XSTAT_VER_XSTAT: + return sys32_newstat(file, statbuf); + case __XSTAT_VER_LXSTAT: + return sys32_newlstat(file, statbuf); + case __XSTAT_VER_FXSTAT: + return sys32_newfstat(file, statbuf); + } + return -EINVAL; + case __XSTAT_VER_2: + return sys_xstat(ver, (char *)A(file), (struct stat64 *)A(statbuf)); + } + return -EINVAL; +} + extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2); asmlinkage int sys32_sysfs(int option, u32 arg1, u32 arg2) @@ -2222,18 +2232,19 @@ asmlinkage int sys32_sendmsg(int fd, u32 user_msg, unsigned user_flags) char address[MAX_SOCK_ADDR]; struct iovec iov[UIO_FASTIOV]; unsigned char ctl[sizeof(struct cmsghdr) + 20]; - struct msghdr kern_msg; - int err; - int total_len; unsigned char *ctl_buf = ctl; + struct msghdr kern_msg; + int err, total_len; if(msghdr_from_user32_to_kern(&kern_msg, (struct msghdr32 *)A(user_msg))) return -EFAULT; if(kern_msg.msg_iovlen > UIO_MAXIOV) return -EINVAL; - total_len = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); - if(total_len < 0) - return total_len; + err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + if(kern_msg.msg_controllen) { struct cmsghdr32 *ucmsg = (struct cmsghdr32 *)kern_msg.msg_control; unsigned long *kcmsg; @@ -2241,41 +2252,40 @@ asmlinkage int sys32_sendmsg(int fd, u32 user_msg, unsigned user_flags) if(kern_msg.msg_controllen > sizeof(ctl) && kern_msg.msg_controllen <= 256) { + err = -ENOBUFS; ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL); - if(!ctl_buf) { - if(kern_msg.msg_iov != iov) - kfree(kern_msg.msg_iov); - return -ENOBUFS; - } + if(!ctl_buf) + goto out_freeiov; } __get_user(cmlen, &ucmsg->cmsg_len); kcmsg = (unsigned long *) ctl_buf; *kcmsg++ = (unsigned long)cmlen; + err = -EFAULT; if(copy_from_user(kcmsg, &ucmsg->cmsg_level, - kern_msg.msg_controllen - sizeof(__kernel_size_t32))) { - if(ctl_buf != ctl) - kfree_s(ctl_buf, kern_msg.msg_controllen); - if(kern_msg.msg_iov != iov) - kfree(kern_msg.msg_iov); - return -EFAULT; - } + kern_msg.msg_controllen - sizeof(__kernel_size_t32))) + goto out_freectl; kern_msg.msg_control = ctl_buf; } kern_msg.msg_flags = user_flags; lock_kernel(); - if(current->files->fd[fd]->f_flags & O_NONBLOCK) - kern_msg.msg_flags |= MSG_DONTWAIT; - if((sock = sockfd_lookup(fd, &err)) != NULL) { + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; err = sock_sendmsg(sock, &kern_msg, total_len); sockfd_put(sock); } unlock_kernel(); +out_freectl: + /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ if(ctl_buf != ctl) - kfree_s(ctl_buf, kern_msg.msg_controllen); + kfree(ctl_buf); +out_freeiov: if(kern_msg.msg_iov != iov) kfree(kern_msg.msg_iov); +out: return err; } @@ -2299,17 +2309,18 @@ asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags) uaddr = kern_msg.msg_name; uaddr_len = &((struct msghdr32 *)A(user_msg))->msg_namelen; err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); - if(err < 0) - return err; + if (err < 0) + goto out; total_len = err; cmsg_ptr = (unsigned long) kern_msg.msg_control; kern_msg.msg_flags = 0; lock_kernel(); - if(current->files->fd[fd]->f_flags & O_NONBLOCK) - user_flags |= MSG_DONTWAIT; - if((sock = sockfd_lookup(fd, &err)) != NULL) { + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; err = sock_recvmsg(sock, &kern_msg, total_len, user_flags); if(err >= 0) len = err; @@ -2317,8 +2328,6 @@ asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags) } unlock_kernel(); - if(kern_msg.msg_iov != iov) - kfree(kern_msg.msg_iov); if(uaddr != NULL && err >= 0) err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); if(err >= 0) { @@ -2330,6 +2339,10 @@ asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags) &((struct msghdr32 *)A(user_msg))->msg_controllen); } } + + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: if(err < 0) return err; return len; @@ -2838,25 +2851,25 @@ asmlinkage int sys32_get_kernel_syms(u32 table) #else /* CONFIG_MODULES */ asmlinkage unsigned long -sys_create_module(const char *name_user, size_t size) +sys32_create_module(const char *name_user, size_t size) { return -ENOSYS; } asmlinkage int -sys_init_module(const char *name_user, struct module *mod_user) +sys32_init_module(const char *name_user, struct module *mod_user) { return -ENOSYS; } asmlinkage int -sys_delete_module(const char *name_user) +sys32_delete_module(const char *name_user) { return -ENOSYS; } asmlinkage int -sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, +sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize, size_t *ret) { /* Let the program know about the new interface. Not that @@ -2868,7 +2881,7 @@ sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, } asmlinkage int -sys_get_kernel_syms(struct kernel_sym *table) +sys32_get_kernel_syms(struct kernel_sym *table) { return -ENOSYS; } @@ -3327,3 +3340,19 @@ asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, u32 ubuf, { return sys_pwrite(fd, (char *) A(ubuf), count, pos); } + + +extern asmlinkage int sys_personality(unsigned long); + +asmlinkage int sys32_personality(unsigned long personality) +{ + int ret; + lock_kernel(); + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + unlock_kernel(); + if (ret == PER_LINUX32) + ret = PER_LINUX; + return ret; +} diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index 4af388b99..ad7bac534 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos32.c,v 1.7 1997/12/11 15:15:19 jj Exp $ +/* $Id: sys_sunos32.c,v 1.11 1998/03/29 10:10:55 davem Exp $ * sys_sunos32.c: SunOS binary compatability layer on sparc64. * * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -16,6 +16,7 @@ #include <linux/mm.h> #include <linux/swap.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/resource.h> #include <linux/ipc.h> #include <linux/shm.h> @@ -70,23 +71,30 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of flags &= ~MAP_NORESERVE; } retval = -EBADF; - if(!(flags & MAP_ANONYMOUS)) - if(fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd])) + if(!(flags & MAP_ANONYMOUS)) { + if(fd >= SUNOS_NR_OPEN) goto out; + file = fget(fd); + if (!file) + goto out; + if (file->f_dentry && file->f_dentry->d_inode) { + struct inode * inode = file->f_dentry->d_inode; + if(MAJOR(inode->i_rdev) == MEM_MAJOR && + MINOR(inode->i_rdev) == 5) { + flags |= MAP_ANONYMOUS; + fput(file); + file = NULL; + } + } + } + retval = -ENOMEM; if(!(flags & MAP_FIXED) && !addr) { unsigned long attempt = get_unmapped_area(addr, len); if(!attempt || (attempt >= 0xf0000000UL)) - goto out; + goto out_putf; addr = (u32) attempt; } - if(file->f_dentry && file->f_dentry->d_inode) { - if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR && - MINOR(file->f_dentry->d_inode->i_rdev) == 5) { - flags |= MAP_ANONYMOUS; - file = 0; - } - } if(!(flags & MAP_FIXED)) addr = 0; ret_type = flags & _MAP_NEW; @@ -98,6 +106,9 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of (unsigned long) off); if(!ret_type) retval = ((retval < 0xf0000000) ? 0 : retval); +out_putf: + if (file) + fput(file); out: unlock_kernel(); return (u32) retval; @@ -372,6 +383,7 @@ static int sunos_filldir(void * __buf, const char * name, int namlen, asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt) { struct file * file; + struct inode * inode; struct sunos_dirent * lastdirent; struct sunos_dirent_callback buf; int error = -EBADF; @@ -381,32 +393,39 @@ asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt) if(fd >= SUNOS_NR_OPEN) goto out; - file = current->files->fd[fd]; + file = fget(fd); if(!file) goto out; error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; + goto out_putf; error = -EINVAL; if(cnt < (sizeof(struct sunos_dirent) + 255)) - goto out; + goto out_putf; buf.curr = (struct sunos_dirent *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, sunos_filldir); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; + lastdirent = buf.previous; error = buf.error; if (lastdirent) { put_user(file->f_pos, &lastdirent->d_off); error = cnt - buf.count; } + +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -454,43 +473,51 @@ static int sunos_filldirentry(void * __buf, const char * name, int namlen, asmlinkage int sunos_getdirentries(unsigned int fd, u32 u_dirent, int cnt, u32 u_basep) { + void *dirent = (void *) A(u_dirent); + unsigned int *basep = (unsigned int *)A(u_basep); struct file * file; + struct inode * inode; struct sunos_direntry * lastdirent; - struct sunos_direntry_callback buf; int error = -EBADF; - void *dirent = (void *) A(u_dirent); - unsigned int *basep = (unsigned int *)A(u_basep); + struct sunos_direntry_callback buf; lock_kernel(); if(fd >= SUNOS_NR_OPEN) goto out; - file = current->files->fd[fd]; + file = fget(fd); if(!file) goto out; error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; + goto out_putf; error = -EINVAL; if(cnt < (sizeof(struct sunos_direntry) + 255)) - goto out; + goto out_putf; buf.curr = (struct sunos_direntry *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, sunos_filldirentry); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; + lastdirent = buf.previous; error = buf.error; if (lastdirent) { put_user(file->f_pos, basep); error = cnt - buf.count; } + +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -622,14 +649,28 @@ asmlinkage int sunos_pathconf(u32 u_path, int name) extern asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp); -asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp) +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp_x) { int ret; /* SunOS binaries expect that select won't change the tvp contents */ lock_kernel(); current->personality |= STICKY_TIMEOUTS; - ret = sys32_select (width, inp, outp, exp, tvp); + ret = sys32_select (width, inp, outp, exp, tvp_x); + if (ret == -EINTR && tvp_x) { + struct timeval32 *tvp = (struct timeval32 *)A(tvp_x); + time_t sec, usec; + + __get_user(sec, &tvp->tv_sec); + __get_user(usec, &tvp->tv_usec); + if (sec == 0 && usec == 0) + ret = 0; + } unlock_kernel(); return ret; } @@ -1297,8 +1338,11 @@ asmlinkage int sunos_open(u32 filename, int flags, int mode) static inline int check_nonblock(int ret, int fd) { - if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY)) - return -SUNOS_EWOULDBLOCK; + if (ret == -EAGAIN) { + struct file * file = fcheck(fd); + if (file && (file->f_flags & O_NDELAY)) + ret = -SUNOS_EWOULDBLOCK; + } return ret; } @@ -1370,12 +1414,42 @@ asmlinkage int sunos_send(int fd, u32 buff, int len, unsigned flags) return ret; } +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); + +asmlinkage int sunos_socket(int family, int type, int protocol) +{ + int ret, one = 1; + + lock_kernel(); + ret = sys_socket(family, type, protocol); + if (ret < 0) + goto out; + + sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT, + (char *)&one, sizeof(one)); +out: + unlock_kernel(); + return ret; +} + asmlinkage int sunos_accept(int fd, u32 sa, u32 addrlen) { - int ret; + int ret, one = 1; lock_kernel(); - ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa), (int *)A(addrlen)), fd); + while (1) { + ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa), + (int *)A(addrlen)), fd); + if (ret != -ENETUNREACH && ret != -EHOSTUNREACH) + break; + } + if (ret < 0) + goto out; + + sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT, + (char *)&one, sizeof(one)); +out: unlock_kernel(); return ret; } diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 48ae0ecdf..6389681dd 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.37 1997/12/24 17:27:31 ecd Exp $ +/* $Id: systbls.S,v 1.41 1998/03/24 05:57:57 ecd Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -19,8 +19,8 @@ sys_call_table32: /*0*/ .word sys_setup, sys_exit, sys_fork, sys_read, sys_write /*5*/ .word sys_open, sys_close, sys32_wait4, sys_creat, sys_link -/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys32_mknod -/*15*/ .word sys32_chmod, sys32_chown, sparc_brk, sys_nis_syscall, sys32_lseek +/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys32_xstat, sys32_mknod +/*15*/ .word sys32_chmod, sys32_lchown, sparc_brk, sys_xmknod, sys32_lseek /*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid /*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys32_pause /*30*/ .word sys32_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice @@ -55,7 +55,7 @@ sys_call_table32: .word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall /*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module .word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname -/*190*/ .word sys32_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*190*/ .word sys32_init_module, sys32_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys32_sigaction, sys_sgetmask /*200*/ .word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys_uselib, old32_readdir .word sys_nis_syscall, sys32_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall @@ -78,8 +78,8 @@ sys_call_table64: sys_call_table: /*0*/ .word sys_setup, sys_exit, sys_fork, sys_read, sys_write /*5*/ .word sys_open, sys_close, sys_wait4, sys_creat, sys_link -/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod -/*15*/ .word sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek +/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_xstat, sys_mknod +/*15*/ .word sys_chmod, sys_lchown, sparc_brk, sys_xmknod, sys_lseek /*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid /*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_nis_syscall /*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice @@ -117,7 +117,7 @@ sys_call_table: /*190*/ .word sys_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_sigaction, sys_sgetmask /*200*/ .word sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall - .word sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall /*210*/ .word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo .word sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex /*220*/ .word sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid @@ -139,7 +139,7 @@ sunos_sys_table: .word sys_close, sunos_wait4, sys_creat .word sys_link, sys_unlink, sunos_execv .word sys_chdir, sunos_nosys, sys32_mknod - .word sys32_chmod, sys32_chown, sunos_brk + .word sys32_chmod, sys32_lchown, sunos_brk .word sunos_nosys, sys32_lseek, sunos_getpid .word sunos_nosys, sunos_nosys, sunos_nosys .word sunos_getuid, sunos_nosys, sys_ptrace @@ -166,7 +166,7 @@ sunos_sys_table: .word sys32_getitimer, sys_gethostname, sys_sethostname .word sunos_getdtablesize, sys_dup2, sunos_nop .word sys32_fcntl, sunos_select, sunos_nop - .word sys_fsync, sys_setpriority, sys_socket + .word sys_fsync, sys_setpriority, sunos_socket .word sys_connect, sunos_accept /*100*/ .word sys_getpriority, sunos_send, sunos_recv .word sunos_nosys, sys_bind, sunos_setsockopt diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index 8b0152231..debb08888 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1,7 +1,8 @@ -/* $Id: time.c,v 1.12 1997/08/22 20:12:13 davem Exp $ +/* $Id: time.c,v 1.13 1998/03/15 17:23:47 ecd Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * * Based largely on code which is: * @@ -42,29 +43,64 @@ static int set_rtc_mmss(unsigned long); * NOTE: On SUN5 systems the ticker interrupt comes in using 2 * interrupts, one at level14 and one with softint bit 0. */ -extern struct sun5_timer *linux_timers; +unsigned long timer_tick_offset; +static unsigned long timer_tick_compare; +static unsigned long timer_ticks_per_usec; -static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static __inline__ void timer_check_rtc(void) { /* 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. */ 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 */ + 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 */ + } +} + +static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + unsigned long ticks; + + do { + do_timer(regs); + + __asm__ __volatile__(" + rd %%tick_cmpr, %0 + add %0, %2, %0 + wr %0, 0, %%tick_cmpr + rd %%tick, %1" + : "=&r" (timer_tick_compare), "=r" (ticks) + : "r" (timer_tick_offset)); + } while (ticks >= timer_tick_compare); + + timer_check_rtc(); } +#ifdef __SMP__ +void timer_tick_interrupt(struct pt_regs *regs) +{ + do_timer(regs); + + /* + * Only keep timer_tick_offset uptodate, but don't set TICK_CMPR. + */ + __asm__ __volatile__(" + rd %%tick_cmpr, %0 + add %0, %1, %0" + : "=&r" (timer_tick_compare) + : "r" (timer_tick_offset)); + + timer_check_rtc(); +} +#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. @@ -318,29 +354,32 @@ __initfunc(void time_init(void)) */ } -extern void init_timers(void (*func)(int, void *, struct pt_regs *)); +extern void init_timers(void (*func)(int, void *, struct pt_regs *), + unsigned long *); __initfunc(void sun4u_start_timers(void)) { - init_timers(timer_interrupt); + unsigned long clock; + + init_timers(timer_interrupt, &clock); + timer_tick_offset = clock / HZ; + timer_ticks_per_usec = clock / 1000000; } static __inline__ unsigned long do_gettimeoffset(void) { - unsigned long offset = 0; - unsigned int count; - - /* XXX -DaveM */ -#if 0 - count = (*master_l10_counter >> 10) & 0x1fffff; -#else - count = 0; -#endif + unsigned long ticks; - if(test_bit(TIMER_BH, &bh_active)) - offset = 1000000; - - return offset + count; + __asm__ __volatile__(" + rd %%tick, %%g1 + add %1, %%g1, %0 + sub %0, %2, %0 +" + : "=r" (ticks) + : "r" (timer_tick_offset), "r" (timer_tick_compare) + : "g1", "g2"); + + return ticks / timer_ticks_per_usec; } void do_gettimeofday(struct timeval *tv) @@ -353,13 +392,16 @@ void do_gettimeofday(struct timeval *tv) * nucleus atomic quad 128-bit loads. */ __asm__ __volatile__(" - sethi %hi(linux_timers), %o1 + sethi %hi(timer_tick_offset), %g3 sethi %hi(xtime), %g2 - ldx [%o1 + %lo(linux_timers)], %g3 + sethi %hi(timer_tick_compare), %g1 + ldx [%g3 + %lo(timer_tick_offset)], %g3 or %g2, %lo(xtime), %g2 + or %g1, %lo(timer_tick_compare), %g1 1: ldda [%g2] 0x24, %o4 membar #LoadLoad | #MemIssue - ldx [%g3], %o1 + rd %tick, %o1 + ldx [%g1], %g7 membar #LoadLoad | #MemIssue ldda [%g2] 0x24, %o2 membar #LoadLoad @@ -367,24 +409,28 @@ void do_gettimeofday(struct timeval *tv) xor %o5, %o3, %o3 orcc %o2, %o3, %g0 bne,pn %xcc, 1b - cmp %o1, 0 - bge,pt %icc, 1f - sethi %hi(tick), %o3 - ldx [%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 + sethi %hi(lost_ticks), %o2 + sethi %hi(timer_ticks_per_usec), %o3 + ldx [%o2 + %lo(lost_ticks)], %o2 + add %g3, %o1, %o1 + ldx [%o3 + %lo(timer_ticks_per_usec)], %o3 + sub %o1, %g7, %o1 + brz,pt %o2, 1f + udivx %o1, %o3, %o1 + sethi %hi(10000), %g2 + or %g2, %lo(10000), %g2 + add %o1, %g2, %o1 +1: sethi %hi(1000000), %o2 + srlx %o5, 32, %o5 or %o2, %lo(1000000), %o2 + add %o5, %o1, %o5 cmp %o5, %o2 bl,a,pn %xcc, 1f stx %o4, [%o0 + 0x0] add %o4, 0x1, %o4 sub %o5, %o2, %o5 stx %o4, [%o0 + 0x0] -1: stx %o5, [%o0 + 0x8]"); +1: st %o5, [%o0 + 0x8]"); } void do_settimeofday(struct timeval *tv) @@ -401,6 +447,7 @@ void do_settimeofday(struct timeval *tv) time_state = TIME_BAD; time_maxerror = 0x70000000; time_esterror = 0x70000000; + sti(); } diff --git a/arch/sparc64/kernel/trampoline.S b/arch/sparc64/kernel/trampoline.S index b5ca851ac..8604b3301 100644 --- a/arch/sparc64/kernel/trampoline.S +++ b/arch/sparc64/kernel/trampoline.S @@ -1,4 +1,4 @@ -/* $Id: trampoline.S,v 1.2 1997/07/28 02:57:32 davem Exp $ +/* $Id: trampoline.S,v 1.3 1998/02/22 21:06:11 jj Exp $ * trampoline.S: Jump start slave processors on sparc64. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -172,8 +172,10 @@ bounce: mov %o2, %g6 wrpr %o1, (PSTATE_MG | PSTATE_IE), %pstate - sethi %hi(0x1ff8), %g2 - or %g2, %lo(0x1ff8), %g2 +#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) + sethi %uhi(KERN_HIGHBITS), %g2 + sllx %g2, 32, %g2 +#undef KERN_HIGHBITS ldx [%o2 + AOFF_task_mm], %g6 ldx [%g6 + AOFF_mm_pgd], %g6 clr %g7 diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 6e1d30990..b255c7623 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.44 1998/01/09 16:39:35 jj Exp $ +/* $Id: traps.c,v 1.49 1998/04/06 16:09:38 jj Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -191,7 +191,7 @@ void bad_trap (struct pt_regs *regs, long lvl) die_if_kernel ("Kernel bad trap", regs); current->tss.sig_desc = SUBSIG_BADTRAP(lvl - 0x100); current->tss.sig_address = regs->tpc; - send_sig(SIGILL, current, 1); + force_sig(SIGILL, current); unlock_kernel (); } @@ -225,7 +225,9 @@ void data_access_exception (struct pt_regs *regs) return; } } - send_sig(SIGSEGV, current, 1); + lock_kernel(); + force_sig(SIGSEGV, current); + unlock_kernel(); } #ifdef CONFIG_PCI @@ -235,16 +237,35 @@ extern volatile int pci_poke_in_progress; extern volatile int pci_poke_faulted; #endif +/* When access exceptions happen, we must do this. */ +static __inline__ void clean_and_reenable_l1_caches(void) +{ + unsigned long va; + + /* Clean 'em. */ + for(va = 0; va < (PAGE_SIZE << 1); va += 32) { + spitfire_put_icache_tag(va, 0x0); + spitfire_put_dcache_tag(va, 0x0); + } + + /* Re-enable. */ + __asm__ __volatile__("flush %%g6\n\t" + "membar #Sync\n\t" + "stxa %0, [%%g0] %1\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC | + LSU_CONTROL_IM | LSU_CONTROL_DM), + "i" (ASI_LSU_CONTROL) + : "memory"); +} + void do_dae(struct pt_regs *regs) { #ifdef CONFIG_PCI -#ifdef DEBUG_PCI_POKES - prom_printf(" (POKE "); -#endif if(pci_poke_in_progress) { - unsigned long va; #ifdef DEBUG_PCI_POKES - prom_printf("tpc[%016lx] tnpc[%016lx] ", + prom_printf(" (POKE tpc[%016lx] tnpc[%016lx] ", regs->tpc, regs->tnpc); #endif pci_poke_faulted = 1; @@ -255,39 +276,30 @@ void do_dae(struct pt_regs *regs) prom_printf("PCI) "); /* prom_halt(); */ #endif - /* Re-enable I/D caches, Ultra turned them off. */ - for(va = 0; va < (PAGE_SIZE << 1); va += 32) { - spitfire_put_icache_tag(va, 0x0); - spitfire_put_dcache_tag(va, 0x0); - } - __asm__ __volatile__("flush %%g6\n\t" - "membar #Sync\n\t" - "stxa %0, [%%g0] %1\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC | - LSU_CONTROL_IM | LSU_CONTROL_DM), - "i" (ASI_LSU_CONTROL) - : "memory"); + clean_and_reenable_l1_caches(); return; } -#ifdef DEBUG_PCI_POKES - prom_printf("USER) "); - prom_printf("tpc[%016lx] tnpc[%016lx]\n"); - prom_halt(); #endif -#endif - send_sig(SIGSEGV, current, 1); + clean_and_reenable_l1_caches(); + lock_kernel(); + force_sig(SIGSEGV, current); + unlock_kernel(); } void instruction_access_exception (struct pt_regs *regs) { - send_sig(SIGSEGV, current, 1); + clean_and_reenable_l1_caches(); + + lock_kernel(); + force_sig(SIGSEGV, current); + unlock_kernel(); } void do_iae(struct pt_regs *regs) { - send_sig(SIGSEGV, current, 1); + lock_kernel(); + force_sig(SIGSEGV, current); + unlock_kernel(); } void do_fpe_common(struct pt_regs *regs) @@ -312,11 +324,7 @@ void do_fpieee(struct pt_regs *regs) do_fpe_common(regs); } -#ifdef CONFIG_MATHEMU_MODULE -volatile int (*handle_mathemu)(struct pt_regs *, struct fpustate *) = NULL; -#else extern int do_mathemu(struct pt_regs *, struct fpustate *); -#endif void do_fpother(struct pt_regs *regs) { @@ -326,18 +334,7 @@ void do_fpother(struct pt_regs *regs) switch ((f->fsr & 0x1c000)) { case (2 << 14): /* unfinished_FPop */ case (3 << 14): /* unimplemented_FPop */ -#ifdef CONFIG_MATHEMU_MODULE -#ifdef CONFIG_KMOD - if (!handle_mathemu) - request_module("math-emu"); -#endif - if (handle_mathemu) - ret = handle_mathemu(regs, f); -#else -#ifdef CONFIG_MATHEMU ret = do_mathemu(regs, f); -#endif -#endif break; } if (ret) return; @@ -576,27 +573,33 @@ void cache_flush_trap(struct pt_regs *regs) #else #error SMP not supported on sparc64 yet #endif + +#if 0 +/* Broken */ int size = prom_getintdefault(node, "ecache-size", 512*1024); int i, j; - unsigned long addr, page_nr; + unsigned long addr; + struct page *page, *end; regs->tpc = regs->tnpc; regs->tnpc = regs->tnpc + 4; if (!suser()) return; size >>= PAGE_SHIFT; addr = PAGE_OFFSET - PAGE_SIZE; + page = mem_map - 1; + end = mem_map + max_mapnr; for (i = 0; i < size; i++) { do { addr += PAGE_SIZE; - page_nr = MAP_NR(addr); - if (page_nr >= max_mapnr) { + page++; + if (page >= end) return; - } - } while (!PageReserved (mem_map + page_nr)); + } while (!PageReserved(page)); /* E-Cache line size is 64B. Let us pollute it :)) */ for (j = 0; j < PAGE_SIZE; j += 64) __asm__ __volatile__ ("ldx [%0 + %1], %%g1" : : "r" (j), "r" (addr) : "g1"); } +#endif } #endif diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index b22cf82f7..3d17fb3cb 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.22 1997/10/16 07:07:46 jj Exp $ +/* $Id: ttable.S,v 1.23 1998/03/15 17:23:48 ecd Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -6,7 +6,7 @@ #include <linux/config.h> - .globl sparc64_ttable_tl0, sparc64_ttable_tl1 + .globl sparc64_ttable_tl0, tl0_itick, sparc64_ttable_tl1, sparc64_ttable_tl0: tl0_resv000: BOOT_KERNEL BTRAP(0x1) BTRAP(0x2) BTRAP(0x3) @@ -45,7 +45,7 @@ tl0_irq7: TRAP_IRQ(handler_irq, 7) TRAP_IRQ(handler_irq, 8) tl0_irq9: TRAP_IRQ(handler_irq, 9) TRAP_IRQ(handler_irq, 10) tl0_irq11: TRAP_IRQ(handler_irq, 11) TRAP_IRQ(handler_irq, 12) tl0_irq13: TRAP_IRQ(handler_irq, 13) -tl0_itick: TRAP_TICK +tl0_itick: TRAP_IRQ(handler_irq, 14) tl0_irq15: TRAP_IRQ(handler_irq, 15) tl0_resv050: BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55) tl0_resv056: BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b) diff --git a/arch/sparc64/lib/VIScsumcopy.S b/arch/sparc64/lib/VIScsumcopy.S index efd2bfcd5..fff41bab2 100644 --- a/arch/sparc64/lib/VIScsumcopy.S +++ b/arch/sparc64/lib/VIScsumcopy.S @@ -1,4 +1,4 @@ -/* $Id: VIScsumcopy.S,v 1.2 1997/08/19 15:25:22 jj Exp $ +/* $Id: VIScsumcopy.S,v 1.4 1998/04/01 08:29:52 davem Exp $ * VIScsumcopy.S: High bandwidth IP checksumming with simultaneous * copying utilizing the UltraSparc Visual Instruction Set. * @@ -393,22 +393,22 @@ vis0s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ add %src, 128, %src /* IEU0 Group */ ldda [%src-128] %asi, %f0 /* Load Group */ ldda [%src-64] %asi, %f16 /* Load Group */ - fmovd %f48, %f62 /* FPA Group */ - faligndata %f0, %f2, %f48 /* FPA Group */ - fcmpgt32 %f32, %f2, %x1 /* FPM Group */ + fmovd %f48, %f62 /* FPA Group f0 available */ + faligndata %f0, %f2, %f48 /* FPA Group f2 available */ + fcmpgt32 %f32, %f2, %x1 /* FPM Group f4 available */ fpadd32 %f0, %f62, %f0 /* FPA */ - fcmpgt32 %f32, %f4, %x2 /* FPM Group */ + fcmpgt32 %f32, %f4, %x2 /* FPM Group f6 available */ faligndata %f2, %f4, %f50 /* FPA */ - fcmpgt32 %f62, %f0, %x3 /* FPM Group */ + fcmpgt32 %f62, %f0, %x3 /* FPM Group f8 available */ faligndata %f4, %f6, %f52 /* FPA */ - fcmpgt32 %f32, %f6, %x4 /* FPM Group */ + fcmpgt32 %f32, %f6, %x4 /* FPM Group f10 available */ inc %x1 /* IEU0 */ faligndata %f6, %f8, %f54 /* FPA */ - fcmpgt32 %f32, %f8, %x5 /* FPM Group */ + fcmpgt32 %f32, %f8, %x5 /* FPM Group f12 available */ srl %x1, 1, %x1 /* IEU0 */ inc %x2 /* IEU1 */ faligndata %f8, %f10, %f56 /* FPA */ - fcmpgt32 %f32, %f10, %x6 /* FPM Group */ + fcmpgt32 %f32, %f10, %x6 /* FPM Group f14 available */ srl %x2, 1, %x2 /* IEU0 */ add %sum, %x1, %sum /* IEU1 */ faligndata %f10, %f12, %f58 /* FPA */ @@ -451,6 +451,7 @@ vis1s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ add %src, 128 - 8, %src /* IEU0 Group */ ldda [%src-128] %asi, %f0 /* Load Group */ ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f0, %f58 /* FPA Group */ fmovd %f48, %f0 /* FPA Group */ fcmpgt32 %f32, %f2, %x2 /* FPM Group */ faligndata %f2, %f4, %f48 /* FPA */ @@ -503,9 +504,10 @@ vis2s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ add %src, 128 - 16, %src /* IEU0 Group */ ldda [%src-128] %asi, %f0 /* Load Group */ ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f0, %f56 /* FPA Group */ fmovd %f48, %f0 /* FPA Group */ sub %dst, 64, %dst /* IEU0 */ - fzero %f2 /* FPA Group */ + fpsub32 %f2, %f2, %f2 /* FPA Group */ fcmpgt32 %f32, %f4, %x3 /* FPM Group */ faligndata %f4, %f6, %f48 /* FPA */ fcmpgt32 %f32, %f6, %x4 /* FPM Group */ @@ -552,10 +554,11 @@ vis3s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ add %src, 128 - 24, %src /* IEU0 Group */ ldda [%src-128] %asi, %f0 /* Load Group */ ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f0, %f54 /* FPA Group */ fmovd %f48, %f0 /* FPA Group */ sub %dst, 64, %dst /* IEU0 */ - fzero %f2 /* FPA Group */ - fzero %f4 /* FPA Group */ + fpsub32 %f2, %f2, %f2 /* FPA Group */ + fpsub32 %f4, %f4, %f4 /* FPA Group */ fcmpgt32 %f32, %f6, %x4 /* FPM Group */ faligndata %f6, %f8, %f48 /* FPA */ fcmpgt32 %f32, %f8, %x5 /* FPM Group */ @@ -597,11 +600,12 @@ vis4s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ add %src, 128 - 32, %src /* IEU0 Group */ ldda [%src-128] %asi, %f0 /* Load Group */ ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f0, %f52 /* FPA Group */ fmovd %f48, %f0 /* FPA Group */ sub %dst, 64, %dst /* IEU0 */ - fzero %f2 /* FPA Group */ - fzero %f4 /* FPA Group */ - fzero %f6 /* FPA Group */ + fpsub32 %f2, %f2, %f2 /* FPA Group */ + fpsub32 %f4, %f4, %f4 /* FPA Group */ + fpsub32 %f6, %f6, %f6 /* FPA Group */ clr %x4 /* IEU0 */ fcmpgt32 %f32, %f8, %x5 /* FPM Group */ faligndata %f8, %f10, %f48 /* FPA */ @@ -697,9 +701,9 @@ vis6s: add %src, 128 - 48, %src /* IEU0 Group */ clr %x6 /* IEU0 */ fcmpgt32 %f32, %f12, %x7 /* FPM Group */ sub %dst, 64, %dst /* IEU0 */ - faligndata %f12, %f14, %f48 /* FPA */ fcmpgt32 %f32, %f14, %x8 /* FPM Group */ - fmovd %f14, %f50 /* FPA */ + faligndata %f12, %f14, %f48 /* FPA */ + fmovd %f14, %f50 /* FPA Group */ vis6: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, ,f52,f54,f56,f58,f60,f62,f48,f50,f50, ,LDBLK(f32), ,,,,,,STBLK,, diff --git a/arch/sparc64/math-emu/Makefile b/arch/sparc64/math-emu/Makefile index ea816d98e..8f695b1e2 100644 --- a/arch/sparc64/math-emu/Makefile +++ b/arch/sparc64/math-emu/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the FPU Quad (long double) instruction emulation. +# Makefile for the FPU instruction emulation. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here @@ -16,18 +16,10 @@ O_OBJS := math.o fabsq.o faddq.o fdivq.o fdmulq.o fitoq.o \ fmuls.o fmuld.o fdivs.o fdivd.o fsmuld.o \ fstoi.o fdtoi.o fstox.o fdtox.o fstod.o fdtos.o -ifeq ($(CONFIG_MATHEMU),m) -M_OBJS := $(O_TARGET) -endif - .S.s: $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s .S.o: $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o -ifneq ($(CONFIG_MATHEMU),y) -do_it_all: -endif - include $(TOPDIR)/Rules.make diff --git a/arch/sparc64/math-emu/double.h b/arch/sparc64/math-emu/double.h index b68d76790..6aff6fdd5 100644 --- a/arch/sparc64/math-emu/double.h +++ b/arch/sparc64/math-emu/double.h @@ -3,7 +3,7 @@ */ #if _FP_W_TYPE_SIZE < 32 -#error "Here's a nickle kid. Go buy yourself a real computer." +#error "Here's a nickel kid. Go buy yourself a real computer." #endif #if _FP_W_TYPE_SIZE < 64 diff --git a/arch/sparc64/math-emu/fabsq.c b/arch/sparc64/math-emu/fabsq.c index e6aa497c8..e01b02046 100644 --- a/arch/sparc64/math-emu/fabsq.c +++ b/arch/sparc64/math-emu/fabsq.c @@ -1,18 +1,5 @@ -#include "soft-fp.h" -#include "quad.h" - int FABSQ(unsigned long *rd, unsigned long *rs2) { -/* - FP_DECL_Q(A); FP_DECL_Q(R); - - __FP_UNPACK_Q(A, rs2); - _FP_FRAC_COPY_2(R, A); - R_c = A_c; - R_e = A_e; - R_s = 0; - __FP_PACK_Q(rd, R); - */ rd[0] = rs2[0] & 0x7fffffffffffffffUL; rd[1] = rs2[1]; return 1; diff --git a/arch/sparc64/math-emu/fcmpeq.c b/arch/sparc64/math-emu/fcmpeq.c index cb37bc0db..e74b1b06b 100644 --- a/arch/sparc64/math-emu/fcmpeq.c +++ b/arch/sparc64/math-emu/fcmpeq.c @@ -11,11 +11,8 @@ int FCMPEQ(void *rd, void *rs2, void *rs1) rd = (void *)(((long)rd)&~3); __FP_UNPACK_Q(A, rs1); __FP_UNPACK_Q(B, rs2); - FP_CMP_Q(ret, A, B, 3); - switch (ret) { - case 1: ret = 2; break; - case -1: ret = 1; break; - } + FP_CMP_Q(ret, B, A, 3); + if (ret == -1) ret = 2; fsr = *(unsigned long *)rd; switch (fccno) { case 0: fsr &= ~0xc00; fsr |= (ret << 10); break; diff --git a/arch/sparc64/math-emu/fcmpq.c b/arch/sparc64/math-emu/fcmpq.c index 81dadf47a..9effefb1f 100644 --- a/arch/sparc64/math-emu/fcmpq.c +++ b/arch/sparc64/math-emu/fcmpq.c @@ -11,11 +11,8 @@ int FCMPQ(void *rd, void *rs2, void *rs1) rd = (void *)(((long)rd)&~3); __FP_UNPACK_Q(A, rs1); __FP_UNPACK_Q(B, rs2); - FP_CMP_Q(ret, A, B, 3); - switch (ret) { - case 1: ret = 2; break; - case -1: ret = 1; break; - } + FP_CMP_Q(ret, B, A, 3); + if (ret == -1) ret = 2; fsr = *(unsigned long *)rd; switch (fccno) { case 0: fsr &= ~0xc00; fsr |= (ret << 10); break; diff --git a/arch/sparc64/math-emu/fnegq.c b/arch/sparc64/math-emu/fnegq.c index dcdea3202..2251e3308 100644 --- a/arch/sparc64/math-emu/fnegq.c +++ b/arch/sparc64/math-emu/fnegq.c @@ -1,18 +1,7 @@ -#include "soft-fp.h" -#include "quad.h" - int FNEGQ(unsigned long *rd, unsigned long *rs2) { -/* - FP_DECL_Q(A); FP_DECL_Q(R); - - __FP_UNPACK_Q(A, rs2); - FP_NEG_Q(R, A); - __FP_PACK_Q(rd, R); - */ rd[0] = rs2[0] ^ 0x8000000000000000UL; rd[1] = rs2[1]; return 1; } - diff --git a/arch/sparc64/math-emu/math.c b/arch/sparc64/math-emu/math.c index 58ed21062..e0380720f 100644 --- a/arch/sparc64/math-emu/math.c +++ b/arch/sparc64/math-emu/math.c @@ -1,4 +1,4 @@ -/* $Id: math.c,v 1.3 1997/10/15 07:28:55 jj Exp $ +/* $Id: math.c,v 1.4 1998/04/06 16:09:57 jj Exp $ * arch/sparc64/math-emu/math.c * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -7,7 +7,6 @@ * of glibc and has appropriate copyrights in it. */ -#include <linux/module.h> #include <linux/types.h> #include <linux/sched.h> @@ -70,7 +69,6 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f) if(tstate & TSTATE_PRIV) die_if_kernel("FPQuad from kernel", regs); - MOD_INC_USE_COUNT; if(current->tss.flags & SPARC_FLAG_32BIT) pc = (u32)pc; if (get_user(insn, (u32 *)pc) != -EFAULT) { @@ -182,28 +180,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f) func(rd, rs2, rs1); regs->tpc = regs->tnpc; regs->tnpc += 4; - MOD_DEC_USE_COUNT; return 1; } -err: MOD_DEC_USE_COUNT; - return 0; +err: return 0; } - -#ifdef MODULE - -MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz), Richard Henderson (rth@cygnus.com)"); -MODULE_DESCRIPTION("FPU emulation module"); - -extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *); - -int init_module(void) -{ - handle_mathemu = do_mathemu; - return 0; -} - -void cleanup_module(void) -{ - handle_mathemu = NULL; -} -#endif diff --git a/arch/sparc64/math-emu/op-2.h b/arch/sparc64/math-emu/op-2.h index 879b6004f..5999cfc3b 100644 --- a/arch/sparc64/math-emu/op-2.h +++ b/arch/sparc64/math-emu/op-2.h @@ -207,6 +207,12 @@ R##_f1 = _FP_FRAC_WORD_4(_z,1); \ } while (0) +/* This next macro appears to be totally broken. Fortunately nowhere + * seems to use it :-> The problem is that we define _z[4] but + * then use it in _FP_FRAC_SRS_4, which will attempt to access + * _z_f[n] which will cause an error. The fix probably involves + * declaring it with _FP_FRAC_DECL_4, see previous macro. -- PMM 02/1998 + */ #define _FP_MUL_MEAT_2_gmp(fs, R, X, Y) \ do { \ _FP_W_TYPE _x[2], _y[2], _z[4]; \ @@ -226,6 +232,11 @@ /* * Division algorithms: + * This seems to be giving me difficulties -- PMM + * Look, NetBSD seems to be able to comment algorithms. Can't you? + * I've thrown printks at the problem. + * This now appears to work, but I still don't really know why. + * Also, I don't think the result is properly normalised... */ #define _FP_DIV_MEAT_2_udiv_64(fs, R, X, Y) \ @@ -236,10 +247,17 @@ _FP_W_TYPE _n_f3, _n_f2, _n_f1, _n_f0, _r_f1, _r_f0; \ _FP_W_TYPE _q_f1, _q_f0, _m_f1, _m_f0; \ _FP_W_TYPE _rmem[2], _qmem[2]; \ - \ + /* I think this check is to ensure that the result is normalised. \ + * Assuming X,Y normalised (ie in [1.0,2.0)) X/Y will be in \ + * [0.5,2.0). Furthermore, it will be less than 1.0 iff X < Y. \ + * In this case we tweak things. (this is based on comments in \ + * the NetBSD FPU emulation code. ) \ + * We know X,Y are normalised because we ensure this as part of \ + * the unpacking process. -- PMM \ + */ \ if (_FP_FRAC_GT_2(X, Y)) \ { \ - R##_e++; \ +/* R##_e++; */ \ _n_f3 = X##_f1 >> 1; \ _n_f2 = X##_f1 << (_FP_W_TYPE_SIZE - 1) | X##_f0 >> 1; \ _n_f1 = X##_f0 << (_FP_W_TYPE_SIZE - 1); \ @@ -247,14 +265,15 @@ } \ else \ { \ + R##_e--; \ _n_f3 = X##_f1; \ _n_f2 = X##_f0; \ _n_f1 = _n_f0 = 0; \ } \ \ /* Normalize, i.e. make the most significant bit of the \ - denominator set. */ \ - _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs - 1); \ + denominator set. CHANGED: - 1 to nothing -- PMM */ \ + _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs /* -1 */); \ \ /* Do the 256/128 bit division given the 128-bit _fp_udivmodtf4 \ primitive snagged from libgcc2.c. */ \ @@ -295,6 +314,11 @@ \ R##_f1 = _q_f1; \ R##_f0 = _q_f0 | ((_r_f1 | _r_f0) != 0); \ + /* adjust so answer is normalized again. I'm not sure what the \ + * final sz param should be. In practice it's never used since \ + * N is 1 which is always going to be < _FP_W_TYPE_SIZE... \ + */ \ + /* _FP_FRAC_SRS_2(R,1,_FP_WFRACBITS_##fs); */ \ } while (0) @@ -406,3 +430,4 @@ D##_f1 = 0; \ _FP_FRAC_SLL_2(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ } while (0) + diff --git a/arch/sparc64/math-emu/op-4.h b/arch/sparc64/math-emu/op-4.h index 2f917a847..5f7099271 100644 --- a/arch/sparc64/math-emu/op-4.h +++ b/arch/sparc64/math-emu/op-4.h @@ -1,11 +1,41 @@ /* * Basic four-word fraction declaration and manipulation. + * + * When adding quadword support for 32 bit machines, we need + * to be a little careful as double multiply uses some of these + * macros: (in op-2.h) + * _FP_MUL_MEAT_2_wide() uses _FP_FRAC_DECL_4, _FP_FRAC_WORD_4, + * _FP_FRAC_ADD_4, _FP_FRAC_SRS_4 + * _FP_MUL_MEAT_2_gmp() uses _FP_FRAC_SRS_4 (and should use + * _FP_FRAC_DECL_4: it appears to be broken and is not used + * anywhere anyway. ) + * + * I've now fixed all the macros that were here from the sparc64 code. + * [*none* of the shift macros were correct!] -- PMM 02/1998 + * + * The only quadword stuff that remains to be coded is: + * 1) the conversion to/from ints, which requires + * that we check (in op-common.h) that the following do the right thing + * for quadwords: _FP_TO_INT(Q,4,r,X,rsz,rsg), _FP_FROM_INT(Q,4,X,r,rs,rt) + * 2) multiply, divide and sqrt, which require: + * _FP_MUL_MEAT_4_*(R,X,Y), _FP_DIV_MEAT_4_*(R,X,Y), _FP_SQRT_MEAT_4(R,S,T,X,q), + * This also needs _FP_MUL_MEAT_Q and _FP_DIV_MEAT_Q to be defined to + * some suitable _FP_MUL_MEAT_4_* macros in sfp-machine.h. + * [we're free to choose whatever FP_MUL_MEAT_4_* macros we need for + * these; they are used nowhere else. ] */ #define _FP_FRAC_DECL_4(X) _FP_W_TYPE X##_f[4] #define _FP_FRAC_COPY_4(D,S) \ (D##_f[0] = S##_f[0], D##_f[1] = S##_f[1], \ D##_f[2] = S##_f[2], D##_f[3] = S##_f[3]) +/* The _FP_FRAC_SET_n(X,I) macro is intended for use with another + * macro such as _FP_ZEROFRAC_n which returns n comma separated values. + * The result is that we get an expansion of __FP_FRAC_SET_n(X,I0,I1,I2,I3) + * which just assigns the In values to the array X##_f[]. + * This is why the number of parameters doesn't appear to match + * at first glance... -- PMM + */ #define _FP_FRAC_SET_4(X,I) __FP_FRAC_SET_4(X, I) #define _FP_FRAC_HIGH_4(X) (X##_f[3]) #define _FP_FRAC_LOW_4(X) (X##_f[0]) @@ -19,26 +49,32 @@ _down = _FP_W_TYPE_SIZE - _up; \ for (_i = 3; _i > _skip; --_i) \ X##_f[_i] = X##_f[_i-_skip] << _up | X##_f[_i-_skip-1] >> _down; \ - X##_f[_i] <<= _up; \ +/* bugfixed: was X##_f[_i] <<= _up; -- PMM 02/1998 */ \ + X##_f[_i] = X##_f[0] << _up; \ for (--_i; _i >= 0; --_i) \ X##_f[_i] = 0; \ } while (0) +/* This one was broken too */ #define _FP_FRAC_SRL_4(X,N) \ do { \ _FP_I_TYPE _up, _down, _skip, _i; \ _skip = (N) / _FP_W_TYPE_SIZE; \ _down = (N) % _FP_W_TYPE_SIZE; \ _up = _FP_W_TYPE_SIZE - _down; \ - for (_i = 0; _i < 4-_skip; ++_i) \ + for (_i = 0; _i < 3-_skip; ++_i) \ X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \ - X##_f[_i] >>= _down; \ + X##_f[_i] = X##_f[3] >> _down; \ for (++_i; _i < 4; ++_i) \ X##_f[_i] = 0; \ } while (0) -/* Right shift with sticky-lsb. */ +/* Right shift with sticky-lsb. + * What this actually means is that we do a standard right-shift, + * but that if any of the bits that fall off the right hand side + * were one then we always set the LSbit. + */ #define _FP_FRAC_SRS_4(X,N,size) \ do { \ _FP_I_TYPE _up, _down, _skip, _i; \ @@ -48,13 +84,15 @@ _up = _FP_W_TYPE_SIZE - _down; \ for (_s = _i = 0; _i < _skip; ++_i) \ _s |= X##_f[_i]; \ - _s = X##_f[_i] << _up; \ - X##_f[0] = X##_f[_skip] >> _down | X##_f[_skip+1] << _up | (_s != 0); \ - for (_i = 1; _i < 4-_skip; ++_i) \ + _s |= X##_f[_i] << _up; \ +/* s is now != 0 if we want to set the LSbit */ \ + for (_i = 0; _i < 3-_skip; ++_i) \ X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \ - X##_f[_i] >>= _down; \ + X##_f[_i] = X##_f[3] >> _down; \ for (++_i; _i < 4; ++_i) \ X##_f[_i] = 0; \ + /* don't fix the LSB until the very end when we're sure f[0] is stable */ \ + X##_f[0] |= (_s != 0); \ } while (0) #define _FP_FRAC_ADD_4(R,X,Y) \ @@ -62,6 +100,92 @@ X##_f[3], X##_f[2], X##_f[1], X##_f[0], \ Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0]) +#define _FP_FRAC_SUB_4(R,X,Y) \ + __FP_FRAC_SUB_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0], \ + X##_f[3], X##_f[2], X##_f[1], X##_f[0], \ + Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0]) + +#define _FP_FRAC_ADDI_4(X,I) \ + __FP_FRAC_ADDI_4(X##_f[3], X##_f[2], X##_f[1], X##_f[0], I) + +#define _FP_ZEROFRAC_4 0,0,0,0 +#define _FP_MINFRAC_4 0,0,0,1 + +#define _FP_FRAC_ZEROP_4(X) ((X##_f[0] | X##_f[1] | X##_f[2] | X##_f[3]) == 0) +#define _FP_FRAC_NEGP_4(X) ((_FP_WS_TYPE)X##_f[3] < 0) +#define _FP_FRAC_OVERP_4(fs,X) (X##_f[0] & _FP_OVERFLOW_##fs) + +#define _FP_FRAC_EQ_4(X,Y) \ + (X##_f[0] == Y##_f[0] && X##_f[1] == Y##_f[1] \ + && X##_f[2] == Y##_f[2] && X##_f[3] == Y##_f[3]) + +#define _FP_FRAC_GT_4(X,Y) \ + (X##_f[3] > Y##_f[3] || \ + (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] || \ + (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] || \ + (X##_f[1] == Y##_f[1] && X##_f[0] > Y##_f[0]) \ + )) \ + )) \ + ) + +#define _FP_FRAC_GE_4(X,Y) \ + (X##_f[3] > Y##_f[3] || \ + (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] || \ + (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] || \ + (X##_f[1] == Y##_f[1] && X##_f[0] >= Y##_f[0]) \ + )) \ + )) \ + ) + + +#define _FP_FRAC_CLZ_4(R,X) \ + do { \ + if (X##_f[3]) \ + { \ + __FP_CLZ(R,X##_f[3]); \ + } \ + else if (X##_f[2]) \ + { \ + __FP_CLZ(R,X##_f[2]); \ + R += _FP_W_TYPE_SIZE; \ + } \ + else if (X##_f[1]) \ + { \ + __FP_CLZ(R,X##_f[2]); \ + R += _FP_W_TYPE_SIZE*2; \ + } \ + else \ + { \ + __FP_CLZ(R,X##_f[0]); \ + R += _FP_W_TYPE_SIZE*3; \ + } \ + } while(0) + + +#define _FP_UNPACK_RAW_4(fs, X, val) \ + do { \ + union _FP_UNION_##fs _flo; _flo.flt = (val); \ + X##_f[0] = _flo.bits.frac0; \ + X##_f[1] = _flo.bits.frac1; \ + X##_f[2] = _flo.bits.frac2; \ + X##_f[3] = _flo.bits.frac3; \ + X##_e = _flo.bits.exp; \ + X##_s = _flo.bits.sign; \ + } while (0) + +#define _FP_PACK_RAW_4(fs, val, X) \ + do { \ + union _FP_UNION_##fs _flo; \ + _flo.bits.frac0 = X##_f[0]; \ + _flo.bits.frac1 = X##_f[1]; \ + _flo.bits.frac2 = X##_f[2]; \ + _flo.bits.frac3 = X##_f[3]; \ + _flo.bits.exp = X##_e; \ + _flo.bits.sign = X##_s; \ + (val) = _flo.flt; \ + } while (0) + + /* * Internals */ @@ -76,3 +200,98 @@ r2 = x2 + y2 + (r1 < x1), \ r3 = x3 + y3 + (r2 < x2)) #endif + +#ifndef __FP_FRAC_SUB_4 +#define __FP_FRAC_SUB_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0) \ + (r0 = x0 - y0, \ + r1 = x1 - y1 - (r0 > x0), \ + r2 = x2 - y2 - (r1 > x1), \ + r3 = x3 - y3 - (r2 > x2)) +#endif + +#ifndef __FP_FRAC_ADDI_4 +/* I always wanted to be a lisp programmer :-> */ +#define __FP_FRAC_ADDI_4(x3,x2,x1,x0,i) \ + (x3 += ((x2 += ((x1 += ((x0 += i) < x0)) < x1) < x2))) +#endif + +/* Convert FP values between word sizes. This appears to be more + * complicated than I'd have expected it to be, so these might be + * wrong... These macros are in any case somewhat bogus because they + * use information about what various FRAC_n variables look like + * internally [eg, that 2 word vars are X_f0 and x_f1]. But so do + * the ones in op-2.h and op-1.h. + */ +#define _FP_FRAC_CONV_1_4(dfs, sfs, D, S) \ + do { \ + _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \ + _FP_WFRACBITS_##sfs); \ + D##_f = S##_f[0]; \ + } while (0) + +#define _FP_FRAC_CONV_2_4(dfs, sfs, D, S) \ + do { \ + _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \ + _FP_WFRACBITS_##sfs); \ + D##_f0 = S##_f[0]; \ + D##_f1 = S##_f[1]; \ + } while (0) + +/* Assembly/disassembly for converting to/from integral types. + * No shifting or overflow handled here. + */ +/* Put the FP value X into r, which is an integer of size rsize. */ +#define _FP_FRAC_ASSEMBLE_4(r, X, rsize) \ + do { \ + if (rsize <= _FP_W_TYPE_SIZE) \ + r = X##_f[0]; \ + else if (rsize <= 2*_FP_W_TYPE_SIZE) \ + { \ + r = X##_f[1]; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f[0]; \ + } \ + else \ + { \ + /* I'm feeling lazy so we deal with int == 3words (implausible)*/ \ + /* and int == 4words as a single case. */ \ + r = X##_f[3]; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f[2]; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f[1]; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f[0]; \ + } \ + } while (0) + +/* "No disassemble Number Five!" */ +/* move an integer of size rsize into X's fractional part. We rely on + * the _f[] array consisting of words of size _FP_W_TYPE_SIZE to avoid + * having to mask the values we store into it. + */ +#define _FP_FRAC_DISASSEMBLE_4(X, r, rsize) \ + do { \ + X##_f[0] = r; \ + X##_f[1] = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE); \ + X##_f[2] = (rsize <= 2*_FP_W_TYPE_SIZE ? 0 : r >> 2*_FP_W_TYPE_SIZE); \ + X##_f[3] = (rsize <= 3*_FP_W_TYPE_SIZE ? 0 : r >> 3*_FP_W_TYPE_SIZE); \ + } while (0); + +#define _FP_FRAC_CONV_4_1(dfs, sfs, D, S) \ + do { \ + D##_f[0] = S##_f; \ + D##_f[1] = D##_f[2] = D##_f[3] = 0; \ + _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ + } while (0) + +#define _FP_FRAC_CONV_4_2(dfs, sfs, D, S) \ + do { \ + D##_f[0] = S##_f0; \ + D##_f[1] = S##_f1; \ + D##_f[2] = D##_f[3] = 0; \ + _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ + } while (0) + +/* FIXME! This has to be written */ +#define _FP_SQRT_MEAT_4(R, S, T, X, q) diff --git a/arch/sparc64/math-emu/op-common.h b/arch/sparc64/math-emu/op-common.h index 8123e4c46..d4ce104f6 100644 --- a/arch/sparc64/math-emu/op-common.h +++ b/arch/sparc64/math-emu/op-common.h @@ -1,3 +1,4 @@ + #define _FP_DECL(wc, X) \ _FP_I_TYPE X##_c, X##_s, X##_e; \ _FP_FRAC_DECL_##wc(X) @@ -507,6 +508,29 @@ do { \ * Convert from FP to integer */ +/* "When a NaN, infinity, large positive argument >= 2147483648.0, or + * large negative argument <= -2147483649.0 is converted to an integer, + * the invalid_current bit...should be set and fp_exception_IEEE_754 should + * be raised. If the floating point invalid trap is disabled, no trap occurs + * and a numerical result is generated: if the sign bit of the operand + * is 0, the result is 2147483647; if the sign bit of the operand is 1, + * the result is -2147483648." + * Similarly for conversion to extended ints, except that the boundaries + * are >= 2^63, <= -(2^63 + 1), and the results are 2^63 + 1 for s=0 and + * -2^63 for s=1. + * -- SPARC Architecture Manual V9, Appendix B, which specifies how + * SPARCs resolve implementation dependencies in the IEEE-754 spec. + * I don't believe that the code below follows this. I'm not even sure + * it's right! + * It doesn't cope with needing to convert to an n bit integer when there + * is no n bit integer type. Fortunately gcc provides long long so this + * isn't a problem for sparc32. + * I have, however, fixed its NaN handling to conform as above. + * -- PMM 02/1998 + * NB: rsigned is not 'is r declared signed?' but 'should the value stored + * in r be signed or unsigned?'. r is always(?) declared unsigned. + * Comments below are mine, BTW -- PMM + */ #define _FP_TO_INT(fs, wc, r, X, rsize, rsigned) \ do { \ switch (X##_c) \ @@ -514,13 +538,14 @@ do { \ case FP_CLS_NORMAL: \ if (X##_e < 0) \ { \ - case FP_CLS_NAN: \ + /* case FP_CLS_NAN: see above! */ \ case FP_CLS_ZERO: \ r = 0; \ } \ else if (X##_e >= rsize - (rsigned != 0)) \ - { \ - case FP_CLS_INF: \ + { /* overflow */ \ + case FP_CLS_NAN: \ + case FP_CLS_INF: \ if (rsigned) \ { \ r = 1; \ @@ -604,6 +629,23 @@ do { \ /* Count leading zeros in a word. */ #ifndef __FP_CLZ +#if _FP_W_TYPE_SIZE < 64 +/* this is just to shut the compiler up about shifts > word length -- PMM 02/1998 */ +#define __FP_CLZ(r, x) \ + do { \ + _FP_W_TYPE _t = (x); \ + r = _FP_W_TYPE_SIZE - 1; \ + if (_t > 0xffff) r -= 16; \ + if (_t > 0xffff) _t >>= 16; \ + if (_t > 0xff) r -= 8; \ + if (_t > 0xff) _t >>= 8; \ + if (_t & 0xf0) r -= 4; \ + if (_t & 0xf0) _t >>= 4; \ + if (_t & 0xc) r -= 2; \ + if (_t & 0xc) _t >>= 2; \ + if (_t & 0x2) r -= 1; \ + } while (0) +#else /* not _FP_W_TYPE_SIZE < 64 */ #define __FP_CLZ(r, x) \ do { \ _FP_W_TYPE _t = (x); \ @@ -620,9 +662,11 @@ do { \ if (_t & 0xc) _t >>= 2; \ if (_t & 0x2) r -= 1; \ } while (0) -#endif +#endif /* not _FP_W_TYPE_SIZE < 64 */ +#endif /* ndef __FP_CLZ */ #define _FP_DIV_HELP_imm(q, r, n, d) \ do { \ q = n / d, r = n % d; \ } while (0) + diff --git a/arch/sparc64/math-emu/quad.h b/arch/sparc64/math-emu/quad.h index dfc3b4eea..48fcc798c 100644 --- a/arch/sparc64/math-emu/quad.h +++ b/arch/sparc64/math-emu/quad.h @@ -1,12 +1,17 @@ /* * Definitions for IEEE Quad Precision */ - -#if _FP_W_TYPE_SIZE < 64 -#error "Only stud muffins allowed, schmuck." +#if _FP_W_TYPE_SIZE < 32 +/* It appears to be traditional to abuse 16bitters in these header files... */ +#error "Here's a nickel, kid. Go buy yourself a real computer." #endif +#if _FP_W_TYPE_SIZE < 64 +/* This is all terribly experimental and I don't know if it'll work properly -- PMM 02/1998 */ +#define _FP_FRACTBITS_Q (4*_FP_W_TYPE_SIZE) +#else #define _FP_FRACTBITS_Q (2*_FP_W_TYPE_SIZE) +#endif #define _FP_FRACBITS_Q 113 #define _FP_FRACXBITS_Q (_FP_FRACTBITS_Q - _FP_FRACBITS_Q) @@ -23,6 +28,66 @@ #define _FP_OVERFLOW_Q \ ((_FP_W_TYPE)1 << (_FP_WFRACBITS_Q % _FP_W_TYPE_SIZE)) +#if _FP_W_TYPE_SIZE < 64 + +union _FP_UNION_Q +{ + long double flt; + struct + { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sign : 1; + unsigned exp : _FP_EXPBITS_Q; + unsigned long frac3 : _FP_FRACBITS_Q - (_FP_IMPLBIT_Q != 0)-(_FP_W_TYPE_SIZE * 3); + unsigned long frac2 : _FP_W_TYPE_SIZE; + unsigned long frac1 : _FP_W_TYPE_SIZE; + unsigned long frac0 : _FP_W_TYPE_SIZE; +#else + unsigned long frac0 : _FP_W_TYPE_SIZE; + unsigned long frac1 : _FP_W_TYPE_SIZE; + unsigned long frac2 : _FP_W_TYPE_SIZE; + unsigned long frac3 : _FP_FRACBITS_Q - (_FP_IMPLBIT_Q != 0)-(_FP_W_TYPE_SIZE * 3); + unsigned exp : _FP_EXPBITS_Q; + unsigned sign : 1; +#endif /* not bigendian */ + } bits __attribute__((packed)); +}; + + +#define FP_DECL_Q(X) _FP_DECL(4,X) +#define FP_UNPACK_RAW_Q(X,val) _FP_UNPACK_RAW_4(Q,X,val) +#define FP_PACK_RAW_Q(val,X) _FP_PACK_RAW_4(Q,val,X) + +#define FP_UNPACK_Q(X,val) \ + do { \ + _FP_UNPACK_RAW_4(Q,X,val); \ + _FP_UNPACK_CANONICAL(Q,4,X); \ + } while (0) + +#define FP_PACK_Q(val,X) \ + do { \ + _FP_PACK_CANONICAL(Q,4,X); \ + _FP_PACK_RAW_4(Q,val,X); \ + } while (0) + +#define FP_NEG_Q(R,X) _FP_NEG(Q,4,R,X) +#define FP_ADD_Q(R,X,Y) _FP_ADD(Q,4,R,X,Y) +/* single.h and double.h define FP_SUB_t this way too. However, _FP_SUB is + * never defined in op-common.h! Fortunately nobody seems to use the FP_SUB_t + * macros: I suggest a combination of FP_NEG and FP_ADD :-> -- PMM 02/1998 + */ +#define FP_SUB_Q(R,X,Y) _FP_SUB(Q,4,R,X,Y) +#define FP_MUL_Q(R,X,Y) _FP_MUL(Q,4,R,X,Y) +#define FP_DIV_Q(R,X,Y) _FP_DIV(Q,4,R,X,Y) +#define FP_SQRT_Q(R,X) _FP_SQRT(Q,4,R,X) + +#define FP_CMP_Q(r,X,Y,un) _FP_CMP(Q,4,r,X,Y,un) +#define FP_CMP_EQ_Q(r,X,Y) _FP_CMP_EQ(Q,4,r,X,Y) + +#define FP_TO_INT_Q(r,X,rsz,rsg) _FP_TO_INT(Q,4,r,X,rsz,rsg) +#define FP_FROM_INT_Q(X,r,rs,rt) _FP_FROM_INT(Q,4,X,r,rs,rt) + +#else /* not _FP_W_TYPE_SIZE < 64 */ union _FP_UNION_Q { long double flt /* __attribute__((mode(TF))) */ ; @@ -69,3 +134,5 @@ union _FP_UNION_Q #define FP_TO_INT_Q(r,X,rsz,rsg) _FP_TO_INT(Q,2,r,X,rsz,rsg) #define FP_FROM_INT_Q(X,r,rs,rt) _FP_FROM_INT(Q,2,X,r,rs,rt) + +#endif /* not _FP_W_TYPE_SIZE < 64 */ diff --git a/arch/sparc64/math-emu/single.h b/arch/sparc64/math-emu/single.h index fa7f386cd..f19d99451 100644 --- a/arch/sparc64/math-emu/single.h +++ b/arch/sparc64/math-emu/single.h @@ -3,7 +3,7 @@ */ #if _FP_W_TYPE_SIZE < 32 -#error "Here's a nickle kid. Go buy yourself a real computer." +#error "Here's a nickel kid. Go buy yourself a real computer." #endif #define _FP_FRACBITS_S 24 diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index 6bc52f3eb..21389e397 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.20 1997/08/04 16:16:51 davem Exp $ +/* $Id: fault.c,v 1.21 1998/03/25 10:43:20 jj Exp $ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -209,7 +209,7 @@ bad_area: } else { current->tss.sig_address = address; current->tss.sig_desc = SUBSIG_NOMAPPING; - send_sig(SIGSEGV, current, 1); + force_sig(SIGSEGV, current); goto out; } unhandled_fault (address, current, regs); diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index b564dc0dc..e7d863997 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,8 +1,8 @@ -/* $Id: init.c,v 1.60 1998/01/10 18:19:51 ecd Exp $ +/* $Id: init.c,v 1.71 1998/03/27 07:00:08 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h> @@ -22,13 +22,20 @@ #include <asm/mmu_context.h> #include <asm/vaddrs.h> +/* Turn this off if you suspect some place in some physical memory hole + might get into page tables (something would be broken very much). */ + +#define FREE_UNUSED_MEM_MAP + extern void show_net_buffers(void); extern unsigned long device_scan(unsigned long); struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS]; /* Ugly, but necessary... -DaveM */ -unsigned long phys_base, null_pmd_table, null_pte_table; +unsigned long phys_base; +unsigned int null_pte_table; +unsigned long two_null_pmd_table, two_null_pte_table; extern unsigned long empty_null_pmd_table; extern unsigned long empty_null_pte_table; @@ -42,12 +49,12 @@ extern void __bfill64(void *, unsigned long *); static __inline__ void __init_pmd(pmd_t *pmdp) { - __bfill64((void *)pmdp, &null_pte_table); + __bfill64((void *)pmdp, &two_null_pte_table); } static __inline__ void __init_pgd(pgd_t *pgdp) { - __bfill64((void *)pgdp, &null_pmd_table); + __bfill64((void *)pgdp, &two_null_pmd_table); } /* @@ -88,26 +95,36 @@ pte_t __bad_page(void) void show_mem(void) { - int i,free = 0,total = 0,reserved = 0; - int shared = 0; + int free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + struct page *page, *end; printk("\nMem-info:\n"); show_free_areas(); printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = max_mapnr; - while (i-- > 0) { + for (page = mem_map, end = mem_map + max_mapnr; + page < end; page++) { + if (PageSkip(page)) { + if (page->next_hash < page) + break; + page = page->next_hash; + } total++; - if (PageReserved(mem_map + i)) + if (PageReserved(page)) reserved++; - else if (!atomic_read(&mem_map[i].count)) + else if (PageSwapCache(page)) + cached++; + else if (!atomic_read(&page->count)) free++; else - shared += atomic_read(&mem_map[i].count) - 1; + shared += atomic_read(&page->count) - 1; } printk("%d pages of RAM\n",total); printk("%d free pages\n",free); printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld pages in page table cache\n",pgtable_cache_size); show_buffers(); #ifdef CONFIG_NET show_net_buffers(); @@ -409,14 +426,10 @@ void mmu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus spin_unlock_irqrestore(&iommu->iommu_lock, flags); } -static char sfmmuinfo[512]; - -char *mmu_info(void) +int mmu_info(char *buf) { /* We'll do the rest later to make it nice... -DaveM */ - sprintf(sfmmuinfo, "MMU Type\t: Spitfire\n"); - - return sfmmuinfo; + return sprintf(buf, "MMU Type\t: Spitfire\n"); } static unsigned long mempool; @@ -633,10 +646,7 @@ void get_new_mmu_context(struct mm_struct *mm, unsigned long *ctx) } #ifndef __SMP__ -unsigned long *pgd_quicklist = NULL; -unsigned long *pmd_quicklist = NULL; -unsigned long *pte_quicklist = NULL; -unsigned long pgtable_cache_size = 0; +struct pgtable_cache_struct pgt_quicklists; #endif pgd_t *get_pgd_slow(void) @@ -653,7 +663,7 @@ pmd_t *get_pmd_slow(pgd_t *pgd, unsigned long offset) { pmd_t *pmd; - pmd = (pmd_t *) __get_free_page(GFP_KERNEL); + pmd = (pmd_t *) __get_free_page(GFP_DMA|GFP_KERNEL); if(pmd) { __init_pmd(pmd); pgd_set(pgd, pmd); @@ -666,9 +676,9 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) { pte_t *pte; - pte = (pte_t *) __get_free_page(GFP_KERNEL); + pte = (pte_t *) __get_free_page(GFP_DMA|GFP_KERNEL); if(pte) { - clear_page((unsigned long)pte); + memset((void *)pte, 0, PTE_TABLE_SIZE); pmd_set(pmd, pte); return pte + offset; } @@ -737,6 +747,7 @@ void sparc_ultra_unmapioaddr(unsigned long virt_addr) pte_clear(ptep); } +#if NOTUSED void sparc_ultra_dump_itlb(void) { int slot; @@ -766,6 +777,7 @@ void sparc_ultra_dump_dtlb(void) slot+2, spitfire_get_dtlb_tag(slot+2), spitfire_get_dtlb_data(slot+2)); } } +#endif /* paging_init() sets up the page tables */ @@ -808,23 +820,30 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) /* Now set kernel pgd to upper alias so physical page computations * work. */ - init_mm.pgd += ((shift) / (sizeof(pgd_t *))); + init_mm.pgd += ((shift) / (sizeof(pgd_t))); /* The funny offsets are to make page table operations much quicker and * requite less state, see pgtable.h for gory details. + * pgtable.h assumes null_pmd_table is null_pte_table - PAGE_SIZE, lets + * check it now. */ - null_pmd_table=__pa(((unsigned long)&empty_null_pmd_table)+shift); null_pte_table=__pa(((unsigned long)&empty_null_pte_table)+shift); + if (null_pmd_table != __pa(((unsigned long)&empty_null_pmd_table)+shift)) { + prom_printf("null_p{md|te}_table broken.\n"); + prom_halt(); + } + two_null_pmd_table = (((unsigned long)null_pmd_table) << 32) | null_pmd_table; + two_null_pte_table = (((unsigned long)null_pte_table) << 32) | null_pte_table; pmdp = (pmd_t *) &empty_null_pmd_table; - for(i = 0; i < 1024; i++) + for(i = 0; i < PTRS_PER_PMD; i++) pmd_val(pmdp[i]) = null_pte_table; - memset((void *) &empty_null_pte_table, 0, PAGE_SIZE); + memset((void *) &empty_null_pte_table, 0, PTE_TABLE_SIZE); /* Now can init the kernel/bad page tables. */ - __bfill64((void *)swapper_pg_dir, &null_pmd_table); - __bfill64((void *)&empty_bad_pmd_table, &null_pte_table); + __bfill64((void *)swapper_pg_dir, &two_null_pmd_table); + __bfill64((void *)&empty_bad_pmd_table, &two_null_pte_table); /* We use mempool to create page tables, therefore adjust it up * such that __pa() macros etc. work. @@ -867,21 +886,34 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem)) { - unsigned long addr, tmp2 = 0; - - for(addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { - if(addr >= PAGE_OFFSET && addr < start_mem) - addr = start_mem; - for(tmp2=0; sp_banks[tmp2].num_bytes != 0; tmp2++) { - unsigned long phys_addr = __pa(addr); - unsigned long base = sp_banks[tmp2].base_addr; - unsigned long limit = base + sp_banks[tmp2].num_bytes; - - if((phys_addr >= base) && (phys_addr < limit) && - ((phys_addr + PAGE_SIZE) < limit)) - mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved); - if (phys_addr >= 0xf0000000) - mem_map[MAP_NR(addr)].flags &= ~(1<<PG_DMA); + unsigned long tmp = 0, paddr, endaddr; + unsigned long end = __pa(end_mem); + + for (paddr = __pa(start_mem); paddr < end; ) { + for (; sp_banks[tmp].num_bytes != 0; tmp++) + if (sp_banks[tmp].base_addr + sp_banks[tmp].num_bytes > paddr) + break; + if (!sp_banks[tmp].num_bytes) { + mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip); + mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (phys_base >> PAGE_SHIFT); + return; + } + + if (sp_banks[tmp].base_addr > paddr) { + /* Making a one or two pages PG_skip holes is not necessary */ + if (sp_banks[tmp].base_addr - paddr > 2 * PAGE_SIZE) { + mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip); + mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (sp_banks[tmp].base_addr >> PAGE_SHIFT); + } + paddr = sp_banks[tmp].base_addr; + } + + endaddr = sp_banks[tmp].base_addr + sp_banks[tmp].num_bytes; + while (paddr < endaddr) { + mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_reserved); + if (paddr >= 0xf0000000) + mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_DMA); + paddr += PAGE_SIZE; } } } @@ -891,31 +923,65 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) int codepages = 0; int datapages = 0; int initpages = 0; - unsigned long tmp2, addr; + unsigned long addr; unsigned long alias_base = phys_base + PAGE_OFFSET - (long)(&empty_zero_page); + struct page *page, *end; end_mem &= PAGE_MASK; max_mapnr = MAP_NR(end_mem); high_memory = (void *) end_mem; start_mem = PAGE_ALIGN(start_mem); - num_physpages = (start_mem - PAGE_OFFSET) >> PAGE_SHIFT; + num_physpages = 0; + + if (phys_base) { + mem_map[0].flags |= (1<<PG_skip) | (1<<PG_reserved); + mem_map[0].next_hash = mem_map + (phys_base >> PAGE_SHIFT); + } - addr = PAGE_OFFSET; + addr = PAGE_OFFSET + phys_base; while(addr < start_mem) { #ifdef CONFIG_BLK_DEV_INITRD - if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) { + if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved); - num_physpages--; - } else + else #endif mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved); addr += PAGE_SIZE; } taint_real_pages(start_mem, end_mem); + +#ifdef FREE_UNUSED_MEM_MAP + end = mem_map + max_mapnr; + for (page = mem_map; page < end; page++) { + if (PageSkip(page)) { + unsigned long low, high; + + low = PAGE_ALIGN((unsigned long)(page+1)); + if (page->next_hash < page) + high = ((unsigned long)end) & PAGE_MASK; + else + high = ((unsigned long)page->next_hash) & PAGE_MASK; + while (low < high) { + mem_map[MAP_NR(low)].flags &= ~(1<<PG_reserved); + low += PAGE_SIZE; + } + } + } +#endif + for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { - if(PageReserved(mem_map + MAP_NR(addr))) { + if (PageSkip(mem_map + MAP_NR(addr))) { + unsigned long next = mem_map[MAP_NR(addr)].next_hash - mem_map; + + next = (next << PAGE_SHIFT) + PAGE_OFFSET; + if (next < addr || next >= end_mem) + break; + addr = next; + } + num_physpages++; + if (PageReserved(mem_map + MAP_NR(addr))) { if ((addr < ((unsigned long) &etext) + alias_base) && (addr >= alias_base)) codepages++; else if((addr >= ((unsigned long)&__init_begin) + alias_base) @@ -926,7 +992,6 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) continue; } atomic_set(&mem_map[MAP_NR(addr)].count, 1); - num_physpages++; #ifdef CONFIG_BLK_DEV_INITRD if (!initrd_start || (addr < initrd_start || addr >= initrd_end)) @@ -934,18 +999,16 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) free_page(addr); } - tmp2 = nr_free_pages << PAGE_SHIFT; - - printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%016lx,%016lx]\n", - tmp2 >> 10, + printk("Memory: %uk available (%dk kernel code, %dk data, %dk init) [%016lx,%016lx]\n", + nr_free_pages << (PAGE_SHIFT-10), codepages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10), PAGE_OFFSET, end_mem); freepages.low = nr_free_pages >> 7; - if(freepages.low < 16) - freepages.low = 16; + if(freepages.low < 48) + freepages.low = 48; freepages.low = freepages.low + (freepages.low >> 1); freepages.high = freepages.low + freepages.low; } @@ -967,20 +1030,25 @@ void free_initmem (void) void si_meminfo(struct sysinfo *val) { - int i; + struct page *page, *end; - i = MAP_NR(high_memory); val->totalram = 0; val->sharedram = 0; val->freeram = nr_free_pages << PAGE_SHIFT; val->bufferram = buffermem; - while (i-- > 0) { - if (PageReserved(mem_map + i)) + for (page = mem_map, end = mem_map + max_mapnr; + page < end; page++) { + if (PageSkip(page)) { + if (page->next_hash < page) + break; + page = page->next_hash; + } + if (PageReserved(page)) continue; val->totalram++; - if (!atomic_read(&mem_map[i].count)) + if (!atomic_read(&page->count)) continue; - val->sharedram += atomic_read(&mem_map[i].count) - 1; + val->sharedram += atomic_read(&page->count) - 1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; diff --git a/arch/sparc64/mm/modutil.c b/arch/sparc64/mm/modutil.c index e6b4b2223..303b98996 100644 --- a/arch/sparc64/mm/modutil.c +++ b/arch/sparc64/mm/modutil.c @@ -1,7 +1,7 @@ -/* $Id: modutil.c,v 1.1 1997/06/27 14:53:35 jj Exp $ +/* $Id: modutil.c,v 1.3 1998/01/16 16:35:02 jj Exp $ * arch/sparc64/mm/modutil.c * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Based upon code written by Linus Torvalds and others. */ @@ -21,7 +21,7 @@ void module_unmap (void * addr) if (!addr) return; if ((PAGE_SIZE-1) & (unsigned long) addr) { - printk("Trying to vfree() bad address (%p)\n", addr); + printk("Trying to unmap module with bad address (%p)\n", addr); return; } for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) { @@ -35,6 +35,32 @@ void module_unmap (void * addr) printk("Trying to unmap nonexistent module vm area (%p)\n", addr); } +void module_shrink(void * addr, unsigned long size) +{ + struct vm_struct *tmp; + + if (!addr) + return; + if ((PAGE_SIZE-1) & (unsigned long) addr) { + printk("Trying to shrink module with bad address (%p)\n", addr); + return; + } + size = PAGE_ALIGN(size); + if (!size) + module_unmap(addr); + for (tmp = modvmlist; tmp; tmp = tmp->next) { + if (tmp->addr == addr) { + if (size > tmp->size - PAGE_SIZE) { + printk("Trying to expand module with module_shrink()\n"); + return; + } + vmfree_area_pages(VMALLOC_VMADDR(tmp->addr)+size, tmp->size-size); + return; + } + } + printk("Trying to shrink nonexistent module vm area (%p)\n", addr); +} + void * module_map (unsigned long size) { void * addr; diff --git a/arch/sparc64/prom/bootstr.c b/arch/sparc64/prom/bootstr.c index 7ef17159d..6d53b8be2 100644 --- a/arch/sparc64/prom/bootstr.c +++ b/arch/sparc64/prom/bootstr.c @@ -1,8 +1,8 @@ -/* $Id: bootstr.c,v 1.4 1997/06/17 13:25:35 jj Exp $ +/* $Id: bootstr.c,v 1.5 1998/01/23 08:51:39 jj Exp $ * bootstr.c: Boot string/argument acquisition from the PROM. * * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright(C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright(C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/string.h> @@ -10,9 +10,9 @@ #include <asm/oplib.h> #define BARG_LEN 256 -int bootstr_len __initdata = BARG_LEN; -static int bootstr_valid __initdata = 0; -static char bootstr_buf[BARG_LEN] __initdata = { 0 }; +int bootstr_len = BARG_LEN; +static int bootstr_valid = 0; +static char bootstr_buf[BARG_LEN] = { 0 }; __initfunc(char * prom_getbootargs(void)) diff --git a/arch/sparc64/prom/init.c b/arch/sparc64/prom/init.c index 7dcef7642..4c1bd1e00 100644 --- a/arch/sparc64/prom/init.c +++ b/arch/sparc64/prom/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.7 1997/03/24 17:43:59 jj Exp $ +/* $Id: init.c,v 1.8 1998/03/15 10:14:44 ecd Exp $ * init.c: Initialize internal variables used by the PROM * library functions. * @@ -9,6 +9,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> +#include <linux/ctype.h> #include <asm/openprom.h> #include <asm/oplib.h> @@ -32,11 +33,13 @@ extern void prom_cif_init(void *, void *); __initfunc(void prom_init(void *cif_handler, void *cif_stack)) { - char buffer[80]; + char buffer[80], *p; + int ints[3]; int node; - + int i = 0; + prom_vers = PROM_P1275; - + prom_cif_init(cif_handler, cif_stack); prom_root_node = prom_getsibling(0); @@ -46,34 +49,45 @@ __initfunc(void prom_init(void *cif_handler, void *cif_stack)) prom_chosen_node = prom_finddevice("/chosen"); if (!prom_chosen_node || prom_chosen_node == -1) prom_halt(); - + prom_stdin = prom_getint (prom_chosen_node, "stdin"); prom_stdout = prom_getint (prom_chosen_node, "stdout"); node = prom_finddevice("/openprom"); if (!node || node == -1) prom_halt(); - + prom_getstring (node, "version", buffer, sizeof (buffer)); - + prom_printf ("\n"); - - if (strncmp (buffer, "OBP ", 4) || buffer[5] != '.' || buffer[7] != '.') { - prom_printf ("Strange OBP version `%s'.\n", buffer); - prom_halt (); + + if (strncmp (buffer, "OBP ", 4)) + goto strange_version; + + /* Version field is expected to be 'OBP xx.yy.zz date...' */ + + p = buffer + 4; + while (p && isdigit(*p) && i < 3) { + ints[i++] = simple_strtoul(p, NULL, 0); + if ((p = strchr(p, '.')) != NULL) + p++; } - /* Version field is expected to be 'OBP x.y.z date...' */ - - prom_rev = buffer[6] - '0'; - prom_prev = ((buffer[4] - '0') << 16) | - ((buffer[6] - '0') << 8) | - (buffer[8] - '0'); - + if (i != 3) + goto strange_version; + + prom_rev = ints[1]; + prom_prev = (ints[0] << 16) | (ints[1] << 8) | ints[2]; + printk ("PROMLIB: Sun IEEE Boot Prom %s\n", buffer + 4); - + prom_meminit(); prom_ranges_init(); /* Initialization successful. */ + return; + +strange_version: + prom_printf ("Strange OBP version `%s'.\n", buffer); + prom_halt (); } diff --git a/arch/sparc64/prom/ranges.c b/arch/sparc64/prom/ranges.c index 83f860d45..7b889bac1 100644 --- a/arch/sparc64/prom/ranges.c +++ b/arch/sparc64/prom/ranges.c @@ -1,4 +1,4 @@ -/* $Id: ranges.c,v 1.8 1997/08/17 22:39:45 ecd Exp $ +/* $Id: ranges.c,v 1.10 1998/03/24 05:54:29 ecd Exp $ * ranges.c: Handle ranges in newer proms for obio/sbus. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -140,17 +140,61 @@ __initfunc(void prom_ebus_ranges_init(struct linux_ebus *ebus)) ebus->num_ebus_ranges = (success/sizeof(struct linux_prom_ebus_ranges)); } +__initfunc(void prom_ebus_intmap_init(struct linux_ebus *ebus)) +{ + int success; + + ebus->num_ebus_intmap = 0; + success = prom_getproperty(ebus->prom_node, "interrupt-map", + (char *)ebus->ebus_intmap, + sizeof(ebus->ebus_intmap)); + if (success == -1) + return; + + ebus->num_ebus_intmap = (success/sizeof(struct linux_prom_ebus_intmap)); + + success = prom_getproperty(ebus->prom_node, "interrupt-map-mask", + (char *)&ebus->ebus_intmask, + sizeof(ebus->ebus_intmask)); + if (success == -1) { + prom_printf("%s: can't get interrupt-map-mask\n", __FUNCTION__); + prom_halt(); + } +} + __initfunc(void prom_pbm_ranges_init(int pnode, struct linux_pbm_info *pbm)) { int success; pbm->num_pbm_ranges = 0; - success = prom_getproperty(pbm->prom_node, "ranges", + success = prom_getproperty(pnode, "ranges", (char *)&pbm->pbm_ranges, sizeof(pbm->pbm_ranges)); if(success != -1) pbm->num_pbm_ranges = (success/sizeof(struct linux_prom_pci_ranges)); } + +__initfunc(void prom_pbm_intmap_init(int pnode, struct linux_pbm_info *pbm)) +{ + int success; + + pbm->num_pbm_intmap = 0; + success = prom_getproperty(pnode, "interrupt-map", + (char *)pbm->pbm_intmap, + sizeof(pbm->pbm_intmap)); + if (success == -1) + return; + + pbm->num_pbm_intmap = (success/sizeof(struct linux_prom_pci_intmap)); + + success = prom_getproperty(pnode, "interrupt-map-mask", + (char *)&pbm->pbm_intmask, + sizeof(pbm->pbm_intmask)); + if (success == -1) { + prom_printf("%s: can't get interrupt-map-mask\n", __FUNCTION__); + prom_halt(); + } +} #endif void diff --git a/arch/sparc64/solaris/Makefile b/arch/sparc64/solaris/Makefile index 056909b6f..691601a3e 100644 --- a/arch/sparc64/solaris/Makefile +++ b/arch/sparc64/solaris/Makefile @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := solaris.o -O_OBJS := entry64.o fs.o misc.o signal.o systbl.o ioctl.o ipc.o socksys.o +O_OBJS := entry64.o fs.o misc.o signal.o systbl.o ioctl.o ipc.o socksys.o timod.o ifeq ($(CONFIG_SOLARIS_EMUL),m) M_OBJS := $(O_TARGET) CPPFLAGS = $(MODFLAGS) diff --git a/arch/sparc64/solaris/conv.h b/arch/sparc64/solaris/conv.h index c806d3dc1..fa716f595 100644 --- a/arch/sparc64/solaris/conv.h +++ b/arch/sparc64/solaris/conv.h @@ -1,10 +1,11 @@ -/* $Id: conv.h,v 1.2 1997/09/03 12:29:13 jj Exp $ +/* $Id: conv.h,v 1.3 1998/03/26 08:46:13 jj Exp $ * conv.h: Utility macros for Solaris emulation * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* #define DEBUG_SOLARIS */ +#define DEBUG_SOLARIS_KMALLOC #ifndef __ASSEMBLY__ @@ -25,4 +26,12 @@ extern unsigned sunos_sys_table[]; #define SYS(name) ((long)sys_call_table[__NR_##name]) #define SUNOS(x) ((long)sunos_sys_table[x]) +#ifdef DEBUG_SOLARIS +#define SOLD(s) printk("%s,%d,%s(): %s\n",__FILE__,__LINE__,__FUNCTION__,(s)) +#define SOLDD(s) printk("solaris: "); printk s +#else +#define SOLD(s) +#define SOLDD(s) +#endif + #endif /* __ASSEMBLY__ */ diff --git a/arch/sparc64/solaris/entry64.S b/arch/sparc64/solaris/entry64.S index 414506522..53d825e6d 100644 --- a/arch/sparc64/solaris/entry64.S +++ b/arch/sparc64/solaris/entry64.S @@ -1,7 +1,7 @@ -/* $Id: entry64.S,v 1.4 1997/09/09 17:13:50 jj Exp $ +/* $Id: entry64.S,v 1.5 1998/03/26 08:46:15 jj Exp $ * entry64.S: Solaris syscall emulation entry point. * - * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996,1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) */ @@ -29,8 +29,11 @@ solaris_syscall_trace: mov %i4, %o4 srl %i1, 0, %o1 mov %i5, %o5 - b,pt %xcc, 2f + andcc %l3, 1, %g0 + be,pt %icc, 2f srl %i2, 0, %o2 + b,pt %xcc, 2f + add %sp, STACK_BIAS + REGWIN_SZ, %o0 solaris_sucks: /* Solaris is a big system which needs to be able to do all the things @@ -59,9 +62,9 @@ solaris_reg: mov %i4, %o4 linux_syscall_for_solaris: - sll %l7, 2, %l4 + sll %l3, 2, %l4 ba,pt %xcc, 10f - lduw [%l6 + %l4], %l7 + lduw [%l6 + %l4], %l3 /* Solaris system calls enter here... */ .align 32 @@ -78,18 +81,18 @@ solaris_sparc_syscall: cmp %l0, 1 bne,pn %icc, solaris_reg 1: srl %i0, 0, %o0 - lduw [%l7 + %l4], %l7 + lduw [%l7 + %l4], %l3 srl %i1, 0, %o1 ldx [%g6 + AOFF_task_flags], %l5 - cmp %l7, NR_SYSCALLS + cmp %l3, NR_SYSCALLS bleu,a,pn %xcc, linux_syscall_for_solaris sethi %hi(sys_call_table32), %l6 - andcc %l7, 1, %g0 + andcc %l3, 1, %g0 bne,a,pn %icc, 10f add %sp, STACK_BIAS + REGWIN_SZ, %o0 10: srl %i2, 0, %o2 mov %i5, %o5 - andn %l7, 3, %l7 + andn %l3, 3, %l7 andcc %l5, 0x20, %g0 bne,pn %icc, solaris_syscall_trace mov %i0, %l5 @@ -110,12 +113,20 @@ ret_from_solaris: andn %g3, %g2, %g3 stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] bne,pn %icc, solaris_syscall_trace2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc - add %l1, 0x4, %l2 !npc = npc+4 - stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] - clr %l6 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 + andcc %l1, 1, %g0 + bne,pn %icc, 2f + clr %l6 + add %l1, 0x4, %l2 + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] ! pc = npc call rtrap - stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] !npc = npc+4 + + /* When tnpc & 1, this comes from setcontext and we don't want to advance pc */ +2: andn %l1, 3, %l1 + call rtrap + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] !npc = npc&~3 + 1: /* System call failure, set Carry condition code. * Also, get abs(errno) to return to the process. @@ -134,15 +145,20 @@ ret_from_solaris: mov 1, %l6 stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] bne,pn %icc, solaris_syscall_trace2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc - add %l1, 0x4, %l2 !npc = npc+4 - - stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 + andcc %l1, 1, %g0 + bne,pn %icc, 2b + add %l1, 0x4, %l2 + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] ! pc = npc call rtrap - stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] !npc = npc+4 + solaris_syscall_trace2: call syscall_trace add %l1, 0x4, %l2 /* npc = npc+4 */ + andcc %l1, 1, %g0 + bne,pn %icc, 2b + nop stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] call rtrap stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] diff --git a/arch/sparc64/solaris/fs.c b/arch/sparc64/solaris/fs.c index 4d1448809..39e69d242 100644 --- a/arch/sparc64/solaris/fs.c +++ b/arch/sparc64/solaris/fs.c @@ -1,11 +1,13 @@ -/* $Id: fs.c,v 1.6 1997/10/13 03:54:05 davem Exp $ +/* $Id: fs.c,v 1.8 1998/03/29 10:11:02 davem Exp $ * fs.c: fs related syscall emulation for Solaris * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/types.h> +#include <linux/sched.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/stat.h> #include <linux/smp_lock.h> #include <linux/limits.h> @@ -339,9 +341,12 @@ asmlinkage int solaris_fstatvfs(unsigned int fd, u32 buf) int error; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - error = -EBADF; - else if (!(dentry = file->f_dentry)) + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + if (!(dentry = file->f_dentry)) error = -ENOENT; else if (!(inode = dentry->d_inode)) error = -ENOENT; @@ -351,6 +356,8 @@ asmlinkage int solaris_fstatvfs(unsigned int fd, u32 buf) error = -ENOSYS; else error = report_statvfs(inode, buf); + fput(file); +out: unlock_kernel(); return error; } @@ -516,7 +523,7 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group) newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } - DQUOT_TRANSFER(inode, newattrs); + DQUOT_TRANSFER(dentry, newattrs); out: return error; } @@ -563,13 +570,15 @@ asmlinkage int solaris_pread(int fd, u32 buf, u32 nbyte, s32 offset) lock_kernel(); retval = -EBADF; - if (fd >= NR_OPEN || - !(file = current->files->fd[fd])) + file = fget(fd); + if (!file) goto bad; + temp = file->f_pos; if (temp != offset) { retval = sys_lseek(fd, offset, 0); - if (retval < 0) goto bad; + if (retval < 0) + goto out_putf; } retval = sys_read(fd, (char *)A(buf), nbyte); if (file->f_pos != temp) { @@ -578,6 +587,9 @@ asmlinkage int solaris_pread(int fd, u32 buf, u32 nbyte, s32 offset) else sys_lseek(fd, temp, 0); } + +out_putf: + fput(file); bad: unlock_kernel(); return retval; @@ -595,13 +607,15 @@ asmlinkage int solaris_pwrite(int fd, u32 buf, u32 nbyte, s32 offset) lock_kernel(); retval = -EBADF; - if (fd >= NR_OPEN || - !(file = current->files->fd[fd])) + file = fget(fd); + if (!file) goto bad; + temp = file->f_pos; if (temp != offset) { retval = sys_lseek(fd, offset, 0); - if (retval < 0) goto bad; + if (retval < 0) + goto out_putf; } retval = sys_write(fd, (char *)A(buf), nbyte); if (file->f_pos != temp) { @@ -610,6 +624,9 @@ asmlinkage int solaris_pwrite(int fd, u32 buf, u32 nbyte, s32 offset) else sys_lseek(fd, temp, 0); } + +out_putf: + fput(file); bad: unlock_kernel(); return retval; diff --git a/arch/sparc64/solaris/ioctl.c b/arch/sparc64/solaris/ioctl.c index c5ef7b99f..242d1e914 100644 --- a/arch/sparc64/solaris/ioctl.c +++ b/arch/sparc64/solaris/ioctl.c @@ -1,7 +1,8 @@ -/* $Id: ioctl.c,v 1.4 1997/09/18 10:38:24 rth Exp $ +/* $Id: ioctl.c,v 1.10 1998/03/29 10:11:00 davem Exp $ * ioctl.c: Solaris ioctl emulation. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz) * * Streams & timod emulation based on code * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk) @@ -15,14 +16,16 @@ #include <linux/smp_lock.h> #include <linux/ioctl.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/netdevice.h> #include <asm/uaccess.h> #include <asm/termios.h> #include "conv.h" +#include "socksys.h" -extern char * getname32(u32 filename); +extern char *getname32(u32 filename); #define putname32 putname extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, @@ -31,6 +34,11 @@ extern asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg); asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg); +extern int timod_putmsg(unsigned int fd, char *ctl_buf, int ctl_len, + char *data_buf, int data_len, int flags); +extern int timod_getmsg(unsigned int fd, char *ctl_buf, int ctl_maxlen, int *ctl_len, + char *data_buf, int data_maxlen, int *data_len, int *flags); + /* termio* stuff {{{ */ struct solaris_termios { @@ -231,31 +239,303 @@ struct strioctl { u32 data; }; +struct solaris_si_sockparams { + int sp_family; + int sp_type; + int sp_protocol; +}; + +struct solaris_o_si_udata { + int tidusize; + int addrsize; + int optsize; + int etsdusize; + int servtype; + int so_state; + int so_options; + int tsdusize; +}; + +struct solaris_si_udata { + int tidusize; + int addrsize; + int optsize; + int etsdusize; + int servtype; + int so_state; + int so_options; + int tsdusize; + struct solaris_si_sockparams sockparams; +}; + +#define SOLARIS_MODULE_TIMOD 0 +#define SOLARIS_MODULE_SOCKMOD 1 +#define SOLARIS_MODULE_MAX 2 + +static struct module_info { + const char *name; + /* can be expanded further if needed */ +} module_table[ SOLARIS_MODULE_MAX + 1 ] = { + /* the ordering here must match the module numbers above! */ + { "timod" }, + { "sockmod" }, + { NULL } +}; + +static inline int solaris_sockmod(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct inode *ino; + /* I wonder which of these tests are superfluous... --patrik */ + if (! current->files->fd[fd] || + ! current->files->fd[fd]->f_dentry || + ! (ino = current->files->fd[fd]->f_dentry->d_inode) || + ! ino->i_sock) + return TBADF; + + switch (cmd & 0xff) { + case 109: /* SI_SOCKPARAMS */ + { + struct solaris_si_sockparams si; + if (copy_from_user (&si, (struct solaris_si_sockparams *) A(arg), sizeof(si))) + return (EFAULT << 8) | TSYSERR; + + /* Should we modify socket ino->socket_i.ops and type? */ + return 0; + } + case 110: /* SI_GETUDATA */ + { + int etsdusize, servtype; + switch (ino->u.socket_i.type) { + case SOCK_STREAM: + etsdusize = 1; + servtype = 2; + break; + default: + etsdusize = -2; + servtype = 3; + break; + } + if (put_user(16384, &((struct solaris_si_udata *)A(arg))->tidusize) || + __put_user(sizeof(struct sockaddr), &((struct solaris_si_udata *)A(arg))->addrsize) || + __put_user(-1, &((struct solaris_si_udata *)A(arg))->optsize) || + __put_user(etsdusize, &((struct solaris_si_udata *)A(arg))->etsdusize) || + __put_user(servtype, &((struct solaris_si_udata *)A(arg))->servtype) || + __put_user(0, &((struct solaris_si_udata *)A(arg))->so_state) || + __put_user(0, &((struct solaris_si_udata *)A(arg))->so_options) || + __put_user(16384, &((struct solaris_si_udata *)A(arg))->tsdusize) || + __put_user(ino->u.socket_i.ops->family, &((struct solaris_si_udata *)A(arg))->sockparams.sp_family) || + __put_user(ino->u.socket_i.type, &((struct solaris_si_udata *)A(arg))->sockparams.sp_type) || + __put_user(ino->u.socket_i.ops->family, &((struct solaris_si_udata *)A(arg))->sockparams.sp_protocol)) + return (EFAULT << 8) | TSYSERR; + return 0; + } + case 101: /* O_SI_GETUDATA */ + { + int etsdusize, servtype; + switch (ino->u.socket_i.type) { + case SOCK_STREAM: + etsdusize = 1; + servtype = 2; + break; + default: + etsdusize = -2; + servtype = 3; + break; + } + if (put_user(16384, &((struct solaris_o_si_udata *)A(arg))->tidusize) || + __put_user(sizeof(struct sockaddr), &((struct solaris_o_si_udata *)A(arg))->addrsize) || + __put_user(-1, &((struct solaris_o_si_udata *)A(arg))->optsize) || + __put_user(etsdusize, &((struct solaris_o_si_udata *)A(arg))->etsdusize) || + __put_user(servtype, &((struct solaris_o_si_udata *)A(arg))->servtype) || + __put_user(0, &((struct solaris_o_si_udata *)A(arg))->so_state) || + __put_user(0, &((struct solaris_o_si_udata *)A(arg))->so_options) || + __put_user(16384, &((struct solaris_o_si_udata *)A(arg))->tsdusize)) + return (EFAULT << 8) | TSYSERR; + return 0; + } + case 102: /* SI_SHUTDOWN */ + case 103: /* SI_LISTEN */ + case 104: /* SI_SETMYNAME */ + case 105: /* SI_SETPEERNAME */ + case 106: /* SI_GETINTRANSIT */ + case 107: /* SI_TCL_LINK */ + case 108: /* SI_TCL_UNLINK */ + } + return TNOTSUPPORT; +} + +static inline int solaris_timod(unsigned int fd, unsigned int cmd, u32 arg, + int len, int *len_p) +{ + struct file *filp; + struct inode *ino; + int ret; + + filp = current->files->fd[fd]; + if (! filp || + ! (ino = filp->f_dentry->d_inode) || + ! ino->i_sock) + return TBADF; + + switch (cmd & 0xff) { + case 141: /* TI_OPTMGMT */ + { + int i; + u32 prim; + SOLD("TI_OPMGMT entry"); + ret = timod_putmsg(fd, (char *)A(arg), len, NULL, -1, 0); + SOLD("timod_putmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + i = MSG_HIPRI; + SOLD("calling timod_getmsg()"); + ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i); + SOLD("timod_getmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + SOLD("ret ok"); + if (get_user(prim, (u32 *)A(arg))) + return (EFAULT << 8) | TSYSERR; + SOLD("got prim"); + if (prim == T_ERROR_ACK) { + u32 tmp, tmp2; + SOLD("prim is T_ERROR_ACK"); + if (get_user(tmp, (u32 *)A(arg)+3) || + get_user(tmp2, (u32 *)A(arg)+2)) + return (EFAULT << 8) | TSYSERR; + return (tmp2 << 8) | tmp; + } + SOLD("TI_OPMGMT return 0"); + return 0; + } + case 142: /* TI_BIND */ + { + int i; + u32 prim; + SOLD("TI_BIND entry"); + ret = timod_putmsg(fd, (char *)A(arg), len, NULL, -1, 0); + SOLD("timod_putmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + len = 1024; /* Solaris allows arbitrary return size */ + i = MSG_HIPRI; + SOLD("calling timod_getmsg()"); + ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i); + SOLD("timod_getmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + SOLD("ret ok"); + if (get_user(prim, (u32 *)A(arg))) + return (EFAULT << 8) | TSYSERR; + SOLD("got prim"); + if (prim == T_ERROR_ACK) { + u32 tmp, tmp2; + SOLD("prim is T_ERROR_ACK"); + if (get_user(tmp, (u32 *)A(arg)+3) || + get_user(tmp2, (u32 *)A(arg)+2)) + return (EFAULT << 8) | TSYSERR; + return (tmp2 << 8) | tmp; + } + SOLD("no ERROR_ACK requested"); + if (prim != T_OK_ACK) + return TBADSEQ; + SOLD("OK_ACK requested"); + i = MSG_HIPRI; + SOLD("calling timod_getmsg()"); + ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i); + SOLD("timod_getmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + SOLD("TI_BIND return ok"); + return 0; + } + case 140: /* TI_GETINFO */ + case 143: /* TI_UNBIND */ + case 144: /* TI_GETMYNAME */ + case 145: /* TI_GETPEERNAME */ + case 146: /* TI_SETMYNAME */ + case 147: /* TI_SETPEERNAME */ + } + return TNOTSUPPORT; +} + static inline int solaris_S(unsigned int fd, unsigned int cmd, u32 arg) { char *p; int ret; mm_segment_t old_fs; struct strioctl si; - + struct inode *ino; + struct file *filp; + struct sol_socket_struct *sock; + struct module_info *mi; + + filp = current->files->fd[fd]; + if (! filp || + ! (ino = filp->f_dentry->d_inode) || + ! ino->i_sock) + return -EBADF; + sock = filp->private_data; + if (! sock) { + printk("solaris_S: NULL private_data\n"); + return -EBADF; + } + if (sock->magic != SOLARIS_SOCKET_MAGIC) { + printk("solaris_S: invalid magic\n"); + return -EBADF; + } + + switch (cmd & 0xff) { case 1: /* I_NREAD */ return -ENOSYS; case 2: /* I_PUSH */ + { p = getname32 (arg); if (IS_ERR (p)) return PTR_ERR(p); + ret = -EINVAL; + for (mi = module_table; mi->name; mi++) { + if (strcmp(mi->name, p) == 0) { + sol_module m; + if (sock->modcount >= MAX_NR_STREAM_MODULES) { + ret = -ENXIO; + break; + } + m = (sol_module) (mi - module_table); + sock->module[sock->modcount++] = m; + ret = 0; + break; + } + } putname32 (p); - return 0; + return ret; + } case 3: /* I_POP */ + if (sock->modcount <= 0) return -EINVAL; + sock->modcount--; return 0; + case 4: /* I_LOOK */ + { + const char *p; + if (sock->modcount <= 0) return -EINVAL; + p = module_table[(unsigned)sock->module[sock->modcount]].name; + if (copy_to_user ((char *)A(arg), p, strlen(p))) + return -EFAULT; + return 0; + } case 5: /* I_FLUSH */ return 0; case 8: /* I_STR */ - if (copy_from_user (&si, (struct strioctl *)A(arg), sizeof(struct strioctl))) + if (copy_from_user(&si, (struct strioctl *)A(arg), sizeof(struct strioctl))) return -EFAULT; + /* We ignore what module is actually at the top of stack. */ switch ((si.cmd >> 8) & 0xff) { + case 'I': + return solaris_sockmod(fd, si.cmd, si.data); case 'T': + return solaris_timod(fd, si.cmd, si.data, si.len, + &((struct strioctl*)A(arg))->len); default: return solaris_ioctl(fd, si.cmd, si.data); } @@ -269,12 +549,25 @@ static inline int solaris_S(unsigned int fd, unsigned int cmd, u32 arg) if (ret == current->pid) return 0x3ff; else return -EINVAL; case 11: /* I_FIND */ + { + int i; p = getname32 (arg); if (IS_ERR (p)) return PTR_ERR(p); - ret = !strcmp(p, "timod"); + ret = 0; + for (i = 0; i < sock->modcount; i++) { + unsigned m = sock->module[i]; + if (strcmp(module_table[m].name, p) == 0) { + ret = 1; + break; + } + } putname32 (p); return ret; + } + case 19: /* I_SWROPT */ + case 32: /* I_SETCLTIME */ + return 0; /* Lie */ } return -ENOSYS; } @@ -287,7 +580,8 @@ static inline int solaris_s(unsigned int fd, unsigned int cmd, u32 arg) return 0; /* We don't support them */ case 1: /* SIOCGHIWAT */ case 3: /* SIOCGLOWAT */ - put_user_ret (0, (u32 *)A(arg), -EFAULT); + if (put_user (0, (u32 *)A(arg))) + return -EFAULT; return 0; /* Lie */ case 7: /* SIOCATMARK */ return sys_ioctl(fd, SIOCATMARK, arg); @@ -368,8 +662,10 @@ static inline int solaris_i(unsigned int fd, unsigned int cmd, u32 arg) ret = sys_socketcall(((cmd & 0xff) == 52) ? SYS_GETSOCKNAME : SYS_GETPEERNAME, args); set_fs(old_fs); - if (ret >= 0) - copy_to_user_ret((char *)A(arg), &uaddr, uaddr_len, -EFAULT); + if (ret >= 0) { + if (copy_to_user((char *)A(arg), &uaddr, uaddr_len)) + return -EFAULT; + } return ret; } #if 0 @@ -382,7 +678,8 @@ static inline int solaris_i(unsigned int fd, unsigned int cmd, u32 arg) int i = 0; for (d = dev_base; d; d = d->next) i++; - put_user_ret (i, (int *)A(arg), -EFAULT); + if (put_user (i, (int *)A(arg))) + return -EFAULT; return 0; } } @@ -393,14 +690,13 @@ static inline int solaris_i(unsigned int fd, unsigned int cmd, u32 arg) asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg) { - struct file * filp; + struct file *filp; int error = -EBADF; lock_kernel(); - if(fd >= NR_OPEN) goto out; - - filp = current->files->fd[fd]; - if(!filp) goto out; + filp = fcheck(fd); + if (!filp) + goto out; error = -EFAULT; switch ((cmd >> 8) & 0xff) { @@ -410,6 +706,7 @@ asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case 'r': error = solaris_r(fd, cmd, arg); break; case 's': error = solaris_s(fd, cmd, arg); break; case 't': error = solaris_t(fd, cmd, arg); break; + case 'f': error = sys_ioctl(fd, cmd, arg); break; default: error = -ENOSYS; break; diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index 7a6fb5969..4179f54b3 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.6 1997/12/14 23:40:15 ecd Exp $ +/* $Id: misc.c,v 1.10 1998/04/01 05:16:06 davem Exp $ * misc.c: Miscelaneous syscall emulation for Solaris * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -106,7 +106,7 @@ static char *machine(void) static char *platform(char *buffer) { - int i; + int i, len; struct { char *platform; int id_machtype; @@ -128,7 +128,9 @@ static char *platform(char *buffer) }; *buffer = 0; - prom_getproperty(prom_root_node, "name", buffer, 256); + len = prom_getproperty(prom_root_node, "name", buffer, 256); + if(len > 0) + buffer[len] = 0; if (*buffer) { char *p; @@ -145,10 +147,13 @@ static char *platform(char *buffer) static char *serial(char *buffer) { int node = prom_getchild(prom_root_node); + int len; node = prom_searchsiblings(node, "options"); *buffer = 0; - prom_getproperty(node, "system-board-serial#", buffer, 256); + len = prom_getproperty(node, "system-board-serial#", buffer, 256); + if(len > 0) + buffer[len] = 0; if (!*buffer) return "4512348717234"; else @@ -268,6 +273,8 @@ asmlinkage int solaris_sysinfo(int cmd, u32 buf, s32 count) #define SOLARIS_CONFIG_PHYS_PAGES 26 #define SOLARIS_CONFIG_AVPHYS_PAGES 27 +extern unsigned prom_cpu_nodes[NR_CPUS]; + asmlinkage int solaris_sysconf(int id) { switch (id) { @@ -279,9 +286,8 @@ asmlinkage int solaris_sysconf(int id) case SOLARIS_CONFIG_XOPEN_VER: return 3; case SOLARIS_CONFIG_CLK_TCK: case SOLARIS_CONFIG_PROF_TCK: - return prom_getintdefault( - linux_cpus[smp_processor_id()].prom_node, - "clock-frequency", 167000000); + return prom_getintdefault(prom_cpu_nodes[smp_processor_id()], + "clock-frequency", 167000000); #ifdef __SMP__ case SOLARIS_CONFIG_NPROC_CONF: return NR_CPUS; case SOLARIS_CONFIG_NPROC_ONLN: return smp_num_cpus; @@ -362,6 +368,14 @@ asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid) return -EINVAL; } +asmlinkage int solaris_gettimeofday(u32 tim) +{ + int (*sys_gettimeofday)(struct timeval *, struct timezone *) = + (int (*)(struct timeval *, struct timezone *))SYS(gettimeofday); + + return sys_gettimeofday((struct timeval *)(u64)tim, NULL); +} + asmlinkage int do_sol_unimplemented(struct pt_regs *regs) { printk ("Unimplemented Solaris syscall %d %08x %08x %08x %08x\n", @@ -401,10 +415,13 @@ struct exec_domain solaris_exec_domain = { NULL }; +extern int init_socksys(void); + #ifdef MODULE -MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz)"); +MODULE_AUTHOR("Jakub Jelinek (jj@ultra.linux.cz), Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)"); MODULE_DESCRIPTION("Solaris binary emulation module"); +EXPORT_NO_SYMBOLS; #ifdef __sparc_v9__ extern u32 tl0_solaris[8]; @@ -416,12 +433,13 @@ extern u32 tl0_solaris[8]; extern u32 solaris_sparc_syscall[]; extern u32 solaris_syscall[]; -extern int init_socksys(void); extern void cleanup_socksys(void); int init_module(void) { int ret; + + SOLDD(("Solaris module at %p\n", solaris_sparc_syscall)); register_exec_domain(&solaris_exec_domain); if ((ret = init_socksys())) { unregister_exec_domain(&solaris_exec_domain); diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c index 3a18916c9..523073a6d 100644 --- a/arch/sparc64/solaris/socksys.c +++ b/arch/sparc64/solaris/socksys.c @@ -1,7 +1,8 @@ -/* $Id: socksys.c,v 1.2 1997/09/08 11:29:38 jj Exp $ +/* $Id: socksys.c,v 1.7 1998/03/29 10:11:04 davem Exp $ * socksys.c: /dev/inet/ stuff for Solaris emulation. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997, 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz) * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk) */ @@ -12,14 +13,17 @@ #include <linux/smp_lock.h> #include <linux/ioctl.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/init.h> #include <linux/poll.h> #include <linux/file.h> +#include <linux/malloc.h> #include <asm/uaccess.h> #include <asm/termios.h> #include "conv.h" +#include "socksys.h" extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); @@ -30,6 +34,20 @@ IPPROTO_EGP, IPPROTO_PUP, IPPROTO_UDP, IPPROTO_IDP, IPPROTO_RAW, 0, 0, 0, 0, 0, 0, }; +#ifndef DEBUG_SOLARIS_KMALLOC + +#define mykmalloc kmalloc +#define mykfree kfree + +#else + +extern void * mykmalloc(size_t s, int gfp); +extern void mykfree(void *); + +#endif + +static unsigned int (*sock_poll)(struct file *, poll_table *); + static struct file_operations socksys_file_ops = { NULL, /* lseek */ NULL, /* read */ @@ -48,6 +66,7 @@ static int socksys_open(struct inode * inode, struct file * filp) struct dentry *dentry; int (*sys_socket)(int,int,int) = (int (*)(int,int,int))SUNOS(97); + struct sol_socket_struct * sock; family = ((MINOR(inode->i_rdev) >> 4) & 0xf); switch (family) { @@ -68,30 +87,78 @@ static int socksys_open(struct inode * inode, struct file * filp) protocol = 0; break; } + fd = sys_socket(family, type, protocol); - if (fd < 0) return fd; + if (fd < 0) + return fd; + /* + * N.B. The following operations are not legal! + * Try instead: + * d_delete(filp->f_dentry), then d_instantiate with sock inode + */ dentry = filp->f_dentry; - filp->f_dentry = current->files->fd[fd]->f_dentry; + filp->f_dentry = dget(fcheck(fd)->f_dentry); filp->f_dentry->d_inode->i_rdev = inode->i_rdev; filp->f_dentry->d_inode->i_flock = inode->i_flock; filp->f_dentry->d_inode->u.socket_i.file = filp; filp->f_op = &socksys_file_ops; + sock = (struct sol_socket_struct*) + mykmalloc(sizeof(struct sol_socket_struct), GFP_KERNEL); + if (!sock) return -ENOMEM; + SOLDD(("sock=%016lx(%016lx)\n", sock, filp)); + sock->magic = SOLARIS_SOCKET_MAGIC; + sock->modcount = 0; + sock->state = TS_UNBND; + sock->offset = 0; + sock->pfirst = sock->plast = NULL; + filp->private_data = sock; + SOLDD(("filp->private_data %016lx\n", filp->private_data)); + + sys_close(fd); dput(dentry); - FD_CLR(fd, ¤t->files->close_on_exec); - FD_CLR(fd, ¤t->files->open_fds); - put_filp(current->files->fd[fd]); - current->files->fd[fd] = NULL; return 0; } static int socksys_release(struct inode * inode, struct file * filp) { + struct sol_socket_struct * sock; + struct T_primsg *it; + + /* XXX: check this */ + sock = (struct sol_socket_struct *)filp->private_data; + SOLDD(("sock release %016lx(%016lx)\n", sock, filp)); + it = sock->pfirst; + while (it) { + struct T_primsg *next = it->next; + + SOLDD(("socksys_release %016lx->%016lx\n", it, next)); + mykfree((char*)it); + it = next; + } + filp->private_data = NULL; + SOLDD(("socksys_release %016lx\n", sock)); + mykfree((char*)sock); return 0; } static unsigned int socksys_poll(struct file * filp, poll_table * wait) { - return 0; + struct inode *ino; + unsigned int mask = 0; + + ino=filp->f_dentry->d_inode; + if (ino && ino->i_sock) { + struct sol_socket_struct *sock; + sock = (struct sol_socket_struct*)filp->private_data; + if (sock && sock->pfirst) { + mask |= POLLIN | POLLRDNORM; + if (sock->pfirst->pri == MSG_HIPRI) + mask |= POLLPRI; + } + } + if (sock_poll) + mask |= (*sock_poll)(filp, wait); + return mask; } static struct file_operations socksys_fops = { @@ -110,6 +177,7 @@ __initfunc(int init_socksys(void)) { int ret; + struct file * file; int (*sys_socket)(int,int,int) = (int (*)(int,int,int))SUNOS(97); int (*sys_close)(unsigned int) = @@ -125,8 +193,11 @@ init_socksys(void)) printk ("Couldn't create socket\n"); return ret; } - socksys_file_ops = *current->files->fd[ret]->f_op; + file = fcheck(ret); + /* N.B. Is this valid? Suppose the f_ops are in a module ... */ + socksys_file_ops = *file->f_op; sys_close(ret); + sock_poll = socksys_file_ops.poll; socksys_file_ops.poll = socksys_poll; socksys_file_ops.release = socksys_release; return 0; diff --git a/arch/sparc64/solaris/socksys.h b/arch/sparc64/solaris/socksys.h new file mode 100644 index 000000000..5d1b78ec1 --- /dev/null +++ b/arch/sparc64/solaris/socksys.h @@ -0,0 +1,208 @@ +/* $Id: socksys.h,v 1.2 1998/03/26 08:46:07 jj Exp $ + * socksys.h: Definitions for STREAMS modules emulation code. + * + * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz) + */ + +#define MSG_HIPRI 0x01 +#define MSG_ANY 0x02 +#define MSG_BAND 0x04 + +#define MORECTL 1 +#define MOREDATA 2 + +#define TBADADDR 1 +#define TBADOPT 2 +#define TACCES 3 +#define TBADF 4 +#define TNOADDR 5 +#define TOUTSTATE 6 +#define TBADSEQ 7 +#define TSYSERR 8 +#define TLOOK 9 +#define TBADDATA 10 +#define TBUFOVFLW 11 +#define TFLOW 12 +#define TNODATA 13 +#define TNODIS 14 +#define TNOUDERR 15 +#define TBADFLAG 16 +#define TNOREL 17 +#define TNOTSUPPORT 18 +#define TSTATECHNG 19 + +#define T_CONN_REQ 0 +#define T_CONN_RES 1 +#define T_DISCON_REQ 2 +#define T_DATA_REQ 3 +#define T_EXDATA_REQ 4 +#define T_INFO_REQ 5 +#define T_BIND_REQ 6 +#define T_UNBIND_REQ 7 +#define T_UNITDATA_REQ 8 +#define T_OPTMGMT_REQ 9 +#define T_ORDREL_REQ 10 + +#define T_CONN_IND 11 +#define T_CONN_CON 12 +#define T_DISCON_IND 13 +#define T_DATA_IND 14 +#define T_EXDATA_IND 15 +#define T_INFO_ACK 16 +#define T_BIND_ACK 17 +#define T_ERROR_ACK 18 +#define T_OK_ACK 19 +#define T_UNITDATA_IND 20 +#define T_UDERROR_IND 21 +#define T_OPTMGMT_ACK 22 +#define T_ORDREL_IND 23 + +#define T_NEGOTIATE 0x0004 +#define T_FAILURE 0x0040 + +#define TS_UNBND 0 /* unbound */ +#define TS_WACK_BREQ 1 /* waiting for T_BIND_REQ ack */ +#define TS_WACK_UREQ 2 /* waiting for T_UNBIND_REQ ack */ +#define TS_IDLE 3 /* idle */ +#define TS_WACK_OPTREQ 4 /* waiting for T_OPTMGMT_REQ ack */ +#define TS_WACK_CREQ 5 /* waiting for T_CONN_REQ ack */ +#define TS_WCON_CREQ 6 /* waiting for T_CONN_REQ confirmation */ +#define TS_WRES_CIND 7 /* waiting for T_CONN_IND */ +#define TS_WACK_CRES 8 /* waiting for T_CONN_RES ack */ +#define TS_DATA_XFER 9 /* data transfer */ +#define TS_WIND_ORDREL 10 /* releasing read but not write */ +#define TS_WREQ_ORDREL 11 /* wait to release write but not read */ +#define TS_WACK_DREQ6 12 /* waiting for T_DISCON_REQ ack */ +#define TS_WACK_DREQ7 13 /* waiting for T_DISCON_REQ ack */ +#define TS_WACK_DREQ9 14 /* waiting for T_DISCON_REQ ack */ +#define TS_WACK_DREQ10 15 /* waiting for T_DISCON_REQ ack */ +#define TS_WACK_DREQ11 16 /* waiting for T_DISCON_REQ ack */ +#define TS_NOSTATES 17 + +struct T_conn_req { + s32 PRIM_type; + s32 DEST_length; + s32 DEST_offset; + s32 OPT_length; + s32 OPT_offset; +}; + +struct T_bind_req { + s32 PRIM_type; + s32 ADDR_length; + s32 ADDR_offset; + u32 CONIND_number; +}; + +struct T_unitdata_req { + s32 PRIM_type; + s32 DEST_length; + s32 DEST_offset; + s32 OPT_length; + s32 OPT_offset; +}; + +struct T_optmgmt_req { + s32 PRIM_type; + s32 OPT_length; + s32 OPT_offset; + s32 MGMT_flags; +}; + +struct T_bind_ack { + s32 PRIM_type; + s32 ADDR_length; + s32 ADDR_offset; + u32 CONIND_number; +}; + +struct T_error_ack { + s32 PRIM_type; + s32 ERROR_prim; + s32 TLI_error; + s32 UNIX_error; +}; + +struct T_ok_ack { + s32 PRIM_type; + s32 CORRECT_prim; +}; + +struct T_conn_ind { + s32 PRIM_type; + s32 SRC_length; + s32 SRC_offset; + s32 OPT_length; + s32 OPT_offset; + s32 SEQ_number; +}; + +struct T_conn_con { + s32 PRIM_type; + s32 RES_length; + s32 RES_offset; + s32 OPT_length; + s32 OPT_offset; +}; + +struct T_discon_ind { + s32 PRIM_type; + s32 DISCON_reason; + s32 SEQ_number; +}; + +struct T_unitdata_ind { + s32 PRIM_type; + s32 SRC_length; + s32 SRC_offset; + s32 OPT_length; + s32 OPT_offset; +}; + +struct T_optmgmt_ack { + s32 PRIM_type; + s32 OPT_length; + s32 OPT_offset; + s32 MGMT_flags; +}; + +struct opthdr { + s32 level; + s32 name; + s32 len; + char value[0]; +}; + +struct T_primsg { + struct T_primsg *next; + unsigned char pri; + unsigned char band; + int length; + s32 type; +}; + +struct strbuf { + s32 maxlen; + s32 len; + u32 buf; +} ; + +/* Constants used by STREAMS modules emulation code */ + +typedef char sol_module; + +#define MAX_NR_STREAM_MODULES 16 + +/* Private data structure assigned to sockets. */ + +struct sol_socket_struct { + int magic; + int modcount; + sol_module module[MAX_NR_STREAM_MODULES]; + long state; + int offset; + struct T_primsg *pfirst, *plast; +}; + +#define SOLARIS_SOCKET_MAGIC 0xADDED + diff --git a/arch/sparc64/solaris/systbl.S b/arch/sparc64/solaris/systbl.S index c425fb721..bb18807f1 100644 --- a/arch/sparc64/solaris/systbl.S +++ b/arch/sparc64/solaris/systbl.S @@ -1,4 +1,4 @@ -/* $Id: systbl.S,v 1.5 1997/09/04 15:46:24 jj Exp $ +/* $Id: systbl.S,v 1.6 1998/03/26 08:46:08 jj Exp $ * systbl.S: System call entry point table for Solaris compatibility. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -14,11 +14,9 @@ #define REGS(name) name+1 /* Hack till all be implemented */ -#define solaris_getmsg solaris_unimplemented #define solaris_getpmsg solaris_unimplemented #define solaris_hrtsys solaris_unimplemented #define solaris_msgsys solaris_unimplemented -#define solaris_putmsg solaris_unimplemented #define solaris_putpmsg solaris_unimplemented #define solaris_semsys solaris_unimplemented @@ -115,8 +113,8 @@ solaris_sys_table: .word solaris_unimplemented /* libattach 82 */ .word solaris_unimplemented /* libdetach 83 */ .word CHAIN(sysfs) /* sysfs dxx 84 */ - .word REGS(solaris_getmsg) /* getmsg dxxx 85 */ - .word REGS(solaris_putmsg) /* putmsg dxxd 86 */ + .word solaris_getmsg /* getmsg dxxx 85 */ + .word solaris_putmsg /* putmsg dxxd 86 */ .word CHAIN(poll) /* poll xdd 87 */ .word solaris_lstat /* lstat sp 88 */ .word CHAIN(symlink) /* symlink ss 89 */ @@ -186,7 +184,7 @@ solaris_sys_table: .word solaris_unimplemented /* fchroot d 153 */ .word solaris_unimplemented /* lvlproc dx 154 */ .word solaris_unimplemented /* ? 155 */ - .word CHAIN(gettimeofday) /* gettimeofday xx 156 */ + .word solaris_gettimeofday /* gettimeofday x 156 */ .word CHAIN(getitimer) /* getitimer dx 157 */ .word CHAIN(setitimer) /* setitimer dxx 158 */ .word solaris_unimplemented /* lwp-xxx 159 */ diff --git a/arch/sparc64/solaris/timod.c b/arch/sparc64/solaris/timod.c new file mode 100644 index 000000000..7673702d2 --- /dev/null +++ b/arch/sparc64/solaris/timod.c @@ -0,0 +1,979 @@ +/* $Id: timod.c,v 1.1 1998/03/26 08:46:18 jj Exp $ + * timod.c: timod emulation. + * + * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz) + * + * Streams & timod emulation based on code + * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk) + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/netdevice.h> +#include <linux/poll.h> + +#include <asm/uaccess.h> +#include <asm/termios.h> + +#include "conv.h" +#include "socksys.h" + +extern char *getname32(u32 filename); +#define putname32 putname + +extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, + unsigned long arg); +extern asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, + u32 arg); +asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg); + +#ifdef __SMP__ +spinlock_t timod_pagelock = SPIN_LOCK_UNLOCKED; +#endif +static char * page = NULL ; + +#ifndef DEBUG_SOLARIS_KMALLOC + +#define mykmalloc kmalloc +#define mykfree kfree + +#else + +void * mykmalloc(size_t s, int gfp) +{ + static char * page; + static size_t free = 0; + void * r; + s = ((s + 63) & ~63); + if( s > PAGE_SIZE ) { + SOLD("too big size, calling real kmalloc"); + return kmalloc(s, gfp); + } + if( s > free ) { + /* we are wasting memory, but we don't care */ + page = (char *)__get_free_page(gfp); + free = PAGE_SIZE; + } + r = page; + page += s; + free -= s; + return r; +} + +void mykfree(void *p) +{ +} + +#endif + +#ifndef DEBUG_SOLARIS + +#define BUF_SIZE PAGE_SIZE +#define PUT_MAGIC(a,m) +#define CHECK_MAGIC(a,m) +#define BUF_OFFSET 0 +#define MKCTL_TRAILER 0 + +#else + +#define BUF_SIZE (PAGE_SIZE-2*sizeof(u64)) +#define BUFPAGE_MAGIC 0xBADC0DEDDEADBABEL +#define MKCTL_MAGIC 0xDEADBABEBADC0DEDL +#define PUT_MAGIC(a,m) do{(*(u64*)(a))=(m);}while(0) +#define CHECK_MAGIC(a,m) do{if((*(u64*)(a))!=(m))printk("%s,%u,%s(): magic %08x at %p corrupted!\n",\ + __FILE__,__LINE__,__FUNCTION__,(m),(a));}while(0) +#define BUF_OFFSET sizeof(u64) +#define MKCTL_TRAILER sizeof(u64) + +#endif + +static char *getpage( void ) +{ + char *r; + SOLD("getting page"); + spin_lock(&timod_pagelock); + if (page) { + r = page; + page = NULL; + spin_unlock(&timod_pagelock); + SOLD("got cached"); + return r + BUF_OFFSET; + } + spin_unlock(&timod_pagelock); + SOLD("getting new"); + r = (char *)__get_free_page(GFP_KERNEL); + PUT_MAGIC(r,BUFPAGE_MAGIC); + PUT_MAGIC(r+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC); + return r + BUF_OFFSET; +} + +static void putpage(char *p) +{ + SOLD("putting page"); + p = p - BUF_OFFSET; + CHECK_MAGIC(p,BUFPAGE_MAGIC); + CHECK_MAGIC(p+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC); + spin_lock(&timod_pagelock); + if (page) { + spin_unlock(&timod_pagelock); + free_page((unsigned long)p); + SOLD("freed it"); + } else { + page = p; + spin_unlock(&timod_pagelock); + SOLD("cached it"); + } +} + +static struct T_primsg *timod_mkctl(int size) +{ + struct T_primsg *it; + + SOLD("creating primsg"); + it = (struct T_primsg *)mykmalloc(size+sizeof(*it)-sizeof(s32)+2*MKCTL_TRAILER, GFP_KERNEL); + if (it) { + SOLD("got it"); + it->pri = MSG_HIPRI; + it->length = size; + PUT_MAGIC((char*)((u64)(((char *)&it->type)+size+7)&~7),MKCTL_MAGIC); + } + return it; +} + +static void timod_wake_socket(unsigned int fd) +{ + struct socket *sock; + + SOLD("wakeing socket"); + sock = ¤t->files->fd[fd]->f_dentry->d_inode->u.socket_i; + wake_up_interruptible(&sock->wait); + if (sock->fasync_list && !(sock->flags & SO_WAITDATA)) + kill_fasync(sock->fasync_list, SIGIO); + SOLD("done"); +} + +static void timod_queue(unsigned int fd, struct T_primsg *it) +{ + struct sol_socket_struct *sock; + + SOLD("queuing primsg"); + sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; + it->next = sock->pfirst; + sock->pfirst = it; + if (!sock->plast) + sock->plast = it; + timod_wake_socket(fd); + SOLD("done"); +} + +static void timod_queue_end(unsigned int fd, struct T_primsg *it) +{ + struct sol_socket_struct *sock; + + SOLD("queuing primsg at end"); + sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; + it->next = NULL; + if (sock->plast) + sock->plast->next = it; + else + sock->pfirst = it; + sock->plast = it; + SOLD("done"); +} + +static void timod_error(unsigned int fd, int prim, int terr, int uerr) +{ + struct T_primsg *it; + + SOLD("making error"); + it = timod_mkctl(sizeof(struct T_error_ack)); + if (it) { + struct T_error_ack *err = (struct T_error_ack *)&it->type; + + SOLD("got it"); + err->PRIM_type = T_ERROR_ACK; + err->ERROR_prim = prim; + err->TLI_error = terr; + err->UNIX_error = uerr; /* FIXME: convert this */ + timod_queue(fd, it); + } + SOLD("done"); +} + +static void timod_ok(unsigned int fd, int prim) +{ + struct T_primsg *it; + struct T_ok_ack *ok; + + SOLD("creating ok ack"); + it = timod_mkctl(sizeof(*ok)); + if (it) { + SOLD("got it"); + ok = (struct T_ok_ack *)&it->type; + ok->PRIM_type = T_OK_ACK; + ok->CORRECT_prim = prim; + timod_queue(fd, it); + } + SOLD("done"); +} + +static int timod_optmgmt(unsigned int fd, int flag, char *opt_buf, int opt_len, int do_ret) +{ + int error, failed; + int ret_space, ret_len; + long args[5]; + char *ret_pos,*ret_buf; + int (*sys_socketcall)(int, unsigned long *) = + (int (*)(int, unsigned long *))SYS(socketcall); + mm_segment_t old_fs = get_fs(); + + SOLD("entry"); + SOLDD(("fd %u flg %u buf %p len %u doret %u",fd,flag,opt_buf,opt_len,do_ret)); + if (!do_ret && (!opt_buf || opt_len <= 0)) + return 0; + SOLD("getting page"); + ret_pos = ret_buf = getpage(); + ret_space = BUF_SIZE; + ret_len = 0; + + error = failed = 0; + SOLD("looping"); + while(opt_len >= sizeof(struct opthdr)) { + struct opthdr *opt; + int orig_opt_len; + SOLD("loop start"); + opt = (struct opthdr *)ret_pos; + if (ret_space < sizeof(struct opthdr)) { + failed = TSYSERR; + break; + } + SOLD("getting opthdr"); + if (copy_from_user(opt, opt_buf, sizeof(struct opthdr)) || + opt->len > opt_len) { + failed = TBADOPT; + break; + } + SOLD("got opthdr"); + if (flag == T_NEGOTIATE) { + char *buf; + + SOLD("handling T_NEGOTIATE"); + buf = ret_pos + sizeof(struct opthdr); + if (ret_space < opt->len + sizeof(struct opthdr) || + copy_from_user(buf, opt_buf+sizeof(struct opthdr), opt->len)) { + failed = TSYSERR; + break; + } + SOLD("got optdata"); + args[0] = fd; + args[1] = opt->level; + args[2] = opt->name; + args[3] = (long)buf; + args[4] = opt->len; + SOLD("calling SETSOCKOPT"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_SETSOCKOPT, args); + set_fs(old_fs); + if (error) { + failed = TBADOPT; + break; + } + SOLD("SETSOCKOPT ok"); + } + orig_opt_len = opt->len; + opt->len = ret_space - sizeof(struct opthdr); + if (opt->len < 0) { + failed = TSYSERR; + break; + } + args[0] = fd; + args[1] = opt->level; + args[2] = opt->name; + args[3] = (long)(ret_pos+sizeof(struct opthdr)); + args[4] = (long)&opt->len; + SOLD("calling GETSOCKOPT"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_GETSOCKOPT, args); + set_fs(old_fs);; + if (error) { + failed = TBADOPT; + break; + } + SOLD("GETSOCKOPT ok"); + ret_space -= sizeof(struct opthdr) + opt->len; + ret_len += sizeof(struct opthdr) + opt->len; + ret_pos += sizeof(struct opthdr) + opt->len; + opt_len -= sizeof(struct opthdr) + orig_opt_len; + opt_buf += sizeof(struct opthdr) + orig_opt_len; + SOLD("loop end"); + } + SOLD("loop done"); + if (do_ret) { + SOLD("generating ret msg"); + if (failed) + timod_error(fd, T_OPTMGMT_REQ, failed, -error); + else { + struct T_primsg *it; + it = timod_mkctl(sizeof(struct T_optmgmt_ack) + ret_len); + if (it) { + struct T_optmgmt_ack *ack = + (struct T_optmgmt_ack *)&it->type; + SOLD("got primsg"); + ack->PRIM_type = T_OPTMGMT_ACK; + ack->OPT_length = ret_len; + ack->OPT_offset = sizeof(struct T_optmgmt_ack); + ack->MGMT_flags = (failed ? T_FAILURE : flag); + memcpy(((char*)ack)+sizeof(struct T_optmgmt_ack), + ret_buf, ret_len); + timod_queue(fd, it); + } + } + } + SOLDD(("put_page %p\n", ret_buf)); + putpage(ret_buf); + SOLD("done"); + return 0; +} + +int timod_putmsg(unsigned int fd, char *ctl_buf, int ctl_len, + char *data_buf, int data_len, int flags) +{ + int ret, error, terror; + char *buf; + struct file *filp; + struct inode *ino; + struct sol_socket_struct *sock; + mm_segment_t old_fs = get_fs(); + long args[6]; + int (*sys_socketcall)(int, unsigned long *) = + (int (*)(int, unsigned long *))SYS(socketcall); + int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int) = + (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int))SYS(sendto); + filp = current->files->fd[fd]; + ino = filp->f_dentry->d_inode; + sock = (struct sol_socket_struct *)filp->private_data; + SOLD("entry"); + if (get_user(ret, (int *)A(ctl_buf))) + return -EFAULT; + switch (ret) { + case T_BIND_REQ: + { + struct T_bind_req req; + + SOLDD(("bind %016lx(%016lx)\n", sock, filp)); + SOLD("T_BIND_REQ"); + if (sock->state != TS_UNBND) { + timod_error(fd, T_BIND_REQ, TOUTSTATE, 0); + return 0; + } + SOLD("state ok"); + if (copy_from_user(&req, ctl_buf, sizeof(req))) { + timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("got ctl req"); + if (req.ADDR_offset && req.ADDR_length) { + if (req.ADDR_length > BUF_SIZE) { + timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("req size ok"); + buf = getpage(); + if (copy_from_user(buf, ctl_buf + req.ADDR_offset, req.ADDR_length)) { + timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); + putpage(buf); + return 0; + } + SOLD("got ctl data"); + args[0] = fd; + args[1] = (long)buf; + args[2] = req.ADDR_length; + SOLD("calling BIND"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_BIND, args); + set_fs(old_fs); + putpage(buf); + SOLD("BIND returned"); + } else + error = 0; + if (!error) { + struct T_primsg *it; + if (req.CONIND_number) { + args[0] = fd; + args[1] = req.CONIND_number; + SOLD("calling LISTEN"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_LISTEN, args); + set_fs(old_fs); + SOLD("LISTEN done"); + } + it = timod_mkctl(sizeof(struct T_bind_ack)+sizeof(struct sockaddr)); + if (it) { + struct T_bind_ack *ack; + + ack = (struct T_bind_ack *)&it->type; + ack->PRIM_type = T_BIND_ACK; + ack->ADDR_offset = sizeof(*ack); + ack->ADDR_length = sizeof(struct sockaddr); + ack->CONIND_number = req.CONIND_number; + args[0] = fd; + args[1] = (long)(ack+sizeof(*ack)); + args[2] = (long)&ack->ADDR_length; + set_fs(KERNEL_DS); + sys_socketcall(SYS_GETSOCKNAME,args); + set_fs(old_fs); + sock->state = TS_IDLE; + timod_ok(fd, T_BIND_REQ); + timod_queue_end(fd, it); + SOLD("BIND done"); + return 0; + } + } + SOLD("some error"); + switch (error) { + case -EINVAL: + terror = TOUTSTATE; + error = 0; + break; + case -EACCES: + terror = TACCES; + error = 0; + break; + case -EADDRNOTAVAIL: + case -EADDRINUSE: + terror = TNOADDR; + error = 0; + break; + default: + terror = TSYSERR; + break; + } + timod_error(fd, T_BIND_REQ, terror, -error); + SOLD("BIND done"); + return 0; + } + case T_CONN_REQ: + { + struct T_conn_req req; + unsigned short oldflags; + struct T_primsg *it; + SOLD("T_CONN_REQ"); + if (sock->state != TS_UNBND && sock->state != TS_IDLE) { + timod_error(fd, T_CONN_REQ, TOUTSTATE, 0); + return 0; + } + SOLD("state ok"); + if (copy_from_user(&req, ctl_buf, sizeof(req))) { + timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("got ctl req"); + if (ctl_len > BUF_SIZE) { + timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("req size ok"); + buf = getpage(); + if (copy_from_user(buf, ctl_buf, ctl_len)) { + timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); + putpage(buf); + return 0; + } +#ifdef DEBUG_SOLARIS + { + char * ptr = buf; + int len = ctl_len; + printk("returned data (%d bytes): ",len); + while( len-- ) { + if (!(len & 7)) + printk(" "); + printk("%02x",(unsigned char)*ptr++); + } + printk("\n"); + } +#endif + SOLD("got ctl data"); + args[0] = fd; + args[1] = (long)buf+req.DEST_offset; + args[2] = req.DEST_length; + oldflags = filp->f_flags; + filp->f_flags &= ~O_NONBLOCK; + SOLD("calling CONNECT"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_CONNECT, args); + set_fs(old_fs); + filp->f_flags = oldflags; + SOLD("CONNECT done"); + if (!error) { + struct T_conn_con *con; + SOLD("no error"); + it = timod_mkctl(ctl_len); + if (!it) { + putpage(buf); + return -ENOMEM; + } + con = (struct T_conn_con *)&it->type; +#ifdef DEBUG_SOLARIS + { + char * ptr = buf; + int len = ctl_len; + printk("returned data (%d bytes): ",len); + while( len-- ) { + if (!(len & 7)) + printk(" "); + printk("%02x",(unsigned char)*ptr++); + } + printk("\n"); + } +#endif + memcpy(con, buf, ctl_len); + SOLD("copied ctl_buf"); + con->PRIM_type = T_CONN_CON; + sock->state = TS_DATA_XFER; + } else { + struct T_discon_ind *dis; + SOLD("some error"); + it = timod_mkctl(sizeof(*dis)); + if (!it) { + putpage(buf); + return -ENOMEM; + } + SOLD("got primsg"); + dis = (struct T_discon_ind *)&it->type; + dis->PRIM_type = T_DISCON_IND; + dis->DISCON_reason = -error; /* FIXME: convert this as in iABI_errors() */ + dis->SEQ_number = 0; + } + putpage(buf); + timod_ok(fd, T_CONN_REQ); + it->pri = 0; + timod_queue_end(fd, it); + SOLD("CONNECT done"); + return 0; + } + case T_OPTMGMT_REQ: + { + struct T_optmgmt_req req; + SOLD("OPTMGMT_REQ"); + if (copy_from_user(&req, ctl_buf, sizeof(req))) + return -EFAULT; + SOLD("got req"); + return timod_optmgmt(fd, req.MGMT_flags, + req.OPT_offset > 0 ? ctl_buf + req.OPT_offset : NULL, + req.OPT_length, 1); + } + case T_UNITDATA_REQ: + { + struct T_unitdata_req req; + + int err; + SOLD("T_UNITDATA_REQ"); + if (sock->state != TS_IDLE && sock->state != TS_DATA_XFER) { + timod_error(fd, T_CONN_REQ, TOUTSTATE, 0); + return 0; + } + SOLD("state ok"); + if (copy_from_user(&req, ctl_buf, sizeof(req))) { + timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("got ctl req"); +#ifdef DEBUG_SOLARIS + { + char * ptr = ctl_buf+req.DEST_offset; + int len = req.DEST_length; + printk("socket address (%d bytes): ",len); + while( len-- ) { + char c; + if (get_user(c,ptr)) + printk("??"); + else + printk("%02x",(unsigned char)c); + ptr++; + } + printk("\n"); + } +#endif + err = sys_sendto(fd, data_buf, data_len, 0, req.DEST_length > 0 ? (struct sockaddr*)(ctl_buf+req.DEST_offset) : NULL, req.DEST_length); + if (err == data_len) + return 0; + if(err >= 0) { + printk("timod: sendto failed to send all the data\n"); + return 0; + } + timod_error(fd, T_CONN_REQ, TSYSERR, -err); + return 0; + } + default: + printk("timod_putmsg: unsuported command %u.\n", ret); + break; + } + return -EINVAL; +} + +/* copied directly from fs/select.c */ + +static void free_wait(poll_table * p) +{ + struct poll_table_entry * entry = p->entry + p->nr; + + SOLD("entry"); + while (p->nr > 0) { + p->nr--; + entry--; + remove_wait_queue(entry->wait_address,&entry->wait); + } + SOLD("done"); +} + + +int timod_getmsg(unsigned int fd, char *ctl_buf, int ctl_maxlen, s32 *ctl_len, + char *data_buf, int data_maxlen, s32 *data_len, int *flags_p) +{ + int error; + int oldflags; + struct file *filp; + struct inode *ino; + struct sol_socket_struct *sock; + struct T_unitdata_ind udi; + mm_segment_t old_fs = get_fs(); + long args[6]; + char *tmpbuf; + int tmplen; + int (*sys_socketcall)(int, unsigned long *) = + (int (*)(int, unsigned long *))SYS(socketcall); + int (*sys_recvfrom)(int, void *, size_t, unsigned, struct sockaddr *, int *); + + SOLD("entry"); + SOLDD(("%u %p %d %p %p %d %p %d\n", fd, ctl_buf, ctl_maxlen, ctl_len, data_buf, data_maxlen, data_len, *flags_p)); + filp = current->files->fd[fd]; + ino = filp->f_dentry->d_inode; + sock = (struct sol_socket_struct *)filp->private_data; + SOLDD(("%p %p\n", sock->pfirst, sock->pfirst ? sock->pfirst->next : NULL)); + if ( ctl_maxlen > 0 && !sock->pfirst && ino->u.socket_i.type == SOCK_STREAM + && sock->state == TS_IDLE) { + SOLD("calling LISTEN"); + args[0] = fd; + args[1] = -1; + set_fs(KERNEL_DS); + sys_socketcall(SYS_LISTEN, args); + set_fs(old_fs); + SOLD("LISTEN done"); + } + if (!(filp->f_flags & O_NONBLOCK)) { + poll_table wait_table, *wait; + struct poll_table_entry *entry; + SOLD("getting poll_table"); + entry = (struct poll_table_entry *)__get_free_page(GFP_KERNEL); + if (!entry) + return -ENOMEM; + SOLD("got one"); + wait_table.nr = 0; + wait_table.entry = entry; + wait = &wait_table; + for(;;) { + SOLD("loop"); + current->state = TASK_INTERRUPTIBLE; + /* ! ( l<0 || ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ + /* ( ! l<0 && ! ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ + /* ( l>=0 && ( ! l>=0 || ! ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ + /* ( l>=0 && ( l<0 || ( pfirst && ! (flags == HIPRI && pri != HIPRI) ) ) ) */ + /* ( l>=0 && ( l<0 || ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) ) */ + /* ( l>=0 && ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) */ + if (ctl_maxlen >= 0 && sock->pfirst && (*flags_p != MSG_HIPRI || sock->pfirst->pri == MSG_HIPRI)) + break; + SOLD("cond 1 passed"); + if ( + #if 1 + *flags_p != MSG_HIPRI && + #endif + ((filp->f_op->poll(filp, wait) & POLLIN) || + (filp->f_op->poll(filp, NULL) & POLLIN) || + signal_pending(current)) + ) { + break; + } + if( *flags_p == MSG_HIPRI ) { + SOLD("avoiding lockup"); + break ; + } + SOLD("scheduling"); + schedule(); + } + SOLD("loop done"); + current->state = TASK_RUNNING; + free_wait(&wait_table); + free_page((unsigned long)entry); + if (signal_pending(current)) { + SOLD("signal pending"); + return -EINTR; + } + } + if (ctl_maxlen >= 0 && sock->pfirst) { + struct T_primsg *it = sock->pfirst; +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + int l = min(ctl_maxlen, it->length); + CHECK_MAGIC((char*)((u64)(((char *)&it->type)+sock->offset+it->length+7)&~7),MKCTL_MAGIC); + SOLD("purting ctl data"); + if(copy_to_user(ctl_buf, + (char*)&it->type + sock->offset, l)) + return -EFAULT; + SOLD("pur it"); + if(put_user(l, ctl_len)) + return -EFAULT; + SOLD("set ctl_len"); + *flags_p = it->pri; + it->length -= l; + if (it->length) { + SOLD("more ctl"); + sock->offset += l; + return MORECTL; + } else { + SOLD("removing message"); + sock->pfirst = it->next; + if (!sock->pfirst) + sock->plast = NULL; + SOLDD(("getmsg kfree %016lx->%016lx\n", it, sock->pfirst)); + mykfree(it); + sock->offset = 0; + SOLD("ctl done"); + return 0; + } + } + *flags_p = 0; + if (ctl_maxlen >= 0) { + SOLD("ACCEPT perhaps?"); + if (ino->u.socket_i.type == SOCK_STREAM && sock->state == TS_IDLE) { + struct T_conn_ind ind; + char *buf = getpage(); + int len = BUF_SIZE; + + SOLD("trying ACCEPT"); + if (put_user(ctl_maxlen - sizeof(ind), ctl_len)) + return -EFAULT; + args[0] = fd; + args[1] = (long)buf; + args[2] = (long)&len; + oldflags = filp->f_flags; + filp->f_flags |= O_NONBLOCK; + SOLD("calling ACCEPT"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_ACCEPT, args); + set_fs(old_fs); + filp->f_flags = oldflags; + if (error < 0) { + SOLD("some error"); + putpage(buf); + return error; + } + if (error) { + SOLD("connect"); + putpage(buf); + if (sizeof(ind) > ctl_maxlen) { + SOLD("generating CONN_IND"); + ind.PRIM_type = T_CONN_IND; + ind.SRC_length = len; + ind.SRC_offset = sizeof(ind); + ind.OPT_length = ind.OPT_offset = 0; + ind.SEQ_number = error; + if(copy_to_user(ctl_buf, &ind, sizeof(ind))|| + put_user(sizeof(ind)+ind.SRC_length,ctl_len)) + return -EFAULT; + SOLD("CONN_IND created"); + } + if (data_maxlen >= 0) + put_user(0, data_len); + SOLD("CONN_IND done"); + return 0; + } + if (len>ctl_maxlen) { + SOLD("data don't fit"); + putpage(buf); + return -EFAULT; /* XXX - is this ok ? */ + } + if(copy_to_user(ctl_buf,buf,len) || put_user(len,ctl_len)){ + SOLD("can't copy data"); + putpage(buf); + return -EFAULT; + } + SOLD("ACCEPT done"); + putpage(buf); + } + } + SOLD("checking data req"); + if (data_maxlen <= 0) { + if (data_maxlen == 0) + put_user(0, data_len); + if (ctl_maxlen >= 0) + put_user(0, ctl_len); + return -EAGAIN; + } + SOLD("wants data"); + if (ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) { + SOLD("udi fits"); + tmpbuf = ctl_buf + sizeof(udi); + tmplen = ctl_maxlen - sizeof(udi); + } else { + SOLD("udi does not fit"); + tmpbuf = NULL; + tmplen = 0; + } + if (put_user(tmplen, ctl_len)) + return -EFAULT; + SOLD("set ctl_len"); + oldflags = filp->f_flags; + filp->f_flags |= O_NONBLOCK; + SOLD("calling recvfrom"); + sys_recvfrom = (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(recvfrom); + error = sys_recvfrom(fd, data_buf, min(0,data_maxlen), 0, (struct sockaddr*)tmpbuf, ctl_len); + filp->f_flags = oldflags; + if (error < 0) + return error; + SOLD("error >= 0" ) ; + if (error && ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) { + SOLD("generating udi"); + udi.PRIM_type = T_UNITDATA_IND; + get_user(udi.SRC_length, ctl_len); + udi.SRC_offset = sizeof(udi); + udi.OPT_length = udi.OPT_offset = 0; + copy_to_user(ctl_buf, &udi, sizeof(udi)); + put_user(sizeof(udi)+udi.SRC_length, ctl_len); + SOLD("udi done"); + } else + put_user(0, ctl_len); + put_user(error, data_len); + SOLD("done"); + return 0; +} + +asmlinkage int solaris_getmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3) +{ + struct file *filp; + struct inode *ino; + struct strbuf *ctlptr, *datptr; + struct strbuf ctl, dat; + int *flgptr; + int flags; + int error = -EBADF; + + SOLD("entry"); + lock_kernel(); + if(fd >= NR_OPEN) goto out; + + filp = current->files->fd[fd]; + if(!filp) goto out; + + ino = filp->f_dentry->d_inode; + if (!ino) goto out; + + if (!ino->i_sock) + goto out; + + ctlptr = (struct strbuf *)A(arg1); + datptr = (struct strbuf *)A(arg2); + flgptr = (int *)A(arg3); + + error = -EFAULT; + + if (ctlptr) { + if (copy_from_user(&ctl,ctlptr,sizeof(struct strbuf)) || + put_user(-1,&ctlptr->len)) + goto out; + } else + ctl.maxlen = -1; + + if (datptr) { + if (copy_from_user(&dat,datptr,sizeof(struct strbuf)) || + put_user(-1,&datptr->len)) + goto out; + } else + dat.maxlen = -1; + + if (get_user(flags,flgptr)) + goto out; + + switch (flags) { + case 0: + case MSG_HIPRI: + case MSG_ANY: + case MSG_BAND: + break; + default: + error = -EINVAL; + goto out; + } + + error = timod_getmsg(fd,(char*)A(ctl.buf),ctl.maxlen,&ctlptr->len, + (char*)A(dat.buf),dat.maxlen,&datptr->len,&flags); + + if (!error && put_user(flags,flgptr)) + error = -EFAULT; +out: + unlock_kernel(); + SOLD("done"); + return error; +} + +asmlinkage int solaris_putmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3) +{ + struct file *filp; + struct inode *ino; + struct strbuf *ctlptr, *datptr; + struct strbuf ctl, dat; + int flags = (int) arg3; + int error = -EBADF; + + SOLD("entry"); + lock_kernel(); + if(fd >= NR_OPEN) goto out; + + filp = current->files->fd[fd]; + if(!filp) goto out; + + ino = filp->f_dentry->d_inode; + if (!ino) goto out; + + if (!ino->i_sock && + (MAJOR(ino->i_rdev) != 30 || MINOR(ino->i_rdev) != 1)) + goto out; + + ctlptr = (struct strbuf *)A(arg1); + datptr = (struct strbuf *)A(arg2); + + error = -EFAULT; + + if (ctlptr) { + if (copy_from_user(&ctl,ctlptr,sizeof(ctl))) + goto out; + if (ctl.len < 0 && flags) { + error = -EINVAL; + goto out; + } + } else { + ctl.len = 0; + ctl.buf = 0; + } + + if (datptr) { + if (copy_from_user(&dat,datptr,sizeof(dat))) + goto out; + } else { + dat.len = 0; + dat.buf = 0; + } + + error = timod_putmsg(fd,(char*)A(ctl.buf),ctl.len, + (char*)A(dat.buf),dat.len,flags); +out: + unlock_kernel(); + SOLD("done"); + return error; +} diff --git a/arch/sparc64/vmlinux.lds b/arch/sparc64/vmlinux.lds index 661acc098..d2c08f0fe 100644 --- a/arch/sparc64/vmlinux.lds +++ b/arch/sparc64/vmlinux.lds @@ -52,12 +52,9 @@ SECTIONS . += 8192; empty_bad_pte_table = .; . += 8192; - empty_bad_page = .; - . += 8192; . += 0x40; empty_null_pmd_table = .; . += 8192; - . += 0x40; empty_null_pte_table = .; . += 8192; } |