diff -uNr --exclude=CVS linux-2.4.8-ac9/Documentation/Configure.help linuxppc64_2_4/Documentation/Configure.help --- linux-2.4.8-ac9/Documentation/Configure.help Thu Aug 23 09:17:13 2001 +++ linuxppc64_2_4/Documentation/Configure.help Thu Aug 23 10:25:17 2001 @@ -179,6 +179,14 @@ from Motorola. The Linux PowerPC port has a home page at . +PowerPC64 processor +CONFIG_PPC64 + The PowerPC architecture was designed for both 32 bit and 64 bit + processor implementations. 64 bit PowerPC processors are in many + ways a superset of their 32 bit PowerPC cousins. Each 64 bit PowerPC + processor also has a 32 bit mode to allow for 32 bit compatibility. + The home of the PowerPC 64 Linux project is at + Motorola 68K processors CONFIG_M68K The Motorola 68K microprocessors are now obsolete, having been @@ -19876,30 +19884,70 @@ machines: Apple Power Macintoshes and clones (such as the Motorola Starmax series), PReP (PowerPC Reference Platform) machines such as the Motorola PowerStack, CHRP (Common Hardware Reference Platform), - the embedded MBX boards from Motorola and many others. Currently, + the embedded MBX boards from Motorola and many others. Currently, the default option is to build a kernel which works on the first three. Support for other machines is currently incomplete. Select PowerMac/PReP/MTX/CHRP if configuring for any of the above. Select Gemini if configuring for a Synergy Microsystems' Gemini - series Single Board Computer. More information is available at: - . + series Single Board Computer. More information is available at: + + + Select APUS if configuring for a PowerUP Amiga. More information is + available at: + +# Choice: i or p +Platform support +CONFIG_PPC_ISERIES + Linux runs on certain models of the IBM AS/400, now known as the + IBM iSeries. Generally if you can run LPAR (Logical Partitioning) + on your iSeries you can run Linux in a partition on your machine. + + Linux also runs on most models of IBM pSeries hardware. (pSeries + used to be known as the RS/6000) + + If you have an iSeries and want to run Linux in a partition, + select the iSeries option to build your kernel. + + If you have a pSeries and want to run Linux, select pSeries + as the option to build your kernel. + + See for exact model information to see what + can run the 64 bit PowerPC kernel. - Select APUS if configuring for a PowerUP Amiga. More information is - available at: . + iSeries Linux information from IBM can be found at: + + + pSeries Linux information from IBM can be found at: + + + Project information can be found at: + + + +Platform support +CONFIG_PPC_PSERIES + Linux runs on most models of IBM pSeries hardware. (pSeries used + to be known as the RS/6000) + + See for exact model information for the + 64 bit PowerPC kernel. + + pSeries Linux information from IBM can be found at: + Synergy-Gemini CONFIG_GEMINI Select Gemini if configuring for a Synergy Microsystems' Gemini series Single Board Computer. More information is available at: - . + Amiga-Apus CONFIG_APUS Select APUS if configuring for a PowerUP Amiga. More information is available at: - . + Power management support for PowerBooks CONFIG_PMAC_PBOOK @@ -20095,26 +20143,33 @@ Support for EST8260 CONFIG_EST8260 The EST8260 is a single-board computer manufactured by Wind River - Systems, Inc. (formerly Embedded Support Tools Corp.) and based on - the MPC8260. Wind River Systems has a website at - , but the EST8260 cannot be found on it + Systems Inc. (formerly Embedded Support Tools Corp.) and based + on the MPC8260. Wind River Systems has a website at + , but the EST8260 cannot be found on it and has probably been discontinued or rebadged. +Support for Large Memory +CONFIG_MSCHUNKS + MsChunks stands for Main Store Chunks and specifically allows the + 64 bit PowerPC Linux kernel to optimize for machines with sparce + discontiguous memory. iSeries kernels need to have this on. + It is recommended that for pSeries hardware that you answer Y. + AltiVec support CONFIG_ALTIVEC Say Y here to compile in support for Motorola AltiVec boards. The - AltiVec board is baced on the MPC7400 embedded version of the - PowerPC and adds a SIMD vector-processing unit. Product information + AltiVec board is baced on the MPC7400 embedded version of the + PowerPC and adds a SIMD vector-processing unit. Product information at . ADB raw keycode support CONFIG_MAC_ADBKEYCODES This provides support for sending raw ADB keycodes to console devices. This is the default up to 2.4.0, but in future this may be - phased out in favor of generic Linux keycodes. If you say Y here, + phased out in favor of generic Linux keycodes. If you say Y here, you can dynamically switch via the /proc/sys/dev/mac_hid/keyboard_sends_linux_keycodes - sysctl and with the "keyboard_sends_linux_keycodes=" kernel + sysctl and with the "keyboard_sends_linux_keycodes=" kernel argument. If unsure, say Y here. @@ -20122,8 +20177,8 @@ Mouse button 2+3 emulation support CONFIG_MAC_EMUMOUSEBTN This provides generic support for emulating the 2nd and 3rd mouse - button with keypresses. If you say Y here, the emulation is still - disabled by default. The emulation is controlled by these sysctl + button with keypresses. If you say Y here, the emulation is still + disabled by default. The emulation is controlled by these sysctl entries: /proc/sys/dev/mac_hid/mouse_button_emulation /proc/sys/dev/mac_hid/mouse_button2_keycode @@ -22836,6 +22891,11 @@ CONFIG_XMON Include in-kernel hooks for the xmon kernel monitor/debugger supported by the PPC port. + +Include realtime debugging +CONFIG_PPCDBG + Include in-kernel PowerPC 64 information hooks that may be turned on/off + in real time. Include kgdb kernel debugger CONFIG_KWDB diff -uNr --exclude=CVS linux-2.4.8-ac9/MAINTAINERS linuxppc64_2_4/MAINTAINERS --- linux-2.4.8-ac9/MAINTAINERS Thu Aug 23 09:17:14 2001 +++ linuxppc64_2_4/MAINTAINERS Thu Aug 23 10:25:17 2001 @@ -863,14 +863,10 @@ S: Maintained LINUX FOR 64BIT POWERPC -P: Tom Gall -M: tom_gall@vnet.ibm.com P: David Engebretsen M: engebret@us.ibm.com -P: Dwayne McConnell -M: dwayne@austin.ibm.com W: http://linuxppc64.org -L: linuxppc64-dev@lists.linuxppc.porg +L: linuxppc64-dev@lists.linuxppc.org S: Supported LOGICAL VOLUME MANAGER diff -uNr --exclude=CVS linux-2.4.8-ac9/Makefile linuxppc64_2_4/Makefile --- linux-2.4.8-ac9/Makefile Thu Aug 23 09:17:14 2001 +++ linuxppc64_2_4/Makefile Thu Aug 23 10:25:17 2001 @@ -10,11 +10,13 @@ # SUBARCH tells the usermode build what the underlying arch is. That is set # first, and if a usermode build is happening, the "ARCH=um" on the command # line overrides the setting of ARCH below. If a native build is happening, -# then ARCH is assigned, getting whatever value it gets normally, and +# then ARCH is assigned, getting whatever value it gets normally, and # SUBARCH is subsequently ignored. SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) -ARCH := $(SUBARCH) +#ARCH := $(SUBARCH) + +ARCH := ppc64 CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ @@ -27,7 +29,7 @@ HOSTCC = gcc HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -CROSS_COMPILE = +CROSS_COMPILE = /usr/local/ppc64-current3.0/bin/powerpc64-linux- # # Include the make variables (CC, etc...) @@ -97,7 +99,7 @@ CPPFLAGS := -D__KERNEL__ -I$(HPATH) CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ - -fomit-frame-pointer -fno-strict-aliasing -fno-common + -fomit-frame-pointer -fno-strict-aliasing -fno-common AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) # diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/Makefile linuxppc64_2_4/arch/ppc64/Makefile --- linux-2.4.8-ac9/arch/ppc64/Makefile Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/Makefile Thu Aug 23 16:45:14 2001 @@ -19,13 +19,19 @@ CHECKS = checks endif +# Re: -DPPC64_32B_ADDR_SPACE in CPPLAGS/CFLAGS below. +# For 64-bit apps, temporarily reduce the size of the address space +# available to user application. This allow us to use strace without +# having to compile a strace64 program. This shouldn't affect anyone +# other than Steve Munroe, Peter Bergner. I will back this hack out +# later... -Peter + ASFLAGS = LINKFLAGS = -T arch/ppc64/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic -CPPFLAGS := $(CPPFLAGS) -D__powerpc__ -include $(TOPDIR)/arch/ppc64/mymacros.h -CFLAGS := $(CFLAGS) -D__linux__ -D__powerpc__ -fsigned-char -Wa,-Saix \ +CPPFLAGS := $(CPPFLAGS) -D__powerpc__ -DPPC64_32B_ADDR_SPACE +CFLAGS := $(CFLAGS) -D__linux__ -D__powerpc__ -fsigned-char \ -msoft-float -pipe -Wno-uninitialized $(PRINTK) \ - -include $(TOPDIR)/arch/ppc64/mymacros.h -mminimal-toc \ - -fno-builtin + -mminimal-toc -fno-builtin -DPPC64_32B_ADDR_SPACE CPP = $(CC) -E $(CFLAGS) @@ -52,8 +58,7 @@ BOOT_TARGETS = zImage znetboot.initrd zImage.initrd -ifdef CONFIG_POWER3 -ifndef CONFIG_8260 +ifdef CONFIG_PPC_PSERIES $(BOOT_TARGETS): $(CHECKS) vmlinux @$(MAKEBOOT) $@ @@ -62,23 +67,9 @@ ifdef CONFIG_SMP cp -f vmlinux /tftpboot/vmlinux.smp else - cp -f vmlinux /tftpboot/vmlinux -endif -endif - @$(MAKEBOOT) $@ -else -# 8260 is custom 6xx -$(BOOT_TARGETS): $(CHECKS) vmlinux - @$(MAKEMBXBOOT) $@ + cp -f vmlinux /tftpboot/vmlinux.smp.64 endif endif - -ifdef CONFIG_PPC64BRIDGE -$(BOOT_TARGETS): $(CHECKS) vmlinux - @$(MAKEBOOT) $@ - -znetboot: $(CHECKS) vmlinux - cp -f vmlinux /tftpboot/vmlinux.64 @$(MAKEBOOT) $@ endif diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/boot/Makefile linuxppc64_2_4/arch/ppc64/boot/Makefile --- linux-2.4.8-ac9/arch/ppc64/boot/Makefile Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/boot/Makefile Thu Aug 16 14:57:25 2001 @@ -18,7 +18,7 @@ # compile for 32bit mode. BOOTCC = $(HOSTCC) -BOOTCFLAGS = $(HOSTCFLAGS) +BOOTCFLAGS = $(HOSTCFLAGS) -I$(HPATH) BOOTLD = ld BOOTAS = as BOOTAFLAGS = -D__ASSEMBLY__ $(HOSTCFLAGS) diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/config.in linuxppc64_2_4/arch/ppc64/config.in --- linux-2.4.8-ac9/arch/ppc64/config.in Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/config.in Thu Aug 23 19:48:56 2001 @@ -9,59 +9,23 @@ mainmenu_name "64 bit PowerPC Linux Kernel Configuration" mainmenu_option next_comment -comment 'Code maturity level options' -bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL + comment 'Code maturity level options' + bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL endmenu mainmenu_option next_comment comment 'Platform support' define_bool CONFIG_PPC y define_bool CONFIG_PPC64 y -choice 'Processor Type' \ - "IStar CONFIG_ISTAR \ - iSeries CONFIG_PPC_ISERIES \ - POWER3 CONFIG_POWER3 \ - POWER4 CONFIG_POWER4 " POWER3 +define_bool CONFIG_ALL_PPC y +define_bool CONFIG_SERIAL_CONSOLE y - -if [ "$CONFIG_ISTAR" = "y" -o "$CONFIG_POWER4" = "y" ]; then - define_bool CONFIG_SERIAL_CONSOLE y -fi - - -if [ "$CONFIG_8xx" = "y" ]; then - define_bool CONFIG_SERIAL_CONSOLE y - - choice 'Machine Type' \ - "RPX-Lite CONFIG_RPXLITE \ - RPX-Classic CONFIG_RPXCLASSIC \ - BSE-IP CONFIG_BSEIP \ - MBX CONFIG_MBX \ - WinCept CONFIG_WINCEPT" RPX-Lite -fi -if [ "$CONFIG_6xx" = "y" ]; then - choice 'Machine Type' \ - "PowerMac/PReP/MTX/CHRP CONFIG_ALL_PPC \ - Gemini CONFIG_GEMINI \ - EST8260 CONFIG_EST8260 \ - APUS CONFIG_APUS" PowerMac/PReP/MTX/CHRP -fi - -if [ "$CONFIG_POWER3" = "y" ]; then - define_bool CONFIG_ALL_PPC y -fi - -if [ "$CONFIG_POWER4" = "y" ]; then - define_bool CONFIG_ALL_PPC y -fi - -if [ "$CONFIG_ISTAR" = "y" ]; then - define_bool CONFIG_ALL_PPC y -fi +choice 'Machine Type' \ + "pSeries CONFIG_PPC_PSERIES \ + iSeries CONFIG_PPC_ISERIES" CONFIG_PPC_PSERIES if [ "$CONFIG_PPC_ISERIES" = "y" ]; then - define_bool CONFIG_ALL_PPC y - define_bool CONFIG_MSCHUNKS y + define_bool CONFIG_MSCHUNKS y fi bool 'Symmetric multi-processing support' CONFIG_SMP @@ -83,11 +47,9 @@ define_bool CONFIG_ISA n define_bool CONFIG_SBUS n - -#Too many -o's for menuconfig -#if [ "$CONFIG_POWER3" = "y" -o "$CONFIG_POWER4" = "y" -o "$CONFIG_ISTAR" ="y"]; then - define_bool CONFIG_PCI y -#fi +define_bool CONFIG_MCA n +define_bool CONFIG_EISA n +define_bool CONFIG_PCI y bool 'Networking support' CONFIG_NET bool 'Sysctl support' CONFIG_SYSCTL @@ -99,17 +61,9 @@ define_bool CONFIG_KCORE_ELF y fi -#Commented By Adam. We must ask the question. Interactivity is the key :-) -#define_bool CONFIG_BINFMT_ELF y -#define_bool CONFIG_KERNEL_ELF y //WHAT IS THIS???// - bool 'Kernel Support for 64 bit ELF binaries' CONFIG_BINFMT_ELF - -#Too many -o 's for menuconfig -#if [ "$CONFIG_POWER3" = "y" -o "$CONFIG_POWER4" = "y"-o"$CONFIG_ISTAR"="y"]; then tristate 'Kernel support for 32 bit binaries' CONFIG_BINFMT_ELF32 -#fi tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC @@ -125,24 +79,11 @@ source drivers/parport/Config.in - if [ "$CONFIG_PPC_ISERIES" != "y" ]; then - if [ "$CONFIG_4xx" != "y" -a "$CONFIG_8xx" != "y" ]; then - bool 'Support for VGA Console' CONFIG_VGA_CONSOLE - bool 'Support for frame buffer devices' CONFIG_FB - if [ "$CONFIG_FB" = "y" ]; then - bool 'Backward compatibility mode for Xpmac' CONFIG_FB_COMPAT_XPMAC - fi - - bool 'Power management support for PowerBooks' CONFIG_PMAC_PBOOK - tristate 'Support for PowerMac serial ports' CONFIG_MAC_SERIAL - if [ "$CONFIG_MAC_SERIAL" = "y" ]; then - bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE - fi - bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE - bool 'Support for early boot text console (BootX only)' CONFIG_BOOTX_TEXT - bool 'Support for Motorola Hot Swap' CONFIG_MOTOROLA_HOTSWAP - fi + bool 'Support for VGA Console' CONFIG_VGA_CONSOLE + bool 'Support for frame buffer devices' CONFIG_FB + + bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE fi endmenu @@ -153,44 +94,34 @@ source drivers/md/Config.in if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in + source net/Config.in fi -mainmenu_option next_comment -comment 'ATA/IDE/MFM/RLL support' - -tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE - -if [ "$CONFIG_IDE" != "n" ]; then - source drivers/ide/Config.in -else - define_bool CONFIG_BLK_DEV_IDE_MODES n - define_bool CONFIG_BLK_DEV_HD n -fi -endmenu +define_bool CONFIG_BLK_DEV_IDE_MODES n +define_bool CONFIG_BLK_DEV_HD n mainmenu_option next_comment comment 'SCSI support' tristate 'SCSI support' CONFIG_SCSI if [ "$CONFIG_SCSI" != "n" ]; then - source drivers/scsi/Config.in + source drivers/scsi/Config.in fi endmenu source drivers/ieee1394/Config.in if [ "$CONFIG_NET" = "y" ]; then - mainmenu_option next_comment - comment 'Network device support' + mainmenu_option next_comment + comment 'Network device support' - bool 'Network device support' CONFIG_NETDEVICES - if [ "$CONFIG_NETDEVICES" = "y" ]; then - source drivers/net/Config.in - if [ "$CONFIG_ATM" = "y" ]; then - source drivers/atm/Config.in - fi - fi - endmenu + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + source drivers/net/Config.in + if [ "$CONFIG_ATM" = "y" ]; then + source drivers/atm/Config.in + fi + fi + endmenu fi source net/ax25/Config.in @@ -202,7 +133,7 @@ tristate 'ISDN support' CONFIG_ISDN if [ "$CONFIG_ISDN" != "n" ]; then - source drivers/isdn/Config.in + source drivers/isdn/Config.in fi endmenu @@ -211,7 +142,7 @@ bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then - source drivers/cdrom/Config.in + source drivers/cdrom/Config.in fi endmenu @@ -227,16 +158,12 @@ comment 'Sound' tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/dmasound/Config.in - source drivers/sound/Config.in + source drivers/sound/dmasound/Config.in + source drivers/sound/Config.in fi endmenu -if [ "$CONFIG_8260" = "y" ]; then -source arch/ppc/8260_io/Config.in -fi - source drivers/usb/Config.in mainmenu_option next_comment @@ -245,10 +172,10 @@ bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ bool 'Include kgdb kernel debugger' CONFIG_KGDB bool 'Include xmon kernel debugger' CONFIG_XMON -bool 'Include kdb kernel debugger' CONFIG_KDB -if [ "$CONFIG_KDB" = "y" ]; then - bool ' KDB off by default' CONFIG_KDB_OFF - define_bool CONFIG_KALLSYMS y -fi +#bool 'Include kdb kernel debugger' CONFIG_KDB +#if [ "$CONFIG_KDB" = "y" ]; then +# bool ' KDB off by default' CONFIG_KDB_OFF +# define_bool CONFIG_KALLSYMS y +#fi bool 'Include PPCDBG realtime debugging' CONFIG_PPCDBG endmenu diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/defconfig linuxppc64_2_4/arch/ppc64/defconfig --- linux-2.4.8-ac9/arch/ppc64/defconfig Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/defconfig Mon Aug 20 14:07:04 2001 @@ -1,5 +1,5 @@ # -# Automatically generated make config: don't edit +# Automatically generated by make menuconfig: don't edit # # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set @@ -15,13 +15,12 @@ # CONFIG_PPC=y CONFIG_PPC64=y -# CONFIG_ISTAR is not set -# CONFIG_PPC_ISERIES is not set -CONFIG_POWER3=y -# CONFIG_POWER4 is not set CONFIG_ALL_PPC=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_PPC_ISERIES is not set +CONFIG_PPC_PSERIES=y CONFIG_SMP=y -# CONFIG_MSCHUNKS is not set +CONFIG_MSCHUNKS=y # # Loadable module support @@ -33,6 +32,8 @@ # # CONFIG_ISA is not set # CONFIG_SBUS is not set +# CONFIG_MCA is not set +# CONFIG_EISA is not set CONFIG_PCI=y CONFIG_NET=y CONFIG_SYSCTL=y @@ -50,13 +51,9 @@ # Parallel port support # # CONFIG_PARPORT is not set -CONFIG_VGA_CONSOLE=y +# CONFIG_VGA_CONSOLE is not set CONFIG_FB=y -# CONFIG_FB_COMPAT_XPMAC is not set -# CONFIG_PMAC_PBOOK is not set -# CONFIG_MAC_SERIAL is not set -CONFIG_PROC_DEVICETREE=y -# CONFIG_BOOTX_TEXT is not set +# CONFIG_PROC_DEVICETREE is not set # CONFIG_MOTOROLA_HOTSWAP is not set # @@ -75,7 +72,6 @@ # Block devices # CONFIG_BLK_DEV_FD=y -# CONFIG_VIODASD is not set # CONFIG_BLK_DEV_XD is not set # CONFIG_PARIDE is not set # CONFIG_BLK_CPQ_DA is not set @@ -121,10 +117,6 @@ # CONFIG_IPV6 is not set # CONFIG_KHTTPD is not set # CONFIG_ATM is not set - -# -# -# # CONFIG_IPX is not set # CONFIG_ATALK is not set # CONFIG_DECNET is not set @@ -154,10 +146,6 @@ # SCSI support # CONFIG_SCSI=y - -# -# SCSI support type (disk, tape, CD-ROM) -# CONFIG_BLK_DEV_SD=y CONFIG_SD_EXTRA_DEVS=40 CONFIG_CHR_DEV_ST=y @@ -166,10 +154,6 @@ CONFIG_BLK_DEV_SR_VENDOR=y CONFIG_SR_EXTRA_DEVS=2 CONFIG_CHR_DEV_SG=y - -# -# Some SCSI devices (e.g. CD jukebox) support multiple LUNs -# # CONFIG_SCSI_DEBUG_QUEUES is not set # CONFIG_SCSI_MULTI_LUN is not set # CONFIG_SCSI_CONSTANTS is not set @@ -186,6 +170,7 @@ # CONFIG_SCSI_AHA1740 is not set # CONFIG_SCSI_AIC7XXX is not set # CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_DPT_I2O is not set # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set @@ -414,7 +399,6 @@ # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 -# CONFIG_VIOCONS is not set # # I2C support @@ -441,10 +425,6 @@ # CONFIG_INPUT_EMU10K1 is not set # CONFIG_INPUT_SERIO is not set # CONFIG_INPUT_SERPORT is not set - -# -# Joysticks -# # CONFIG_INPUT_ANALOG is not set # CONFIG_INPUT_A3D is not set # CONFIG_INPUT_ADI is not set @@ -540,6 +520,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y CONFIG_NFS_V3=y # CONFIG_ROOT_NFS is not set @@ -618,17 +599,9 @@ # USB support # # CONFIG_USB is not set - -# -# USB Controllers -# # CONFIG_USB_UHCI is not set # CONFIG_USB_UHCI_ALT is not set # CONFIG_USB_OHCI is not set - -# -# USB Device Class drivers -# # CONFIG_USB_AUDIO is not set # CONFIG_USB_BLUETOOTH is not set # CONFIG_USB_STORAGE is not set @@ -638,48 +611,28 @@ # CONFIG_USB_STORAGE_SDDR09 is not set # CONFIG_USB_ACM is not set # CONFIG_USB_PRINTER is not set - -# -# USB Human Interface Devices (HID) -# # CONFIG_USB_HID is not set # CONFIG_USB_HIDDEV is not set # CONFIG_USB_KBD is not set # CONFIG_USB_MOUSE is not set # CONFIG_USB_WACOM is not set - -# -# USB Imaging devices -# # CONFIG_USB_DC2XX is not set # CONFIG_USB_MDC800 is not set # CONFIG_USB_SCANNER is not set # CONFIG_USB_MICROTEK is not set # CONFIG_USB_HP5300 is not set - -# -# USB Multimedia devices -# # CONFIG_USB_IBMCAM is not set # CONFIG_USB_OV511 is not set # CONFIG_USB_PWC is not set # CONFIG_USB_SE401 is not set # CONFIG_USB_DSBR is not set # CONFIG_USB_DABUSB is not set - -# -# USB Network adaptors -# # CONFIG_USB_PLUSB is not set # CONFIG_USB_PEGASUS is not set # CONFIG_USB_KAWETH is not set # CONFIG_USB_CATC is not set # CONFIG_USB_CDCETHER is not set # CONFIG_USB_USBNET is not set - -# -# USB port drivers -# # CONFIG_USB_USS720 is not set # @@ -707,10 +660,6 @@ # CONFIG_USB_SERIAL_PL2303 is not set # CONFIG_USB_SERIAL_CYBERJACK is not set # CONFIG_USB_SERIAL_OMNINET is not set - -# -# Miscellaneous USB drivers -# # CONFIG_USB_RIO500 is not set # @@ -719,5 +668,4 @@ CONFIG_MAGIC_SYSRQ=y # CONFIG_KGDB is not set CONFIG_XMON=y -# CONFIG_KDB is not set CONFIG_PPCDBG=y diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/HvLpConfig.c linuxppc64_2_4/arch/ppc64/kernel/HvLpConfig.c --- linux-2.4.8-ac9/arch/ppc64/kernel/HvLpConfig.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/HvLpConfig.c Tue Aug 21 22:09:44 2001 @@ -0,0 +1,63 @@ +/* + * HvLpConfig.c + * Copyright (C) 2001 Kyle A. Lucke, IBM Corporation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _HVLPCONFIG_H +#include +#endif + +HvLpIndex HvLpConfig_getLpIndex_outline(void) +{ + return HvLpConfig_getLpIndex(); +} + +const HvLpIndexMap HvLpIndexMapDefault = (1 << (sizeof(HvLpIndexMap) * 8 - 1)); +const HvLpIndex HvHardcodedPrimaryLpIndex = 0; +const HvLpIndex HvMaxArchitectedLps = HVMAXARCHITECTEDLPS; +const HvLpVirtualLanIndex HvMaxArchitectedVirtualLans = 16; +const HvLpSharedPoolIndex HvMaxArchitectedSharedPools = 16; +const HvLpSharedPoolIndex HvMaxSupportedSharedPools = 1; +const HvLpIndex HvMaxRuntimeLpsPreCondor = 12; +const HvLpIndex HvMaxRuntimeLps = HVMAXARCHITECTEDLPS; +const HvLpIndex HvLpIndexInvalid = 0xff; +const u16 HvInvalidProcIndex = 0xffff; +const u32 HvVirtualFlashSize = 0x200; +const u32 HvMaxBusesPreCondor = 32; +const u32 HvMaxBusesCondor = 256; +const u32 HvMaxArchitectedBuses = 512; +const HvLpBus HvCspBusNumber = 1; +const u32 HvMaxSanHwSets = 16; +const HvLpCard HvMaxSystemIops = 200; +const HvLpCard HvMaxBusIops = 20; +const u16 HvMaxUnitsPerIop = 100; +const u64 HvPageSize = 4 * 1024; +const u64 HvChunkSize = HVCHUNKSIZE; +const u64 HvChunksPerMeg = HVCHUNKSPERMEG; +const u64 HvPagesPerChunk = HVPAGESPERCHUNK; +const u64 HvPagesPerMeg = HVPAGESPERMEG; +const u64 HvLpMinMegsPrimary = HVLPMINMEGSPRIMARY; +const u64 HvLpMinMegsSecondary = HVLPMINMEGSSECONDARY; +const u64 HvLpMinChunksPrimary = HVLPMINMEGSPRIMARY * HVCHUNKSPERMEG; +const u64 HvLpMinChunksSecondary = HVLPMINMEGSSECONDARY * HVCHUNKSPERMEG; +const u64 HvLpMinPagesPrimary = HVLPMINMEGSPRIMARY * HVPAGESPERMEG; +const u64 HvLpMinPagesSecondary = HVLPMINMEGSSECONDARY * HVPAGESPERMEG; +const u8 HvLpMinProcs = 1; +const u8 HvLpConfigMinInteract = 1; +const u16 HvLpMinSharedProcUnitsX100 = 10; +const u16 HvLpMaxSharedProcUnitsX100 = 100; +const HvLpSharedPoolIndex HvLpSharedPoolIndexInvalid = 0xff; diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/Makefile linuxppc64_2_4/arch/ppc64/kernel/Makefile --- linux-2.4.8-ac9/arch/ppc64/kernel/Makefile Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/Makefile Tue Aug 14 23:13:12 2001 @@ -26,12 +26,12 @@ ioctl32.o ptrace32.o signal32.o open_pic.o xics.o \ pmc.o mf_proc.o proc_pmc.o iSeries_setup.o \ ItLpQueue.o hvCall.o mf.o viopath.o HvLpEvent.o \ - iSeries_proc.o HvCall.o flight_recorder.o + iSeries_proc.o HvCall.o flight_recorder.o HvLpConfig.o obj-$(CONFIG_PCI) += pci.o ifeq ($(CONFIG_PPC_ISERIES),y) -obj-$(CONFIG_PCI) += iSeries_dma.o iSeries_rtc.o +obj-$(CONFIG_PCI) += iSeries_dma.o iSeries_rtc.o proc_pcifr.o else obj-$(CONFIG_PCI) += pci_dma.o proc_pcifr.o endif diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/entry.S linuxppc64_2_4/arch/ppc64/kernel/entry.S --- linux-2.4.8-ac9/arch/ppc64/kernel/entry.S Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/entry.S Fri Aug 24 07:02:12 2001 @@ -306,10 +306,10 @@ mtmsrd r6 /* Update machine state */ ld r6,_MSR(r1) - mtspr SRR0,r7 - mtspr SRR1,r6 ld r2,GPR2(r1) ld r1,GPR1(r1) + mtspr SRR0,r7 + mtspr SRR1,r6 /* sync required to force memory operations on this processor */ /* to complete before the current thread gives up control. */ @@ -334,33 +334,20 @@ ld r5,_MSR(r1) andi. r5,r5,MSR_EE beq 2f - -_GLOBAL(lost_irq_ret) -3: LOADBASE(r4,ppc_n_lost_interrupts) - lwz r4,ppc_n_lost_interrupts@l(r4) - cmpi 0,r4,0 - bne- 1f /* do_IRQ if lost interrupts */ +irq_recheck: + mfmsr r5 + andi. r5,r5,MSR_EE + bne 4f /* - * Check for pending I/O events (iSeries) - * If no I/O events pending then CR0 = "eq" and r4 = 0 - * (kills registers r5 and r6) + * Check for pending interrupts (iSeries) */ - CHECKLPQUEUE(r4,r5,r6) - beq+ 4f /* skip do_IRQ if no events */ -1: + CHECKANYINT(r4,r5) + beq+ 4f /* skip do_IRQ if no interrupts */ + addi r3,r1,STACK_FRAME_OVERHEAD bl .do_IRQ - b 3b /* loop back and handle more */ + b irq_recheck /* loop back and handle more */ 4: - /* - * Check for lost decrementer interrupts. - * (If decrementer popped while we were in the hypervisor) - */ - CHECKDECR(r4,r5) - beq+ 5f - addi r3,r1,STACK_FRAME_OVERHEAD - bl .timer_interrupt -5: LOADADDR(r4,irq_stat) #ifdef CONFIG_SMP /* get processor # */ @@ -388,7 +375,6 @@ /* NEED_RESCHED is a volatile long (64-bits) */ mfspr r4,SPRG3 /* current task's PACA */ ld r4,PACACURRENT(r4) /* Get 'current' */ - ld r3,NEED_RESCHED(r4) cmpi 0,r3,0 /* check need_resched flag */ beq+ 7f @@ -423,17 +409,26 @@ rldicl r0,r0,48,1 rldicl r0,r0,16,0 /* clear MSR_EE */ mtmsrd r0 /* Update machine state */ + + ld r0,_MSR(r1) + andi. r3,r0,MSR_EE + beq+ 1f + + CHECKANYINT(r4,r3) + bne- irq_recheck - /* if returning to user mode, set new sprg3 and save kernel SP */ +1: + stdcx. r0,0,r1 /* to clear the reservation */ + + /* if returning to user mode, save kernel SP */ ld r0,_MSR(r1) andi. r0,r0,MSR_PR beq+ 1f addi r0,r1,INT_FRAME_SIZE /* size of frame */ mfspr r4,SPRG3 /* current task's PACA */ - ld r4,PACACURRENT(r4) /* Get 'current' */ - std r0,THREAD+KSP(r4) /* save kernel stack pointer */ - mfspr r2,SPRG3 /* Get Paca */ - std r1,PACAKSAVE(r2) /* save exception stack pointer */ + ld r2,PACACURRENT(r4) /* Get 'current' */ + std r0,THREAD+KSP(r2) /* save kernel stack pointer */ + std r1,PACAKSAVE(r4) /* save exception stack pointer */ 1: ld r0,_MSR(r1) mtspr SRR1,r0 diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/head.S linuxppc64_2_4/arch/ppc64/kernel/head.S --- linux-2.4.8-ac9/arch/ppc64/kernel/head.S Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/head.S Fri Aug 24 07:02:12 2001 @@ -26,6 +26,8 @@ * */ +#define SECONDARY_PROCESSORS + #include "ppc_asm.h" #include "ppc_defs.h" #include @@ -326,22 +328,32 @@ .globl SystemReset_Iseries SystemReset_Iseries: - mfspr r3,SPRG3 /* Get Paca address */ - lhz r24,PACAPACAINDEX(r3) /* Get processor # */ + mfspr 25,SPRG3 /* Get Paca address */ + lhz r24,PACAPACAINDEX(r25) /* Get processor # */ cmpi 0,r24,0 /* Are we processor 0? */ beq .__start_initialization_iSeries /* Start up the first processor */ mfspr r4,CTRLF li r5,RUNLATCH /* Turn off the run light */ andc r4,r4,r5 mtspr CTRLT,r4 + 1: HMT_LOW #ifdef CONFIG_SMP - lbz r23,PACAPROCSTART(r3) /* Test if this processor + lbz r23,PACAPROCSTART(r25) /* Test if this processor * should start */ + sync + LOADADDR(r3,current_set) + sldi r28,r24,4 /* get current_set[cpu#] */ + ldx r3,r3,r28 + addi r1,r3,TASK_UNION_SIZE + subi r1,r1,STACK_FRAME_OVERHEAD + cmpi 0,r23,0 beq iseries_secondary_smp_loop /* Loop until told to go */ +#ifdef SECONDARY_PROCESSORS bne .__secondary_start /* Loop until told to go */ +#endif iseries_secondary_smp_loop: /* Let the Hypervisor know we are alive */ /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */ @@ -354,13 +366,13 @@ which are running on multi-threaded machines. */ lis r3,0x8000 rldicr r3,r3,32,15 /* r3 = (r3 << 32) & 0xffff000000000000 */ - addi r3,r3,18 /* r3 = -x8000000000000012 which is "yield" */ + addi r3,r3,18 /* r3 = 0x8000000000000012 which is "yield" */ li r4,0 /* "yield timed" */ li r5,-1 /* "yield forever" */ #endif /* CONFIG_SMP */ li r0,-1 /* r0=-1 indicates a Hypervisor call */ sc /* Invoke the hypervisor via a system call */ - mfspr r3,SPRG3 /* Put r3 back */ + mfspr r25,SPRG3 /* Put r25 back ???? */ b 1b /* If SMP not configured, secondaries * loop forever */ @@ -368,7 +380,6 @@ STD_EXCEPTION_COMMON( 0x100, SystemReset, .SystemResetException ) STD_EXCEPTION_COMMON( 0x200, MachineCheck, .MachineCheckException ) - STD_EXCEPTION_COMMON( 0x500, HardwareInterrupt, .do_IRQ ) STD_EXCEPTION_COMMON( 0x900, Decrementer, .timer_interrupt ) STD_EXCEPTION_COMMON( 0xa00, Trap_0a, .UnknownException ) STD_EXCEPTION_COMMON( 0xb00, Trap_0b, .UnknownException ) @@ -376,8 +387,21 @@ STD_EXCEPTION_COMMON( 0xe00, Trap_0e, .UnknownException ) STD_EXCEPTION_COMMON( 0xf00, PerformanceMonitor, .PerformanceMonitorException ) +/* r20 is in SPRG2, + r21 is in the PACA +*/ .globl DataAccess_common DataAccess_common: + mfcr r20 + mfspr r21,DAR + srdi r21,r21,60 + cmpi 0,r21,0xc + bne 3f + + /* Segment faulted on a bolted segment. Go off and map that segment. */ + b .do_stab_bolted + +3: mtcr r20 EXCEPTION_PROLOG_COMMON mfspr r20,DSISR std r20,_DSISR(r21) @@ -520,6 +544,17 @@ .llong .do_page_fault .llong .ret_from_except + .globl HardwareInterrupt_common +HardwareInterrupt_common: + EXCEPTION_PROLOG_COMMON +HardwareInterrupt_entry: + addi r3,r1,STACK_FRAME_OVERHEAD + SET_REG_TO_CONST(r20, MSR_KERNEL) + li r6,0x500 + bl .transfer_to_handler + .llong .do_IRQ + .llong .ret_from_except + .globl Alignment_common Alignment_common: EXCEPTION_PROLOG_COMMON @@ -559,6 +594,11 @@ .globl SystemCall_common SystemCall_common: EXCEPTION_PROLOG_COMMON + cmpi 0,r0,0x5555 /* Special syscall to handle pending */ + bne+ 1f /* interrupts */ + andi. r6,r23,MSR_PR /* Only allowed from kernel */ + beq+ HardwareInterrupt_entry +1: std r3,ORIG_GPR3(r21) SET_REG_TO_CONST(r20, MSR_KERNEL) rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ @@ -612,6 +652,124 @@ ld r21,GPR21(r21) rfid +/* orig r20 is in SPRG2, + orig r21 is in the PACA + r20 contains CCR + + r22 needs to be saved + r1 needs to be saved + CCR needs to be saved +*/ +_GLOBAL(do_stab_bolted) + mfsprg r21,3 + std r22,PACAR22(r21) + std r1,PACAR1(r21) + stw r20,PACACCR(r21) + mfspr r21,DAR + + /* (((ea >> 28) & 0x1fff) << 15) | (ea >> 60) */ + rldicl r20,r21,36,32 /* Permits a full 32b of ESID */ + rldicr r20,r20,15,48 + rldicl r21,r21,4,60 + or r20,r20,r21 + + li r21,9 /* VSID_RANDOMIZER */ + rldicr r21,r21,32,31 + oris r21,r21,58231 + ori r21,r21,39831 + + mulld r20,r20,r21 + clrldi r20,r20,28 /* r20 = vsid */ + + mfsprg r21,3 + ld r21,PACASTABVIRT(r21) + + /* Hash to the primary group */ + mfspr r22,DAR + rldicl r22,r22,36,59 + rldicr r22,r22,7,56 + or r21,r21,r22 /* r21 = first ste of the group */ + + /* Search the primary group for a free entry */ + li r22,0 +1: + ld r1,0(r21) /* Test valid bit of the current ste */ + rldicl r1,r1,57,63 + cmpwi r1,0 + bne 2f + ld r1,8(r21) /* Get the current vsid part of the ste */ + rldimi r1,r20,12,0 /* Insert the new vsid value */ + std r1,8(r21) /* Put new entry back into the stab */ + eieio /* Order vsid update */ + ld r1,0(r21) /* Get the esid part of the ste */ + mfspr r20,DAR /* Get the new esid */ + rldicl r20,r20,36,28 /* Permits a full 36b of ESID */ + rldimi r1,r20,28,0 /* Insert the new esid value */ + ori r1,r1,144 /* Turn on valid and kp */ + std r1,0(r21) /* Put new entry back into the stab */ + sync /* Order the update */ + b 3f +2: + addi r22,r22,1 + addi r21,r21,16 + cmpldi r22,7 + ble 1b + + /* Stick for only searching the primary group for now. */ + /* At least for now, we use a very simple random castout scheme */ + /* Use the TB as a random number ; OR in 1 to avoid entry 0 */ + mftb r22 + andi. r22,r22,7 + ori r22,r22,1 + sldi r22,r22,4 + + /* r21 currently points to and ste one past the group of interest */ + /* make it point to the randomly selected entry */ + subi r21,r21,128 + ori r21,r21,r22 /* r21 is the entry to invalidate */ + + isync /* mark the entry invalid */ + ld r1,0(r21) + li r22,-129 + and r1,r1,r22 + std r1,0(r21) + sync + + ld r1,8(r21) + rldimi r1,r20,12,0 + std r1,8(r21) + eieio + + ld r1,0(r21) /* Get the esid part of the ste */ + mr r22,r1 + mfspr r20,DAR /* Get the new esid */ + rldicl r20,r20,36,32 /* Permits a full 32b of ESID */ + rldimi r1,r20,28,0 /* Insert the new esid value */ + ori r1,r1,144 /* Turn on valid and kp */ + std r1,0(r21) /* Put new entry back into the stab */ + + rldicl r22,r22,36,28 + rldicr r22,r22,28,35 + slbie r22 + sync + +3: + /* All done -- return from exception. */ + mfsprg r20,3 /* Load the PACA pointer */ + + ld r22,LPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */ + ld r21,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */ + mtspr SRR1,r21 + mtspr SRR0,r22 + + lwz r21,PACACCR(r20) /* Restore the clobbered regs */ + mtcr r21 + ld r1, PACAR1(r20) + ld r21,PACAR21(r20) + ld r22,PACAR22(r20) + mfspr r20,SPRG2 + rfid + _GLOBAL(do_stab_SI) mflr r21 /* Save LR in r21 */ /* r21 restored later from r1 */ @@ -670,17 +828,14 @@ SAVE_4GPRS(16, r21) SAVE_8GPRS(24, r21) /* - * Clear any reservation, and clear the RESULT field + * Clear the RESULT field */ - li r22,RESULT - stdcx. r22,r22,r21 /* to clear the reservation */ li r22,0 std r22,RESULT(r21) /* * Test if from user state; result will be tested later */ - andi. r23,r23,MSR_PR /* Set CR for later branch */ - /* SPRG3 -> PACA */ + andi. r23,r23,MSR_PR /* Set CR for later branch */ /* * Indicate that r1 contains the kernel stack and * get the Kernel TOC and CURRENT pointers from the Paca @@ -696,7 +851,6 @@ addi r24,r1,STACK_FRAME_OVERHEAD std r24,THREAD+PT_REGS(r22) 2: - /* * Since we store 'current' in the PACA now, we don't need to * set it here. When r2 was used as 'current' it had to be @@ -733,27 +887,37 @@ * At entry, r3 = this processor's number (in Linux terms, not hardware). */ _GLOBAL(pseries_secondary_smp_init) + + /* turn on 64-bit mode */ + bl .enable_64b_mode + isync + /* Set up a Paca value for this processor. */ LOADADDR(r24, xPaca) /* Get base vaddr of Paca array */ mulli r25,r3,PACA_SIZE /* Calculate vaddr of right Paca */ add r25,r25,r24 /* for this processor. */ - SET_REG_TO_CONST(r26,KERNELBASE) /* Calculate raddr of the Paca */ - sub r26,r25,r26 - mtspr SPRG3,r25 /* Save vaddr of Paca in SPRG3 */ mr r24,r3 /* __secondary_start needs cpu# */ 1: HMT_LOW - lbz r23,PACAPROCSTART(r26) /* Test if this processor should */ + lbz r23,PACAPROCSTART(r25) /* Test if this processor should */ /* start. */ + sync + LOADADDR(r3,current_set) + addi r3,r3,8 + sldi r28,r24,4 /* get current_set[cpu#] */ + ldx r1,r3,r28 + cmpi 0,r23,0 #ifdef CONFIG_SMP - bne .__secondary_start /* Loop until told to go. */ +#ifdef SECONDARY_PROCESSORS + bne .__secondary_start #endif - b 1b - +#endif + b 1b /* Loop until told to go */ + _GLOBAL(__start_initialization_iSeries) LOADADDR(r1,init_task_union) @@ -1071,68 +1235,57 @@ * On entry the following are set: * r24 = cpu# (in Linux terms) * r25 = Paca virtual address - * r26 = Paca physical address * SPRG3 = Paca virtual address */ _GLOBAL(__secondary_start) - /* turn on 64 bit mode */ - bl .enable_64b_mode - isync - - /* get our current offset. */ - SET_REG_TO_CONST(r27, KERNELBASE) HMT_MEDIUM /* Set thread priority to MEDIUM */ - /* get a virtual pointer to current */ - LOADADDR(r3,current_set) - sub r3,r3,r27 - addi r3,r3,8 - sldi r28,r24,4 /* get current_set[cpu#] */ - ldx r1,r3,r28 - /* set up the TOC (virtual address) */ LOADADDR(r2,__toc_start) addi r2,r2,0x4000 addi r2,r2,0x4000 - std r2,PACATOC(r26) + std r2,PACATOC(r25) li r6,0 - std r6,PACAKSAVE(r26) + std r6,PACAKSAVE(r25) #ifndef CONFIG_PPC_ISERIES /* Initialize the page table pointer register. */ - LOADADDR(r6,_SDR1) - sub r6,r6,r27 + LOADADDR(r6,_SDR1) ld r6,0(r6) /* get the value of _SDR1 */ mtspr SDR1,r6 /* set the htab location */ #endif /* Initialize the segment table subsystem. */ - ld r4,PACASTABREAL(r26) /* get raddr of segment table */ - ori r3,r4,1 /* turn on valid bit */ - mtasr r3 /* set the stab location */ + ld r4,PACASTABVIRT(r25) /* get addr of segment table */ li r3,0 /* 0 -> include esid 0xC00000000 */ - slbia + std r3,0(r1) /* Zero the stack frame pointer */ bl .stab_initialize - /* get a virtual pointer to current */ - SET_REG_TO_CONST(r27, KERNELBASE) LOADADDR(r3,current_set) - sub r3,r3,r27 sldi r28,r24,4 /* get current_set[cpu#] */ ldx r6,r3,r28 + std r6,PACACURRENT(r25) addi r1,r6,TASK_UNION_SIZE subi r1,r1,STACK_FRAME_OVERHEAD - addi r3,r3,8 - ld r3,0(r3) + ld r3,PACASTABREAL(r25) /* get raddr of segment table */ + ori r4,r3,1 /* turn on valid bit */ + +#ifdef CONFIG_PPC_ISERIES + li r0,-1 /* hypervisor call */ + li r3,1 + sldi r3,r3,63 /* 0x8000000000000000 */ + ori r3,r3,4 /* 0x8000000000000004 */ + sc /* HvCall_setASR */ +#else + mtasr r4 /* set the stab location */ +#endif + slbia + li r7,0 - std r7,0(r3) mtlr r7 - mfspr r4,SPRG3 - std r6,PACACURRENT(r4) - /* enable MMU and jump to start_secondary */ LOADADDR(r3,.start_secondary) SET_REG_TO_CONST(r4, MSR_KERNEL) @@ -1356,6 +1509,10 @@ bolted_dir: .space 4096 +/* 4096 * 31 bytes of storage */ + .globl stab_array +stab_array: + .space 131072 /* * This space gets a copy of optional info passed to us by the bootstrap * Used to pass parameters into the kernel like root=/dev/sda1, etc. diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/htab.c linuxppc64_2_4/arch/ppc64/kernel/htab.c --- linux-2.4.8-ac9/arch/ppc64/kernel/htab.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/htab.c Tue Aug 21 14:49:26 2001 @@ -82,6 +82,22 @@ #define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) #define RELOC(x) (*PTRRELOC(&(x))) +extern unsigned long htab_size( unsigned long ); + +static inline void +create_pte_mapping(unsigned long start, unsigned long end, + unsigned long mode, unsigned long mask) +{ + unsigned long addr, offset = reloc_offset(); + HTAB *_htab_data = PTRRELOC(&htab_data); + HPTE *htab = (HPTE *)__v2a(_htab_data->htab); + + for (addr=start; addr < end ;addr+=0x1000) { + unsigned long vsid = get_kernel_vsid(addr); + unsigned long va = (vsid << 28) | (addr & 0xfffffff); + make_pte(htab, va, (unsigned long)__v2a(addr), mode, mask); + } +} void htab_initialize(void) @@ -125,11 +141,18 @@ * (addr & KERNELBASE) == 0 (ie they are disjoint). * We also assume that the va is <= 64 bits. */ - create_pte_mapping(_stext, __start_naca, mode_ro, mask); - create_pte_mapping(__start_naca, __end_stab, mode_rw, mask); - create_pte_mapping(__end_stab, _etext, mode_ro, mask); - create_pte_mapping(_etext, RELOC(klimit), mode_rw, mask); - create_pte_mapping(__a2v(table), __a2v(table+htab_size_bytes), mode_rw, mask); +#if 0 + create_pte_mapping((unsigned long)_stext, (unsigned long)__start_naca, mode_ro, mask); + create_pte_mapping((unsigned long)__start_naca, (unsigned long)__end_stab, mode_rw, mask); + create_pte_mapping((unsigned long)__end_stab, (unsigned long)_etext, mode_ro, mask); + create_pte_mapping((unsigned long)_etext, RELOC(klimit), mode_rw, mask); + create_pte_mapping((unsigned long)__a2v(table), (unsigned long)__a2v(table+htab_size_bytes), mode_rw, mask); +#else + create_pte_mapping((unsigned long)KERNELBASE, + KERNELBASE+(_naca->physicalMemorySize), + mode_rw, mask); +#endif + } static inline unsigned long hpt_hash( unsigned long vpn ) @@ -150,6 +173,7 @@ { HPTE *hptep; unsigned long hash, i; + volatile unsigned long x = 1; hash = hpt_hash( va >> 12 ); @@ -162,9 +186,12 @@ hptep->dw0.dw0.avpn = va >> 23; hptep->dw0.dw0.bolted = 1; /* bolted */ hptep->dw0.dw0.v = 1; /* make valid */ - break; + return; } } + + /* We should _never_ get here and too early to call xmon. */ + for(;x;x|=1); } void invalidate_hpte( unsigned long slot ) @@ -318,6 +345,69 @@ return 0; } +static unsigned long get_hpte0( unsigned long slot ) +{ + unsigned long dword0; + + if ( _machine == _MACH_iSeries ) { + HPTE hpte; + HvCallHpt_get( &hpte, slot ); + dword0 = hpte.dw0.dword0; + } + else { + HPTE * hptep = htab_data.htab + slot; + dword0 = hptep->dw0.dword0; + } + + return dword0; +} + +long find_hpte( unsigned long vpn ) +{ + HPTE hpte; + long slot; + + if ( _machine == _MACH_iSeries ) { + slot = HvCallHpt_findValid( &hpte, vpn ); + if ( hpte.dw0.dw0.v ) { + if ( slot < 0 ) { + slot &= 0x7fffffffffffffff; + slot = -slot; + } + } + else + slot = -1; + } + else { + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + unsigned long hash; + unsigned long i,j; + + hash = hpt_hash( vpn ); + for ( j=0; j<2; ++j ) { + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + for ( i=0; i> 11 ) ) && + ( hpte_dw0.h.v ) && + ( hpte_dw0.h.h == j ) ) { + /* HPTE matches */ + if ( j ) + slot = -slot; + return slot; + } + ++slot; + } + hash = ~hash; + } + slot = -1; + } + return slot; +} + /* This function is called by btmalloc to bolt an entry in the hpt */ void build_valid_hpte( unsigned long vsid, unsigned long ea, unsigned long pa, pte_t * ptep, unsigned hpteflags, unsigned bolted ) @@ -474,23 +564,6 @@ 1 ) ); } -static unsigned long get_hpte0( unsigned long slot ) -{ - unsigned long dword0; - - if ( _machine == _MACH_iSeries ) { - HPTE hpte; - HvCallHpt_get( &hpte, slot ); - dword0 = hpte.dw0.dword0; - } - else { - HPTE * hptep = htab_data.htab + slot; - dword0 = hptep->dw0.dword0; - } - - return dword0; -} - static void updateHptePP( long slot, unsigned long newpp, unsigned long va ) { if ( _machine == _MACH_iSeries ) { @@ -552,6 +625,7 @@ long slot; struct mm_struct * mm; pte_t old_pte, new_pte, *ptep; + volatile unsigned long x = 1; /* Check for invalid addresses. */ if (!IS_VALID_EA(ea)) { @@ -561,6 +635,14 @@ regionid = REGION_ID(ea); switch ( regionid ) { case KERNEL_REGION_ID: + + /* As htab_initialize is now, we shouldn't ever get here since + * we're bolting the entire 0xC0... region. + */ + udbg_printf("Doh!!! hash_page saw a kernel address...\n"); + xmon(0); + for(;x;x|=1); + vsid = get_kernel_vsid( ea ); va = ( vsid << 28 ) | ( ea & 0x0fffffff ); vpn = va >> PAGE_SHIFT; @@ -568,7 +650,7 @@ pte_val(old_pte) = ((__pa(ea)&PAGE_MASK)<<(PTE_SHIFT-PAGE_SHIFT)) | _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED | _PAGE_RW | _PAGE_COHERENT | - _PAGE_DIRTY | _PAGE_HASHPTE; + _PAGE_DIRTY | _PAGE_HPTENOIX; spin_lock( &hash_table_lock ); break; case VMALLOC_REGION_ID: @@ -669,6 +751,24 @@ access |= _PAGE_PRESENT; if ( 0 == ( access & ~(pte_val(old_pte)) ) ) { + /* + * Check if pte might have an hpte, but we have + * no slot information + */ + if ( pte_val(old_pte) & _PAGE_HPTENOIX ) { + unsigned long slot; + pte_val(old_pte) &= ~_PAGE_HPTEFLAGS; + slot = find_hpte( vpn ); + if ( slot != -1 ) { + if ( slot < 0 ) { + pte_val(old_pte) |= _PAGE_SECONDARY; + slot = -slot; + } + pte_val(old_pte) |= ((slot << 12) & _PAGE_GROUP_IX) | _PAGE_HASHPTE; + + } + } + /* User has appropriate access rights. */ new_pte = old_pte; /* If the attempted access was a store */ @@ -848,6 +948,25 @@ /* HPTE matches */ invalidate_hpte( slot ); } + else { + unsigned k; + /* Temporarily lets check for the hpte in all possible slots */ + for ( secondary = 0; secondary < 2; ++secondary ) { + hash = hpt_hash( vpn ); + if ( secondary ) + hash = ~hash; + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + for ( k=0; k<8; ++k ) { + hpte_dw0.d = get_hpte0( slot+k ); + if ( ( hpte_dw0.h.avpn == (vpn >> 11) ) && + ( hpte_dw0.h.v ) && + ( hpte_dw0.h.h == secondary ) ) { + while (1) ; + } + } + } + + } spin_unlock_irqrestore( &hash_table_lock, flags ); } @@ -1042,20 +1161,5 @@ pteg_count >>= 3; } return (pteg_count << 7); /* pteg_count*128B/PTEG */ -} - -static inline void -create_pte_mapping(unsigned long start, unsigned long end, - unsigned long mode, unsigned long mask) -{ - unsigned long addr, offset = reloc_offset(); - HTAB *_htab_data = PTRRELOC(&htab_data); - HPTE *htab = (HPTE *)__v2a(_htab_data->htab); - - for (addr=start; addr < end ;addr+=0x1000) { - unsigned long vsid = get_kernel_vsid(addr); - unsigned long va = (vsid << 28) | (addr & 0xfffffff); - make_pte(htab, va, __v2a(addr), mode, mask); - } } diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/iSeries_IoMmTable.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_IoMmTable.c --- linux-2.4.8-ac9/arch/ppc64/kernel/iSeries_IoMmTable.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_IoMmTable.c Fri Aug 24 06:41:08 2001 @@ -0,0 +1,187 @@ +/************************************************************************/ +/* This module supports the iSeries I/O Address translation mapping */ +/* Copyright (C) 20yy */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, December 14, 2000 */ +/* Added Bar table for IoMm performance. */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include "iSeries_IoMmTable.h" +#include "pci.h" + +void iSeries_allocateDeviceBars(struct pci_dev* PciDevPtr); +/*******************************************************************/ +/* Table defines */ +/* Entry size is 4 MB * 1024 Entries = 4GB. */ +/*******************************************************************/ +#define iSeries_IoMmTable_Entry_Size 0x00400000 +#define iSeries_IoMmTable_Size 1024 +#define iSeries_Base_Io_Memory 0xFFFFFFFF + +/*******************************************************************/ +/* Static and Global variables */ +/*******************************************************************/ +struct pci_dev* iSeries_IoMmTable[iSeries_IoMmTable_Size]; +u8 iSeries_IoBarTable[iSeries_IoMmTable_Size]; +static int iSeries_CurrentIndex; +static char* iSeriesPciIoText = "iSeries PCI I/O"; +static spinlock_t iSeriesIoMmTableLock = SPIN_LOCK_UNLOCKED; +/*******************************************************************/ +/* iSeries_IoMmTable_Initialize */ +/*******************************************************************/ +/* - Initalizes the Address Translation Table and get it ready for */ +/* use. Must be called before any client calls any of the other */ +/* methods. */ +/*******************************************************************/ +void iSeries_IoMmTable_Initialize(void) { + int Index; + spin_lock(&iSeriesIoMmTableLock); + for(Index=0;Indexresource[BarNumber]; + iSeries_IoMmTable_AllocateEntry(PciDev, BarNumber); + } +} + +/*******************************************************************/ +/* iSeries_IoMmTable_AllocateEntry */ +/*******************************************************************/ +/* Adds pci_dev entry in address translation table */ +/*******************************************************************/ +/* - Allocates the number of entries required in table base on BAR */ +/* size. */ +/* - This version, allocates top down, starting at 4GB. */ +/* - The size is round up to be a multiple of entry size. */ +/* - CurrentIndex is decremented to keep track of the last entry. */ +/* - Builds the resource entry for allocated BARs. */ +/*******************************************************************/ +void iSeries_IoMmTable_AllocateEntry(struct pci_dev* PciDev, int BarNumber) { + struct resource* BarResource = &PciDev->resource[BarNumber]; + int BarSize = BarResource->end - BarResource->start; + unsigned long BarStartAddr; + unsigned long BarEndAddr; + /***************************************************************/ + /* No space to allocate, skip Allocation. */ + /***************************************************************/ + if(BarSize == 0) return; /* Quick stage exit */ + + /***************************************************************/ + /* Allocate the table entries needed. */ + /***************************************************************/ + spin_lock(&iSeriesIoMmTableLock); + while(BarSize > 0) { + iSeries_IoMmTable[iSeries_CurrentIndex] = PciDev; + iSeries_IoBarTable[iSeries_CurrentIndex] = BarNumber; + BarSize -= iSeries_IoMmTable_Entry_Size; + --iSeries_CurrentIndex; /* Next Free entry */ + } + spin_unlock(&iSeriesIoMmTableLock); + BarStartAddr = iSeries_IoMmTable_Entry_Size*(iSeries_CurrentIndex+1); + BarEndAddr = BarStartAddr + (BarResource->end - BarResource->start); + /***************************************************************/ + /* Build Resource info */ + /***************************************************************/ + BarResource->name = iSeriesPciIoText; + BarResource->start = (long)BarStartAddr; + BarResource->end = (long)BarEndAddr; + + PPCDBG(PPCDBG_BUSWALK,"BarAloc %04X-%016LX\n",iSeries_CurrentIndex+1,BarStartAddr); +} +/*******************************************************************/ +/* Translates an I/O Memory address to pci_dev* */ +/*******************************************************************/ +struct pci_dev* iSeries_xlateIoMmAddress(unsigned long* IoAddress) { + int PciDevIndex = (unsigned long)IoAddress/iSeries_IoMmTable_Entry_Size; + struct pci_dev* PciDev = iSeries_IoMmTable[PciDevIndex]; + if(PciDev == 0) { + printk("PCI: Invalid I/O Address: 0x%016LX\n",IoAddress); + PCIFR("Invalid MMIO Address 0x%016LX",(unsigned long)IoAddress); + } + return PciDev; +} +/************************************************************************/ +/* Returns the Bar number of Address */ +/************************************************************************/ +int iSeries_IoMmTable_Bar(unsigned long* IoAddress) { + int BarIndex = (unsigned long)IoAddress/iSeries_IoMmTable_Entry_Size; + int BarNumber = iSeries_IoBarTable[BarIndex]; + return BarNumber; +} +/************************************************************************/ +/* Return the Bar Base Address or 0. */ +/************************************************************************/ +unsigned long* iSeries_IoMmTable_BarBase(unsigned long* IoAddress) { + unsigned long BaseAddr = 0; + struct pci_dev* PciDev = iSeries_xlateIoMmAddress(IoAddress); + if(PciDev != NULL) { + int BarNumber = iSeries_IoMmTable_Bar(IoAddress); + if(BarNumber != -1) { + BaseAddr = PciDev->resource[BarNumber].start; + } + } + return (unsigned long*)BaseAddr; +} +/************************************************************************/ +/* Return the Bar offset within the Bar Space */ +/* Note: Assumes that address is valid. */ +/************************************************************************/ +unsigned long iSeries_IoMmTable_BarOffset(unsigned long* IoAddress) { + return (unsigned long)IoAddress-(unsigned long)iSeries_IoMmTable_BarBase(IoAddress); +} +/************************************************************************/ +/* Return 0 if Address is valid I/O Address */ +/************************************************************************/ +int iSeries_Is_IoMmAddress(unsigned long* IoAddress) { + if( iSeries_xlateIoMmAddress(IoAddress) == NULL) return 1; + else return 0; +} + + diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/iSeries_IoMmTable.h linuxppc64_2_4/arch/ppc64/kernel/iSeries_IoMmTable.h --- linux-2.4.8-ac9/arch/ppc64/kernel/iSeries_IoMmTable.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_IoMmTable.h Fri Aug 24 06:41:08 2001 @@ -0,0 +1,99 @@ +#ifndef _ISERIES_IOMMTABLE_H +#define _ISERIES_IOMMTABLE_H +/************************************************************************/ +/* File iSeries_IoMmTable.h created by Allan Trautman on Dec 12 2001. */ +/************************************************************************/ +/* Interfaces for the write/read Io address translation table. */ +/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created December 12, 2000 */ +/* End Change Activity */ +/************************************************************************/ + +/************************************************************************/ +/* iSeries_IoMmTable_Initialize */ +/************************************************************************/ +/* - Initalizes the Address Translation Table and get it ready for use. */ +/* Must be called before any client calls any of the other methods. */ +/* */ +/* Parameters: None. */ +/* */ +/* Return: None. */ +/************************************************************************/ +extern void iSeries_IoMmTable_Initialize(void); + +/************************************************************************/ +/* iSeries_allocateDeviceBars */ +/************************************************************************/ +/* - Allocates ALL pci_dev BAR's and updates the resources with the BAR */ +/* value. BARS with zero length will not have the resources. The */ +/* HvCallPci_getBarParms is used to get the size of the BAR space. */ +/* It calls as400_IoMmTable_AllocateEntry to allocate each entry. */ +/* */ +/* Parameters: */ +/* pci_dev = Pointer to pci_dev structure that will be mapped to pseudo */ +/* I/O Address. */ +/* */ +/* Return: */ +/* The pci_dev I/O resources updated with pseudo I/O Addresses. */ +/************************************************************************/ +extern void iSeries_allocateDeviceBars(struct pci_dev* ); + +/************************************************************************/ +/* iSeries_IoMmTable_AllocateEntry */ +/************************************************************************/ +/* - Allocates(adds) the pci_dev entry in the Address Translation Table */ +/* and updates the Resources for the device. */ +/* */ +/* Parameters: */ +/* pci_dev = Pointer to pci_dev structure that will be mapped to pseudo */ +/* I/O Address. */ +/* */ +/* BarNumber = Which Bar to be allocated. */ +/* */ +/* Return: */ +/* The pseudo I/O Address in the resources that will map to the */ +/* pci_dev on iSeries_xlateIoMmAddress call. */ +/************************************************************************/ +extern void iSeries_IoMmTable_AllocateEntry(struct pci_dev* , int BarNumber); + +/************************************************************************/ +/* iSeries_xlateIoMmAddress */ +/************************************************************************/ +/* - Translates an I/O Memory address to pci_dev that has been allocated*/ +/* the psuedo I/O Address. */ +/* */ +/* Parameters: */ +/* IoAddress = I/O Memory Address. */ +/* */ +/* Return: */ +/* A pci_dev pointer to the device mapped to the I/O address. */ +/************************************************************************/ +extern struct pci_dev* iSeries_xlateIoMmAddress(unsigned long* IoAddress); + +/************************************************************************/ +/* Helper Methods */ +/************************************************************************/ +extern int iSeries_IoMmTable_Bar(unsigned long *IoAddress); +extern unsigned long* iSeries_IoMmTable_BarBase(unsigned long* IoAddress); +extern unsigned long iSeries_IoMmTable_BarOffset(unsigned long* IoAddress); +extern int iSeries_Is_IoMmAddress(unsigned long* IoAddress); + +#endif /* _ISERIES_IOMMTABLE_H */ diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/iSeries_VpdInfo.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_VpdInfo.c --- linux-2.4.8-ac9/arch/ppc64/kernel/iSeries_VpdInfo.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_VpdInfo.c Fri Aug 24 06:41:08 2001 @@ -0,0 +1,360 @@ +/************************************************************************/ +/* File iSeries_vpdInfo.c created by Allan Trautman on Fri Feb 2 2001. */ +/************************************************************************/ +/* This code gets the card location of the hardware */ +/* Copyright (C) 20yy */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Feb 2, 2001 */ +/* Ported to ppc64, August 20, 2001 */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "pci.h" + +/************************************************/ +/* Size of Bus VPD data */ +/************************************************/ +#define BUS_VPDSIZE 1024 +/************************************************/ +/* Bus Vpd Tags */ +/************************************************/ +#define VpdEndOfDataTag 0x78 +#define VpdEndOfAreaTag 0x79 +#define VpdIdStringTag 0x82 +#define VpdVendorAreaTag 0x84 +/************************************************/ +/* Mfg Area Tags */ +/************************************************/ +#define VpdAsmPartNumber 0x504E // "PN" +#define VpdFruFlag 0x4647 // "FG" +#define VpdFruLocation 0x464C // "FL" +#define VpdFruFrameId 0x4649 // "FI" +#define VpdFruPartNumber 0x464E // "FN" +#define VpdFruPowerData 0x5052 // "PR" +#define VpdFruSerial 0x534E // "SN" +#define VpdFruType 0x4343 // "CC" +#define VpdFruCcinExt 0x4345 // "CE" +#define VpdFruRevision 0x5256 // "RV" +#define VpdSlotMapFormat 0x4D46 // "MF" +#define VpdSlotMap 0x534D // "SM" + +/************************************************/ +/* Structures of the areas */ +/************************************************/ +struct BusVpdAreaStruct { + u8 Tag; + u8 LowLength; + u8 HighLength; +}; +typedef struct BusVpdAreaStruct BusVpdArea; +#define BUS_ENTRY_SIZE 3 + +struct MfgVpdAreaStruct { + u16 Tag; + u8 TagLength; +}; +typedef struct MfgVpdAreaStruct MfgVpdArea; +#define MFG_ENTRY_SIZE 3 + +struct SlotMapFormatStruct { + u16 Tag; + u8 TagLength; + u16 Format; +}; + +struct FrameIdMapStruct{ + u16 Tag; + u8 TagLength; + u8 FrameId; +}; +typedef struct FrameIdMapStruct FrameIdMap; + +struct SlotMapStruct { + u8 AgentId; + u8 SecondaryAgentId; + u8 PhbId; + char CardLocation[3]; + char Parms[8]; + char Reserved[2]; +}; +typedef struct SlotMapStruct SlotMap; +#define SLOT_ENTRY_SIZE 16 + +/****************************************************************/ +/* Prototypes */ +/****************************************************************/ +static void iSeries_Parse_Vpd(BusVpdArea*, int, LocationData*); +static void iSeries_Parse_MfgArea(MfgVpdArea*,int, LocationData*); +static void iSeries_Parse_SlotArea(SlotMap*, int, LocationData*); +static void iSeries_Parse_PhbId(BusVpdArea*, int, LocationData*); + +/**************************************************************** + * iSeries_loc-code(struct pci_dev* PciDev) * + * * + * Gets the frame and card location of the pci_dev device. The * + * return is a kmalloc string. The user must free this string * + * when they are done. This is specifically for the device tree* + * * + * Returns: * + * "Frame 1, Card C10" * + ****************************************************************/ +char* iSeries_Location_Code(struct pci_dev* PciDev) { + char TempBuffer[128]; + LocationData* LocationPtr = iSeries_GetLocationData(PciDev); + int LineLen = sprintf(TempBuffer," Frame%3d, Card %4s ", + LocationPtr->FrameId,LocationPtr->CardLocation); + char* RtnString = kmalloc(LineLen+7,GFP_KERNEL); + strcpy(RtnString,TempBuffer); + kfree(LocationPtr); + return RtnString; +} +/****************************************************************/ +/* */ +/* */ +/* */ +/****************************************************************/ +LocationData* iSeries_GetLocationData(struct pci_dev* PciDev) { + LocationData* LocationPtr = 0; + BusVpdArea* BusVpdPtr = 0; + int BusVpdLen = 0; + BusVpdPtr = (BusVpdArea*)kmalloc(BUS_VPDSIZE, GFP_KERNEL); + BusVpdLen = HvCallPci_getBusVpd(PciDev->bus->number,REALADDR(BusVpdPtr),BUS_VPDSIZE); + /* printk("PCI: VpdBuffer 0x%08X \n",(int)BusVpdPtr); */ + /***************************************************************/ + /* Need to set Agent Id, Bus in location info before the call */ + /***************************************************************/ + LocationPtr = (LocationData*)kmalloc(LOCATION_DATA_SIZE, GFP_KERNEL); + LocationPtr->Bus = PciDev->bus->number; + LocationPtr->Board = 0; + LocationPtr->FrameId = 0; + iSeries_Parse_PhbId(BusVpdPtr,BusVpdLen,LocationPtr); + LocationPtr->Card = PCI_SLOT(PciDev->devfn); + strcpy(LocationPtr->CardLocation,"Cxx"); + LocationPtr->AgentId = ISERIES_DECODE_DEVICE(PciDev->devfn); + LocationPtr->SecondaryAgentId = 0x10; + /* And for Reference only. */ + LocationPtr->LinuxBus = PciDev->bus->number; + LocationPtr->LinuxDevFn = PciDev->devfn; + /***************************************************************/ + /* Any data to process? */ + /***************************************************************/ + if(BusVpdLen > 0) { + iSeries_Parse_Vpd(BusVpdPtr, BUS_VPDSIZE, LocationPtr); + } + else { + PCIFR("No VPD Data...."); + } + kfree(BusVpdPtr); + + return LocationPtr; +} +/****************************************************************/ +/* */ +/****************************************************************/ +void iSeries_Parse_Vpd(BusVpdArea* VpdData,int VpdLen, LocationData* LocationPtr) { + MfgVpdArea* MfgVpdPtr = 0; + int BusTagLen = 0; + BusVpdArea* BusVpdPtr = VpdData; + int BusVpdLen = VpdLen; + /*************************************************************/ + /* Make sure this is what I think it is */ + /*************************************************************/ + if(BusVpdPtr->Tag != VpdIdStringTag) { /*0x82 */ + PCIFR("Not 0x82 start of VPD."); + return; + } + BusTagLen = (BusVpdPtr->HighLength*256)+BusVpdPtr->LowLength; + BusTagLen += BUS_ENTRY_SIZE; + BusVpdPtr = (BusVpdArea*)((u32)BusVpdPtr+BusTagLen); + BusVpdLen -= BusTagLen; + /*************************************************************/ + /* Parse the Data */ + /*************************************************************/ + while(BusVpdLen > 0 ) { + BusTagLen = (BusVpdPtr->HighLength*256)+BusVpdPtr->LowLength; + /*********************************************************/ + /* End of data Found */ + /*********************************************************/ + if(BusVpdPtr->Tag == VpdEndOfAreaTag) { + BusVpdLen = 0; /* Done, Make sure */ + } + /*********************************************************/ + /* Was Mfg Data Found */ + /*********************************************************/ + else if(BusVpdPtr->Tag == VpdVendorAreaTag) { + MfgVpdPtr = (MfgVpdArea*)((u32)BusVpdPtr+BUS_ENTRY_SIZE); + iSeries_Parse_MfgArea(MfgVpdPtr,BusTagLen,LocationPtr); + } + /********************************************************/ + /* On to the next tag. */ + /********************************************************/ + if(BusVpdLen > 0) { + BusTagLen += BUS_ENTRY_SIZE; + BusVpdPtr = (BusVpdArea*)((u32)BusVpdPtr+BusTagLen); + BusVpdLen -= BusTagLen; + } + } +} + +/*****************************************************************/ +/* Parse the Mfg Area */ +/*****************************************************************/ +void iSeries_Parse_MfgArea(MfgVpdArea* VpdDataPtr,int VpdLen, LocationData* LocationPtr) { + SlotMap* SlotMapPtr = 0; + u16 SlotMapFmt = 0; + int MfgTagLen = 0; + MfgVpdArea* MfgVpdPtr = VpdDataPtr; + int MfgVpdLen = VpdLen; + + /*************************************************************/ + /* Parse Mfg Data */ + /*************************************************************/ + while(MfgVpdLen > 0) { + MfgTagLen = MfgVpdPtr->TagLength; + if (MfgVpdPtr->Tag == VpdFruFlag) {} /* FG */ + else if(MfgVpdPtr->Tag == VpdFruSerial) {} /* SN */ + else if(MfgVpdPtr->Tag == VpdAsmPartNumber){} /* PN */ + /*********************************************************/ + /* Frame ID */ + /*********************************************************/ + if(MfgVpdPtr->Tag == VpdFruFrameId) { /* FI */ + LocationPtr->FrameId = ((FrameIdMap*)MfgVpdPtr)->FrameId; + } + /*********************************************************/ + /* Slot Map Format */ + /*********************************************************/ + else if(MfgVpdPtr->Tag == VpdSlotMapFormat){ /* MF */ + SlotMapFmt = ((struct SlotMapFormatStruct*)MfgVpdPtr)->Format; + } + /*********************************************************/ + /* Slot Labels */ + /*********************************************************/ + else if(MfgVpdPtr->Tag == VpdSlotMap){ /* SM */ + if(SlotMapFmt == 0x1004) SlotMapPtr = (SlotMap*)((u32)MfgVpdPtr+MFG_ENTRY_SIZE+1); + else SlotMapPtr = (SlotMap*)((u32)MfgVpdPtr+MFG_ENTRY_SIZE); + iSeries_Parse_SlotArea(SlotMapPtr,MfgTagLen, LocationPtr); + } + /*********************************************************/ + /* Point to the next Mfg Area */ + /* Use defined size, sizeof give wrong answer */ + /*********************************************************/ + MfgTagLen += MFG_ENTRY_SIZE; + MfgVpdPtr = (MfgVpdArea*)( (u32)MfgVpdPtr + MfgTagLen); + MfgVpdLen -= MfgTagLen; + } +} +/*****************************************************************/ +/* Look for "BUS" Tag to set the PhbId. */ +/*****************************************************************/ +void iSeries_Parse_PhbId(BusVpdArea* VpdData,int VpdLen,LocationData* LocationPtr) { + int PhbId = 0xff; /* Not found flag */ + char* PhbPtr = (char*)VpdData+3; /* Skip over 82 tag */ + int DataLen = VpdLen; + while(DataLen > 0) { + if(*PhbPtr == 'B' && *(PhbPtr+1) == 'U' && *(PhbPtr+2) == 'S') { + if(*(PhbPtr+3) == ' ') PhbPtr += 4;/* Skip white spac*/ + else PhbPtr += 3; + if (*PhbPtr == '0') PhbId = 0; /* Don't convert, */ + else if(*PhbPtr == '1') PhbId = 1; /* Sanity check */ + else if(*PhbPtr == '2') PhbId = 2; /* values */ + DataLen = 0; /* Exit loop. */ + } + ++PhbPtr; + --DataLen; + } + LocationPtr->PhbId = PhbId; +} +/*****************************************************************/ +/* Parse the Slot Area */ +/*****************************************************************/ +void iSeries_Parse_SlotArea(SlotMap* MapPtr,int MapLen, LocationData* LocationPtr) { + int SlotMapLen = MapLen; + SlotMap* SlotMapPtr = MapPtr; + /*************************************************************/ + /* Parse Slot label until we find the one requrested */ + /*************************************************************/ + while(SlotMapLen > 0) { + if(SlotMapPtr->AgentId == LocationPtr->AgentId && + SlotMapPtr->SecondaryAgentId == LocationPtr->SecondaryAgentId) { + /*****************************************************/ + /* If Phb wasn't found, grab the first one found. */ + /*****************************************************/ + if(LocationPtr->PhbId == 0xff) LocationPtr->PhbId = SlotMapPtr->PhbId; + if( SlotMapPtr->PhbId == LocationPtr->PhbId ) { + /*****************************************************/ + /* Found what we were looking for, extract the data. */ + /*****************************************************/ + memcpy(&LocationPtr->CardLocation,&SlotMapPtr->CardLocation,3); + LocationPtr->CardLocation[3] = 0; /* Null terminate*/ + SlotMapLen = 0; /* We are done */ + } + } + /*********************************************************/ + /* Point to the next Slot */ + /* Use defined size, sizeof may give wrong answer */ + /*********************************************************/ + SlotMapLen -= SLOT_ENTRY_SIZE; + SlotMapPtr = (SlotMap*)((u32)SlotMapPtr+SLOT_ENTRY_SIZE); + } +} +/************************************************************************/ +/* Formats the device information. */ +/* - Pass in pci_dev* pointer to the device. */ +/* - Pass in buffer to place the data. Danger here is the buffer must */ +/* be as big as the client says it is. Should be at least 128 bytes.*/ +/* Return will the length of the string data put in the buffer. */ +/* Format: */ +/* PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet */ +/* controller */ +/************************************************************************/ +int iSeries_Device_Information(struct pci_dev* PciDev,char* Buffer, int BufferSize) { + LocationData* LocationPtr; /* VPD Information */ + char* BufPtr = Buffer; + int LineLen = 0; + if(BufferSize >= 128) { + LineLen = sprintf(BufPtr+LineLen,"PCI: Bus%3d, Device%3d, Vendor %04X ", + PciDev->bus->number, PCI_SLOT(PciDev->devfn),PciDev->vendor); + + LocationPtr = iSeries_GetLocationData(PciDev); + LineLen += sprintf(BufPtr+LineLen," Frame%3d, Card %4s ", + LocationPtr->FrameId,LocationPtr->CardLocation); + kfree(LocationPtr); + + if(pci_class_name(PciDev->class >> 8) == 0) { + LineLen += sprintf(BufPtr+LineLen,"0x%04X ",(int)(PciDev->class >> 8)); + } + else { + LineLen += sprintf(BufPtr+LineLen,"%s",pci_class_name(PciDev->class >> 8) ); + } + } + return LineLen; +} diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/iSeries_setup.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_setup.c --- linux-2.4.8-ac9/arch/ppc64/kernel/iSeries_setup.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_setup.c Fri Aug 24 09:49:43 2001 @@ -54,7 +54,7 @@ extern void abort(void); static void build_iSeries_Memory_Map( void ); static void setup_iSeries_cache_sizes( void ); -static void iSeries_bolt_kernel( unsigned long pages_to_bolt ); +static void iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr); extern void ppcdbg_initialize(void); /* Global Variables */ @@ -92,7 +92,7 @@ */ if ( naca->xRamDisk ) { - initrd_start = __va(naca->xRamDisk); + initrd_start = (unsigned long)__va(naca->xRamDisk); initrd_end = initrd_start + naca->xRamDiskSize * PAGE_SIZE; initrd_below_start_ok = 1; // ramdisk in kernel space ROOT_DEV = MKDEV( RAMDISK_MAJOR, 0 ); @@ -243,7 +243,7 @@ loadAreaFirstChunk = (u32)addr_to_chunk(itLpNaca.xLoadAreaAddr); loadAreaSize = itLpNaca.xLoadAreaChunks; - + /* Only add the pages already mapped here. * Otherwise we might add the hpt pages * The rest of the pages of the load area @@ -256,7 +256,7 @@ loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; printk( "Mapping load area - physical addr = 0000000000000000\n" - " absolute addr = %016x\n", + " absolute addr = %016lx\n", chunk_to_addr(loadAreaFirstChunk) ); printk( "Load area size %dK\n", loadAreaSize*256 ); @@ -275,7 +275,7 @@ hptSizeChunks = hptSizePages >> (msChunks.chunk_shift-PAGE_SHIFT); hptLastChunk = hptFirstChunk + hptSizeChunks - 1; - printk( "HPT absolute addr = %016x, size = %dK\n", + printk( "HPT absolute addr = %016lx, size = %dK\n", chunk_to_addr(hptFirstChunk), hptSizeChunks*256 ); /* Fill in the htab_data structure */ @@ -289,7 +289,7 @@ htab_data.htab = NULL; /* Update the page table to bolt the kernel */ - iSeries_bolt_kernel( __pa(((unsigned long)_end + PAGE_SIZE) & ~PAGE_SIZE) >> PAGE_SHIFT ); + iSeries_bolt_kernel( 0, __pa(PAGE_ALIGN((unsigned long)_end)) ); /* Determine if absolute memory has any * holes so that we can interpret the @@ -357,6 +357,13 @@ * nextPhysChunk */ naca->physicalMemorySize = chunk_to_addr(nextPhysChunk); + + iSeries_bolt_kernel( __pa(PAGE_ALIGN((unsigned long)_end)), naca->physicalMemorySize ); + + lmb_init(); + lmb_add( 0, naca->physicalMemorySize ); + lmb_reserve( 0, __pa(klimit)); + } /* @@ -390,22 +397,27 @@ } /* - * Bolt the kernel (text and data) into the HPT + * Bolt the kernel addr space into the HPT */ -static void __init iSeries_bolt_kernel( unsigned long pages_to_bolt ) +static void __init iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr) { - unsigned long vsid, va, vpn, slot, i; + unsigned long pa; + unsigned long mode_rw = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX; HPTE hpte; - for ( i=0; i> PAGE_SHIFT; - slot = HvCallHpt_findValid( &hpte, vpn ); + for (pa=saddr; pa < eaddr ;pa+=PAGE_SIZE) { + unsigned long ea = __va(pa); + unsigned long vsid = get_kernel_vsid( ea ); + unsigned long va = ( vsid << 28 ) | ( pa & 0xfffffff ); + unsigned long vpn = va >> PAGE_SHIFT; + unsigned long slot = HvCallHpt_findValid( &hpte, vpn ); if ( hpte.dw0.dw0.v ) { - /* Mark the page as bolted */ + /* HPTE exists, so just bolt it */ HvCallHpt_setSwBits( slot, 0x10, 0 ); + } else { + /* No HPTE exists, so create a new bolted one */ + build_valid_hpte(vsid, ea, pa, NULL, mode_rw, 1); } } } @@ -502,6 +514,77 @@ return (len); } +#ifdef CONFIG_SMP +void iSeries_spin_lock(spinlock_t *lock) +{ + int i; + for (;;) { + for (i=1024; i>0; --i) { + HMT_low(); + if ( lock->lock == 0 ) { + HMT_medium(); + if ( __spin_trylock(lock) ) + return; + } + } + HMT_medium(); + HvCallCfg_getLps(); + } +} + +void iSeries_read_lock(__rwlock_t *rw) +{ + int i; + for (;;) { + for (i=1024; i>0; --i) { + HMT_low(); + if ( rw->lock >= 0 ) { + HMT_medium(); + if ( __read_trylock(rw) ) + return; + } + } + HMT_medium(); + HvCallCfg_getLps(); + } +} + +void iSeries_write_lock(__rwlock_t *rw) +{ + int i; + for (;;) { + for (i=1024; i>0; --i) { + HMT_low(); + if ( rw->lock == 0 ) { + HMT_medium(); + if ( __write_trylock(rw) ) + return; + } + } + HMT_medium(); + HvCallCfg_getLps(); + } +} + +void iSeries_brlock_spin( spinlock_t *lock ) +{ + int i; + for(;;) { + for (i=1024; i>0; --i) { + HMT_low(); + if (lock->lock == 0) { + HMT_medium(); + return; + } + + } + HMT_medium(); + HvCallCfg_getLps(); + } +} + +#endif /* CONFIG_SMP */ + int iSeries_get_cpuinfo(char *buffer) { int len = 0; @@ -605,7 +688,7 @@ * Set the RTC in the virtual service processor * This requires flowing LpEvents to the primary partition */ -int __init +int iSeries_set_rtc_time(unsigned long time) { mf_setRtcTime(time); @@ -616,7 +699,7 @@ * Get the RTC from the virtual service processor * This requires flowing LpEvents to the primary partition */ -unsigned long __init +unsigned long iSeries_get_rtc_time(void) { unsigned long time; diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/idle.c linuxppc64_2_4/arch/ppc64/kernel/idle.c --- linux-2.4.8-ac9/arch/ppc64/kernel/idle.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/idle.c Wed Aug 15 08:37:55 2001 @@ -31,41 +31,91 @@ #include #include -static void power_save(void) +#include +#include +#include + +unsigned long maxYieldTime = 0; +unsigned long minYieldTime = 0xffffffffffffffffUL; + +static void yield_shared_processor(void) { - /* Implement me */ ; + struct Paca *paca; + unsigned long tb; + unsigned long yieldTime; + + /* Turn off the run light */ + unsigned long CTRL; + CTRL = mfspr(CTRLF); + CTRL &= ~RUNLATCH; + mtspr(CTRLT, CTRL); + + HMT_low(); + + paca = (struct Paca *)mfspr(SPRG3); + HvCall_setEnabledInterrupts( HvCall_MaskIPI | + HvCall_MaskLpEvent | + HvCall_MaskLpProd | + HvCall_MaskTimeout ); + + __cli(); + __sti(); + tb = get_tb(); + /* Compute future tb value when yield should expire */ + HvCall_yieldProcessor( HvCall_YieldTimed, tb+tb_ticks_per_jiffy ); + + yieldTime = get_tb() - tb; + if ( yieldTime > maxYieldTime ) + maxYieldTime = yieldTime; + + if ( yieldTime < minYieldTime ) + minYieldTime = yieldTime; + + /* + * disable/enable will force any pending interrupts + * to be seen. + */ + __cli(); + /* + * The decrementer stops during the yield. Just force + * a fake decrementer now and the timer interrupt code + * will straighten it all out. We have to do this + * while disabled so we don't do it between where it is + * checked and where it is reset. + */ + + paca->xLpPaca.xIntDword.xFields.xDecrInt = 1; + __sti(); } int idled(void) { - int do_power_save = 0; + struct Paca *paca; + long oldval; /* endless loop with no priority at all */ current->nice = 20; current->counter = -100; init_idle(); - for (;;) { -#ifdef CONFIG_SMP - int oldval; + paca = (struct Paca *)mfspr(SPRG3); - if (!do_power_save) { - /* - * Deal with another CPU just having chosen a thread to - * run here: - */ + for (;;) { + + if ( paca->xLpPaca.xSharedProc ) { + if ( !current->need_resched ) + yield_shared_processor(); + } + else { + /* Avoid an IPI by setting need_resched */ oldval = xchg(¤t->need_resched, -1); - if (!oldval) { while(current->need_resched == -1) - ; /* Do Nothing */ + HMT_low(); } } -#endif - if (do_power_save && !current->need_resched) - power_save(); - if (current->need_resched) { + HMT_medium(); schedule(); check_pgt_cache(); } @@ -82,3 +132,5 @@ idled(); return 0; } + + diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/irq.c linuxppc64_2_4/arch/ppc64/kernel/irq.c --- linux-2.4.8-ac9/arch/ppc64/kernel/irq.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/irq.c Mon Aug 13 10:40:21 2001 @@ -526,7 +526,8 @@ { int cpu = smp_processor_id(); int irq; - unsigned long flags; + struct Paca * paca; + struct ItLpQueue * lpq; /* if(cpu) udbg_printf("Entering do_IRQ\n"); */ @@ -556,23 +557,23 @@ } /* if on iSeries partition */ else { + paca = (struct Paca *)mfspr(SPRG3); #ifdef CONFIG_SMP - if ( xPaca[cpu].xLpPacaPtr->xIpiCnt ) { - xPaca[cpu].xLpPacaPtr->xIpiCnt = 0; + if ( paca->xLpPaca.xIntDword.xFields.xIpiCnt ) { + paca->xLpPaca.xIntDword.xFields.xIpiCnt = 0; iSeries_smp_message_recv( regs ); } #endif /* CONFIG_SMP */ - __no_use_save_flags( &flags ); - __no_use_cli(); - lpEvent_count += ItLpQueue_process( &xItLpQueue, regs ); - __no_lpq_restore_flags( flags ); + lpq = paca->lpQueuePtr; + if ( lpq ) + lpEvent_count += ItLpQueue_process( lpq, regs ); } irq_exit(cpu); if ( _machine == _MACH_iSeries ) { - if ( xPaca[cpu].xLpPacaPtr->xDecrInt ) { - xPaca[cpu].xLpPacaPtr->xDecrInt = 0; + if ( paca->xLpPaca.xIntDword.xFields.xDecrInt ) { + paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; /* Signal a fake decrementer interrupt */ timer_interrupt( regs ); } diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/lmb.c linuxppc64_2_4/arch/ppc64/kernel/lmb.c --- linux-2.4.8-ac9/arch/ppc64/kernel/lmb.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/lmb.c Wed Aug 15 15:36:41 2001 @@ -21,7 +21,8 @@ extern unsigned long klimit; extern unsigned long reloc_offset(void); -static long lmb_add_region(struct lmb_region *, unsigned long, unsigned long); + +static long lmb_add_region(struct lmb_region *, unsigned long, unsigned long, unsigned long); struct lmb lmb = { 0, @@ -39,7 +40,9 @@ rgn->region[r1].size += rgn->region[r2].size; for(i=r2; i < rgn->cnt-1 ;i++) { rgn->region[i].base = rgn->region[i+1].base; + rgn->region[i].physbase = rgn->region[i+1].physbase; rgn->region[i].size = rgn->region[i+1].size; + rgn->region[i].type = rgn->region[i+1].type; } rgn->cnt--; } @@ -57,11 +60,13 @@ */ _lmb->memory.region[0].base = 0; _lmb->memory.region[0].size = 0; + _lmb->memory.region[0].type = LMB_MEMORY_AREA; _lmb->memory.cnt = 1; /* Ditto. */ _lmb->reserved.region[0].base = 0; _lmb->reserved.region[0].size = 0; + _lmb->reserved.region[0].type = LMB_MEMORY_AREA; _lmb->reserved.cnt = 1; } @@ -69,14 +74,20 @@ void lmb_analyze(void) { - unsigned long i; - unsigned long size_mask = 0; + unsigned long i, physbase = 0; unsigned long total_size = 0; + unsigned long size_mask = 0; unsigned long offset = reloc_offset(); struct lmb *_lmb = PTRRELOC(&lmb); for(i=0; i < _lmb->memory.cnt ;i++) { unsigned long lmb_size = _lmb->memory.region[i].size; +#ifdef CONFIG_MSCHUNKS + _lmb->memory.region[i].physbase = physbase; + physbase += lmb_size; +#else + _lmb->memory.region[i].physbase = _lmb->memory.region[i].base; +#endif total_size += lmb_size; size_mask |= lmb_size; } @@ -92,7 +103,20 @@ struct lmb *_lmb = PTRRELOC(&lmb); struct lmb_region *_rgn = &(_lmb->memory); - return lmb_add_region(_rgn, base, size); + return lmb_add_region(_rgn, base, size, LMB_MEMORY_AREA); + +} + +/* This routine called with relocation disabled. */ +long +lmb_add_io(unsigned long base, unsigned long size) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_rgn = &(_lmb->memory); + + return lmb_add_region(_rgn, base, size, LMB_IO_AREA); + } long @@ -102,20 +126,27 @@ struct lmb *_lmb = PTRRELOC(&lmb); struct lmb_region *_rgn = &(_lmb->reserved); - return lmb_add_region(_rgn, base, size); + return lmb_add_region(_rgn, base, size, LMB_MEMORY_AREA); } /* This routine called with relocation disabled. */ static long -lmb_add_region(struct lmb_region *rgn, unsigned long base, unsigned long size) +lmb_add_region(struct lmb_region *rgn, unsigned long base, unsigned long size, + unsigned long type) { unsigned long i, coalesced = 0; + long adjacent; /* First try and coalesce this LMB with another. */ for(i=0; i < rgn->cnt ;i++) { unsigned long rgnbase = rgn->region[i].base; unsigned long rgnsize = rgn->region[i].size; - long adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize); + unsigned long rgntype = rgn->region[i].type; + + if ( rgntype != type ) + continue; + + adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize); if ( adjacent > 0 ) { rgn->region[i].base -= size; rgn->region[i].physbase -= size; @@ -147,10 +178,12 @@ rgn->region[i+1].base = rgn->region[i].base; rgn->region[i+1].physbase = rgn->region[i].physbase; rgn->region[i+1].size = rgn->region[i].size; + rgn->region[i+1].type = rgn->region[i].type; } else { rgn->region[i+1].base = base; rgn->region[i+1].physbase = lmb_abs_to_phys(base); rgn->region[i+1].size = size; + rgn->region[i+1].type = type; break; } } @@ -181,15 +214,19 @@ { long i, j; unsigned long base; - unsigned long lmbbase, lmbsize; unsigned long offset = reloc_offset(); struct lmb *_lmb = PTRRELOC(&lmb); struct lmb_region *_mem = &(_lmb->memory); struct lmb_region *_rsv = &(_lmb->reserved); for(i=_mem->cnt-1; i >= 0 ;i--) { - lmbbase = _mem->region[i].base; - lmbsize = _mem->region[i].size; + unsigned long lmbbase = _mem->region[i].base; + unsigned long lmbsize = _mem->region[i].size; + unsigned long lmbtype = _mem->region[i].type; + + if ( lmbtype != LMB_MEMORY_AREA ) + continue; + base = _ALIGN_DOWN(lmbbase+lmbsize-size, align); while ( (lmbbase <= base) && @@ -201,16 +238,28 @@ break; } - if ( i < 0 ) { + if ( i < 0 ) return 0; - } - lmb_add_region(_rsv, base, size); + lmb_add_region(_rsv, base, size, LMB_MEMORY_AREA); return base; } unsigned long +lmb_phys_mem_size(void) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_mem = &(_lmb->memory); + unsigned long idx = _mem->cnt-1; + unsigned long lastbase = _mem->region[idx].physbase; + unsigned long lastsize = _mem->region[idx].size; + + return (lastbase + lastsize); +} + +unsigned long lmb_end_of_DRAM(void) { unsigned long offset = reloc_offset(); @@ -248,4 +297,46 @@ return pa; } +void +lmb_dump(char *str) +{ + unsigned long i; + + udbg_printf("\nlmb_dump: %s\n", str); + udbg_printf(" debug = %s\n", + (lmb.debug) ? "TRUE" : "FALSE"); + udbg_printf(" memory.cnt = %d\n", + lmb.memory.cnt); + udbg_printf(" memory.size = 0x%lx\n", + lmb.memory.size); + udbg_printf(" memory.lcd_size = 0x%lx\n", + lmb.memory.lcd_size); + for(i=0; i < lmb.memory.cnt ;i++) { + udbg_printf(" memory.region[%d].base = 0x%lx\n", + i, lmb.memory.region[i].base); + udbg_printf(" .physbase = 0x%lx\n", + lmb.memory.region[i].physbase); + udbg_printf(" .size = 0x%lx\n", + lmb.memory.region[i].size); + udbg_printf(" .type = 0x%lx\n", + lmb.memory.region[i].type); + } + udbg_printf("\n"); + udbg_printf(" reserved.cnt = %d\n", + lmb.reserved.cnt); + udbg_printf(" reserved.size = 0x%lx\n", + lmb.reserved.size); + udbg_printf(" reserved.lcd_size = 0x%lx\n", + lmb.reserved.lcd_size); + for(i=0; i < lmb.reserved.cnt ;i++) { + udbg_printf(" reserved.region[%d].base = 0x%lx\n", + i, lmb.reserved.region[i].base); + udbg_printf(" .physbase = 0x%lx\n", + lmb.reserved.region[i].physbase); + udbg_printf(" .size = 0x%lx\n", + lmb.reserved.region[i].size); + udbg_printf(" .type = 0x%lx\n", + lmb.reserved.region[i].type); + } +} diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/mf.c linuxppc64_2_4/arch/ppc64/kernel/mf.c --- linux-2.4.8-ac9/arch/ppc64/kernel/mf.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/mf.c Wed Aug 15 08:38:20 2001 @@ -1019,45 +1019,6 @@ int mf_setRtcTime(unsigned long time) { -/* - char ceTime[12] = "\x00\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00"; - int rc = 0; - struct rtc_time tm; - u16 year; - u8 day, mon, hour, min, sec, y1, y2; - - to_tm(time, &tm); - - year = tm.tm_year; - y1 = (year >> 8) & 0x00FF; - y2 = year & 0x00FF; - - BIN_TO_BCD(tm.tm_sec); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_mon); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(y1); - BIN_TO_BCD(y2); - - year = y1 * 100 + y2; - day = tm.tm_mday; - mon = tm.tm_mon; - hour = tm.tm_hour; - min = tm.tm_min; - sec = tm.tm_sec; - - memcpy(ceTime + 4, &year, 2); - memcpy(ceTime + 6, &sec, 1); - memcpy(ceTime + 7, &min, 1); - memcpy(ceTime + 8, &hour, 1); - memcpy(ceTime + 10, &day, 1); - memcpy(ceTime + 11, &mon, 1); - - rc = signalCEMsg( ceTime, NULL ); - - return rc; -*/ struct rtc_time tm; to_tm(time, &tm); @@ -1067,7 +1028,7 @@ struct RtcTimeData { - volatile int xDone; + struct semaphore *xSemaphore; struct CeMsgData xCeMsg; int xRc; }; @@ -1079,7 +1040,7 @@ memcpy(&(rtc->xCeMsg), ceMsg, sizeof(rtc->xCeMsg)); rtc->xRc = 0; - rtc->xDone = 1; + up(rtc->xSemaphore); } static unsigned long lastsec = 1; @@ -1129,14 +1090,16 @@ int mf_getRtc( struct rtc_time * tm ) { - struct CeMsgCompleteData ceComplete; struct RtcTimeData rtcData; int rc = 0; + DECLARE_MUTEX_LOCKED(Semaphore); memset(&ceComplete, 0, sizeof(ceComplete)); memset(&rtcData, 0, sizeof(rtcData)); + rtcData.xSemaphore = &Semaphore; + ceComplete.xHdlr = &getRtcTimeComplete; ceComplete.xToken = (void *)&rtcData; @@ -1144,13 +1107,22 @@ if ( rc == 0 ) { - while( rtcData.xDone != 1 ) - { - udelay(10); - } + down(&Semaphore); if ( rtcData.xRc == 0) { + if ( ( rtcData.xCeMsg.xCEMsg[2] == 0xa9 ) || + ( rtcData.xCeMsg.xCEMsg[2] == 0xaf ) ) { + /* TOD clock is not set */ + tm->tm_sec = 1; + tm->tm_min = 1; + tm->tm_hour = 1; + tm->tm_mday = 10; + tm->tm_mon = 8; + tm->tm_year = 71; + mf_setRtc( tm ); + } + { u32 dataWord1 = *((u32 *)(rtcData.xCeMsg.xCEMsg+4)); u32 dataWord2 = *((u32 *)(rtcData.xCeMsg.xCEMsg+8)); u8 year = (dataWord1 >> 16 ) & 0x000000FF; @@ -1176,7 +1148,7 @@ tm->tm_mday = day; tm->tm_mon = mon; tm->tm_year = year; - + } } else { @@ -1189,6 +1161,10 @@ tm->tm_year = 52; } + tm->tm_wday = 0; + tm->tm_yday = 0; + tm->tm_isdst = 0; + } return rc; diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/misc.S linuxppc64_2_4/arch/ppc64/kernel/misc.S --- linux-2.4.8-ac9/arch/ppc64/kernel/misc.S Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/misc.S Tue Aug 14 23:24:17 2001 @@ -101,24 +101,22 @@ /* are we enabling interrupts? */ rlwinm. r0,r3,0,16,16 beq 1f - /* if so, check if there are any lost interrupts */ - LOADBASE(r7,ppc_n_lost_interrupts) - lwz r7,ppc_n_lost_interrupts@l(r7) - cmpi 0,r7,0 /* lost interrupts to process first? */ - bne- .do_lost_interrupts - /* Check for lost decrementer interrupts. - * (If decrementer popped while we were in the hypervisor) - * (calls timer_interrupt if so) + /* Check pending interrupts + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) */ - CHECKDECR(r4,r5) - bne- .do_fake_decrementer - /* Check for pending I/O Events. If no I/O events pending, - * then CR0 = "eq" and r4 == 0 - * (kills registers r5 and r6) + CHECKANYINT(r4,r5) + beq+ 1f + + /* + * Handle pending interrupts in interrupt context */ - CHECKLPQUEUE(r4,r5,r6) - bne- .do_lost_interrupts -1: sync + mtmsrd r3 + li r0,0x5555 + sc + blr +1: + sync mtmsrd r3 isync blr @@ -150,23 +148,22 @@ _GLOBAL(__no_use_sti) mfmsr r3 /* Get current state */ ori r3,r3,MSR_EE /* Turn on 'EE' bit */ - LOADBASE(r4,ppc_n_lost_interrupts) - lwz r4,ppc_n_lost_interrupts@l(r4) - cmpi 0,r4,0 /* lost interrupts to process first? */ - bne- .do_lost_interrupts - /* Check for lost decrementer interrupts. - * (If decrementer popped while we were in the hypervisor) - * (calls timer_interrupt if so) + /* Check for pending interrupts + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) */ - CHECKDECR(r4,r5) - bne- .do_fake_decrementer - /* Check for pending I/O events. If no I/O events pending, - * then CR0 = "eq" and R4 = 0 - * (kills registers r5 and r6) + CHECKANYINT(r4,r5) + beq+ 1f + + /* + * Handle pending interrupts in interrupt context */ - CHECKLPQUEUE(r4,r5,r6) - bne- .do_lost_interrupts + mtmsrd r3 + li r0,0x5555 + sc + blr +1: sync /* Some chip revs have problems here... */ mtmsrd r3 /* Update machine state */ blr @@ -175,21 +172,17 @@ * We were about to enable interrupts but we have to simulate * some interrupts that were lost by enable_irq first. */ -_GLOBAL(do_lost_interrupts) +_GLOBAL(do_fake_interrupt) mflr r0 std r0,16(r1) std r3,-8(r1) stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1) 1: bl .fake_interrupt - LOADBASE(r4,ppc_n_lost_interrupts) - lwz r4,ppc_n_lost_interrupts@l(r4) - cmpi 0,r4,0 - bne- 1b - /* Check for pending I/O events. If no I/O events pending, - * then CR0 = "eq" and R4 = 0 - * (kills registers r5 and r6) + /* Check for pending interrupt + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) */ - CHECKLPQUEUE(r4,r5,r6) + CHECKANYINT(r4,r5) bne- 1b addi r1,r1,STACK_FRAME_OVERHEAD+16 ld r3,-8(r1) @@ -198,6 +191,7 @@ mtmsrd r3 blr +#if 0 _GLOBAL(do_fake_decrementer) mflr r0 std r0,16(r1) @@ -212,6 +206,7 @@ bne- .do_lost_interrupts mtmsrd r3 blr +#endif /* * complement mask on the msr then "or" some values on. @@ -252,7 +247,6 @@ /* * Write any modified data cache blocks out to memory * and invalidate the corresponding instruction cache blocks. - * This is a no-op on the 601. * * flush_icache_range(unsigned long start, unsigned long stop) * diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/mk_defs.c linuxppc64_2_4/arch/ppc64/kernel/mk_defs.c --- linux-2.4.8-ac9/arch/ppc64/kernel/mk_defs.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/mk_defs.c Mon Aug 13 10:16:24 2001 @@ -93,6 +93,7 @@ DEFINE(PACAR1, offsetof(struct Paca, xR1)); DEFINE(PACAR21, offsetof(struct Paca, xR21)); DEFINE(PACAR22, offsetof(struct Paca, xR22)); + DEFINE(PACACCR, offsetof(struct Paca, xCCR)); DEFINE(PACALPQUEUE, offsetof(struct Paca, lpQueuePtr)); DEFINE(PACALPPACA, offsetof(struct Paca, xLpPaca)); DEFINE(PACATOC, offsetof(struct Paca, xTOC)); @@ -100,8 +101,9 @@ DEFINE(LPREGSAV, offsetof(struct Paca, xRegSav)); DEFINE(LPPACASRR0, offsetof(struct ItLpPaca, xSavedSrr0)); DEFINE(LPPACASRR1, offsetof(struct ItLpPaca, xSavedSrr1)); - DEFINE(LPPACADECRINT, offsetof(struct ItLpPaca, xDecrInt)); - DEFINE(LPPACAIPIINT, offsetof(struct ItLpPaca, xIpiCnt)); + DEFINE(LPPACAANYINT, offsetof(struct ItLpPaca, xIntDword.xAnyInt)); + DEFINE(LPPACADECRINT, offsetof(struct ItLpPaca, xIntDword.xFields.xDecrInt)); + DEFINE(LPPACAIPIINT, offsetof(struct ItLpPaca, xIntDword.xFields.xIpiCnt)); DEFINE(LPQCUREVENTPTR, offsetof(struct ItLpQueue, xSlicCurEventPtr)); DEFINE(LPQOVERFLOW, offsetof(struct ItLpQueue, xPlicOverflowIntPending)); DEFINE(LPEVENTFLAGS, offsetof(struct HvLpEvent, xFlags)); diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/pacaData.c linuxppc64_2_4/arch/ppc64/kernel/pacaData.c --- linux-2.4.8-ac9/arch/ppc64/kernel/pacaData.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pacaData.c Tue Aug 21 14:49:35 2001 @@ -39,8 +39,7 @@ 0, /* Kernel stack addr save */ \ (number), /* Paca Index */ \ 0, /* HW Proc Number */ \ - (start), /* Processor start */ \ - {0,0,0}, /* Resv */ \ + 0, /* CCR Save */ \ 0, /* MSR Save */ \ 0, /* LR Save */ \ 0, /* Pointer to thread */ \ @@ -52,21 +51,33 @@ 0, /* R1 Save */ \ (lpq), /* &xItLpQueue, */ \ {0,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, SPIN_LOCK_UNLOCKED, 0}, /*xRtas */ \ - {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, /* Resv */ \ + (start), /* Processor start */ \ + {0,0,0,0,0,0,0}, /* Resv */ \ + 0, /* next_jiffy_update_tb */ \ + {0,0,0,0,0,0,0,0,0,0,0,0}, /* Resv */ \ { /* LpPaca */ \ xDesc: 0xd397d781, /* "LpPa" */ \ xSize: sizeof(struct ItLpPaca), /* */ \ + xFPRegsInUse: 1, \ + xDynProcStatus: 2, \ xEndOfQuantum: 0xffffffffffffffff /* */ \ }, \ { /* LpRegSave */ \ 0xd397d9e2, /* "LpRS" */ \ sizeof(struct ItLpRegSave) /* */ \ - } \ + }, \ + 0, /* pvr */ \ + 0, /* pgd_cache */ \ + 0, /* pmd_cache */ \ + 0, /* pte_cache */ \ + 0, /* pgtable_cache_sz */ \ + 0, /* prof_multiplier */ \ + 0 /* prof_counter */ \ } -struct Paca xPaca[maxPacas] = { +struct Paca xPaca[maxPacas] __page_aligned = { #ifdef CONFIG_PPC_ISERIES - PACAINITDATA( 0, 1, &xItLpQueue, 0x5000, 0xc000000000005000), + PACAINITDATA( 0, 1, &xItLpQueue, 0, 0xc000000000005000), #else PACAINITDATA( 0, 1, 0, 0x5000, 0xc000000000005000), #endif diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/pmc.c linuxppc64_2_4/arch/ppc64/kernel/pmc.c --- linux-2.4.8-ac9/arch/ppc64/kernel/pmc.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pmc.c Tue Aug 14 14:55:53 2001 @@ -22,10 +22,7 @@ * End Change Activity */ -#ifndef _PMC_PROC_H -#include -#endif - +#include #include #include #include diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/pmc_proc.c linuxppc64_2_4/arch/ppc64/kernel/pmc_proc.c --- linux-2.4.8-ac9/arch/ppc64/kernel/pmc_proc.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pmc_proc.c Wed Dec 31 18:00:00 1969 @@ -1,650 +0,0 @@ -/* - * pmc_proc.c - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -/* Change Activity: */ -/* End Change Activity */ - -#ifndef _PMC_PROC_H -#include -#endif - -#include -#include -#include -#include - -#define MMCR0 795 -#define MMCR1 798 -#define MMCRA 786 -#define PMC1 787 -#define PMC2 788 -#define PMC3 789 -#define PMC4 790 -#define PMC5 791 -#define PMC6 792 -#define PMC7 793 -#define PMC8 794 - -static int proc_pmc_control_mode = 0; -#define PMC_CONTROL_CPI 1 -#define PMC_CONTROL_TLB 2 - -static struct proc_dir_entry *pmc_proc_root = NULL; - -int proc_get_lpevents( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data); - -int proc_pmc_get_control( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_mmcr0( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_mmcr1( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_mmcra( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc1( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc2( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc3( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc4( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc5( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc6( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc7( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc8( char *page, char **start, off_t off, int count, int *eof, void *data); - -int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data); - -void pmc_proc_init(struct proc_dir_entry *iSeries_proc) -{ - struct proc_dir_entry *ent = NULL; - - ent = create_proc_entry("lpevents", S_IFREG|S_IRUGO, iSeries_proc); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_get_lpevents; - ent->write_proc = proc_reset_lpevents; - - pmc_proc_root = proc_mkdir("pmc", iSeries_proc); - if (!pmc_proc_root) return; - - ent = create_proc_entry("control", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_control; - ent->write_proc = proc_pmc_set_control; - - ent = create_proc_entry("mmcr0", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_mmcr0; - ent->write_proc = proc_pmc_set_mmcr0; - - ent = create_proc_entry("mmcr1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_mmcr1; - ent->write_proc = proc_pmc_set_mmcr1; - - ent = create_proc_entry("mmcra", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_mmcra; - ent->write_proc = proc_pmc_set_mmcra; - - ent = create_proc_entry("pmc1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc1; - ent->write_proc = proc_pmc_set_pmc1; - - ent = create_proc_entry("pmc2", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc2; - ent->write_proc = proc_pmc_set_pmc2; - - ent = create_proc_entry("pmc3", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc3; - ent->write_proc = proc_pmc_set_pmc3; - - ent = create_proc_entry("pmc4", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc4; - ent->write_proc = proc_pmc_set_pmc4; - - ent = create_proc_entry("pmc5", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc5; - ent->write_proc = proc_pmc_set_pmc5; - - ent = create_proc_entry("pmc6", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc6; - ent->write_proc = proc_pmc_set_pmc6; - - ent = create_proc_entry("pmc7", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc7; - ent->write_proc = proc_pmc_set_pmc7; - - ent = create_proc_entry("pmc8", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc8; - ent->write_proc = proc_pmc_set_pmc8; - - -} - -static int pmc_calc_metrics( char *page, char **start, off_t off, int count, int *eof, int len) -{ - if ( len <= off+count) - *eof = 1; - *start = page+off; - len -= off; - if ( len > count ) - len = count; - if ( len < 0 ) - len = 0; - return len; -} - -static char * lpEventTypes[9] = { - "Hypervisor\t\t", - "Machine Facilities\t", - "Session Manager\t", - "SPD I/O\t\t", - "Virtual Bus\t\t", - "PCI I/O\t\t", - "RIO I/O\t\t", - "Virtual Lan\t\t", - "Virtual I/O\t\t" - }; - - -int proc_get_lpevents -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - unsigned i; - int len = 0; - - len += sprintf( page+len, "LpEventQueue 0\n" ); - len += sprintf( page+len, " events processed:\t%lu\n", - (unsigned long)xItLpQueue.xLpIntCount ); - for (i=0; i<9; ++i) { - len += sprintf( page+len, " %s %10lu\n", - lpEventTypes[i], - (unsigned long)xItLpQueue.xLpIntCountByType[i] ); - } - return pmc_calc_metrics( page, start, off, count, eof, len ); - -} - -int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - return count; -} - -int proc_pmc_get_control -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = 0; - - if ( proc_pmc_control_mode == PMC_CONTROL_CPI ) { - unsigned long mach_cycles = mfspr( PMC5 ); - unsigned long inst_complete = mfspr( PMC4 ); - unsigned long inst_dispatch = mfspr( PMC3 ); - unsigned long thread_active_run = mfspr( PMC1 ); - unsigned long thread_active = mfspr( PMC2 ); - unsigned long cpi = 0; - unsigned long cpithou = 0; - unsigned long remain; - - if ( inst_complete ) { - cpi = thread_active_run / inst_complete; - remain = thread_active_run % inst_complete; - if ( inst_complete > 1000000 ) - cpithou = remain / ( inst_complete / 1000 ); - else - cpithou = ( remain * 1000 ) / inst_complete; - } - len += sprintf( page+len, "PMC CPI Mode\nRaw Counts\n" ); - len += sprintf( page+len, "machine cycles : %12lu\n", mach_cycles ); - len += sprintf( page+len, "thread active cycles : %12lu\n\n", thread_active ); - - len += sprintf( page+len, "instructions completed : %12lu\n", inst_complete ); - len += sprintf( page+len, "instructions dispatched : %12lu\n", inst_dispatch ); - len += sprintf( page+len, "thread active run cycles : %12lu\n", thread_active_run ); - - len += sprintf( page+len, "thread active run cycles/instructions completed\n" ); - len += sprintf( page+len, "CPI = %lu.%03lu\n", cpi, cpithou ); - - } - else if ( proc_pmc_control_mode == PMC_CONTROL_TLB ) { - len += sprintf( page+len, "PMC TLB Mode\n" ); - len += sprintf( page+len, "I-miss count : %12lu\n", mfspr( PMC1 ) ); - len += sprintf( page+len, "I-miss latency : %12lu\n", mfspr( PMC2 ) ); - len += sprintf( page+len, "D-miss count : %12lu\n", mfspr( PMC3 ) ); - len += sprintf( page+len, "D-miss latency : %12lu\n", mfspr( PMC4 ) ); - len += sprintf( page+len, "IERAT miss count : %12lu\n", mfspr( PMC5 ) ); - len += sprintf( page+len, "D-reference count : %12lu\n", mfspr( PMC6 ) ); - len += sprintf( page+len, "miss PTEs searched : %12lu\n", mfspr( PMC7 ) ); - len += sprintf( page+len, "miss >8 PTEs searched : %12lu\n", mfspr( PMC8 ) ); - } - /* IMPLEMENT ME */ - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_mmcr0 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(MMCR0) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_mmcr1 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(MMCR1) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_mmcra -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(MMCRA) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc1 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC1) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc2 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC2) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc3 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC3) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc4 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC4) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc5 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC5) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc6 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC6) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc7 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC7) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc8 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC8) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -unsigned long proc_pmc_conv_int( const char *buf, unsigned count ) -{ - const char * p; - char b0, b1; - unsigned v, multiplier, mult, i; - unsigned long val; - multiplier = 10; - p = buf; - if ( count >= 3 ) { - b0 = buf[0]; - b1 = buf[1]; - if ( ( b0 == '0' ) && - ( ( b1 == 'x' ) || ( b1 == 'X' ) ) ) { - p = buf + 2; - count -= 2; - multiplier = 16; - } - - } - val = 0; - for ( i=0; i= '0' ) && ( b0 <= '9' ) ) - v = b0 - '0'; - else if ( multiplier == 16 ) { - if ( ( b0 >= 'a' ) && ( b0 <= 'f' ) ) - v = b0 - 'a' + 10; - else if ( ( b0 >= 'A' ) && ( b0 <= 'F' ) ) - v = b0 - 'A' + 10; - else - mult = 1; - } - else - mult = 1; - val *= mult; - val += v; - } - - return val; - -} - -static inline void proc_pmc_stop(void) -{ - /* Freeze all counters, leave everything else alone */ - mtspr( MMCR0, mfspr( MMCR0 ) | 0x80000000 ); -} - -static inline void proc_pmc_start(void) -{ - /* Unfreeze all counters, leave everything else alone */ - mtspr( MMCR0, mfspr( MMCR0 ) & ~0x80000000 ); - -} - -static inline void proc_pmc_reset(void) -{ - /* Clear all the PMCs to zeros - * Assume a "stop" has already frozen the counters - * Clear all the PMCs - */ - mtspr( PMC1, 0 ); - mtspr( PMC2, 0 ); - mtspr( PMC3, 0 ); - mtspr( PMC4, 0 ); - mtspr( PMC5, 0 ); - mtspr( PMC6, 0 ); - mtspr( PMC7, 0 ); - mtspr( PMC8, 0 ); - -} - -static inline void proc_pmc_cpi(void) -{ - /* Configure the PMC registers to count cycles and instructions */ - /* so we can compute cpi */ - /* - * MMCRA[30] = 1 Don't count in wait state (CTRL[31]=0) - * MMCR0[6] = 1 Freeze counters when any overflow - * MMCR0[19:25] = 0x01 PMC1 counts Thread Active Run Cycles - * MMCR0[26:31] = 0x05 PMC2 counts Thread Active Cycles - * MMCR1[0:4] = 0x07 PMC3 counts Instructions Dispatched - * MMCR1[5:9] = 0x03 PMC4 counts Instructions Completed - * MMCR1[10:14] = 0x06 PMC5 counts Machine Cycles - * - */ - - proc_pmc_control_mode = PMC_CONTROL_CPI; - - // Indicate to hypervisor that we are using the PMCs - ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; - - // Freeze all counters - mtspr( MMCR0, 0x80000000 ); - mtspr( MMCR1, 0x00000000 ); - - // Clear all the PMCs - mtspr( PMC1, 0 ); - mtspr( PMC2, 0 ); - mtspr( PMC3, 0 ); - mtspr( PMC4, 0 ); - mtspr( PMC5, 0 ); - mtspr( PMC6, 0 ); - mtspr( PMC7, 0 ); - mtspr( PMC8, 0 ); - - /* Freeze counters in Wait State (CTRL[31]=0) */ - mtspr( MMCRA, 0x00000002 ); - - /* PMC3<-0x07, PMC4<-0x03, PMC5<-0x06 */ - mtspr( MMCR1, 0x38cc0000 ); - - mb(); - - /* PMC1<-0x01, PMC2<-0x05 - * Start all counters - */ - mtspr( MMCR0, 0x02000045 ); - -} - -static inline void proc_pmc_tlb(void) -{ - /* Configure the PMC registers to count tlb misses */ - /* - * MMCR0[6] = 1 Freeze counters when any overflow - * MMCR0[19:25] = 0x55 Group count - * PMC1 counts I misses - * PMC2 counts I miss duration (latency) - * PMC3 counts D misses - * PMC4 counts D miss duration (latency) - * PMC5 counts IERAT misses - * PMC6 counts D references (including PMC7) - * PMC7 counts miss PTEs searched - * PMC8 counts miss >8 PTEs searched - * - */ - - proc_pmc_control_mode = PMC_CONTROL_TLB; - - /* Indicate to hypervisor that we are using the PMCs */ - ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; - - /* Freeze all counters */ - mtspr( MMCR0, 0x80000000 ); - mtspr( MMCR1, 0x00000000 ); - - /* Clear all the PMCs */ - mtspr( PMC1, 0 ); - mtspr( PMC2, 0 ); - mtspr( PMC3, 0 ); - mtspr( PMC4, 0 ); - mtspr( PMC5, 0 ); - mtspr( PMC6, 0 ); - mtspr( PMC7, 0 ); - mtspr( PMC8, 0 ); - - mtspr( MMCRA, 0x00000000 ); - - mb(); - - /* PMC1<-0x55 - * Start all counters - */ - mtspr( MMCR0, 0x02001540 ); - -} - -int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - if ( ! strncmp( buffer, "stop", 4 ) ) - proc_pmc_stop(); - else if ( ! strncmp( buffer, "start", 5 ) ) - proc_pmc_start(); - else if ( ! strncmp( buffer, "reset", 5 ) ) - proc_pmc_reset(); - else if ( ! strncmp( buffer, "cpi", 3 ) ) - proc_pmc_cpi(); - else if ( ! strncmp( buffer, "tlb", 3 ) ) - proc_pmc_tlb(); - - /* IMPLEMENT ME */ - return count; -} - -int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - v = v & ~0x04000000; /* Don't allow interrupts for now */ - if ( v & ~0x80000000 ) /* Inform hypervisor we are using PMCs */ - ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; - else - ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 0; - mtspr( MMCR0, v ); - - return count; -} - -int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( MMCR1, v ); - - return count; -} - -int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - v = v & ~0x00008000; /* Don't allow interrupts for now */ - mtspr( MMCRA, v ); - - return count; -} - - -int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC1, v ); - - return count; -} - -int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC2, v ); - - return count; -} - -int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC3, v ); - - return count; -} - -int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC4, v ); - - return count; -} - -int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC5, v ); - - return count; -} - -int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC6, v ); - - return count; -} - -int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC7, v ); - - return count; -} - -int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC8, v ); - - return count; -} - - diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/ppc_asm.h linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.h --- linux-2.4.8-ac9/arch/ppc64/kernel/ppc_asm.h Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.h Mon Aug 13 10:14:50 2001 @@ -72,6 +72,10 @@ stb ra,PACALPPACA+LPPACADECRINT(rb); /* Clear DECR int flag */\ 99: +#define CHECKANYINT(ra,rb) \ + mfspr rb,SPRG3; /* Get Paca address */\ + ld ra,PACALPPACA+LPPACAANYINT(rb); /* Get pending interrupt flags */\ + cmpldi 0,ra,0; /* Macros to adjust thread priority for Iseries hardware multithreading */ #define HMT_LOW or 1,1,1 diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/ppc_defs.h linuxppc64_2_4/arch/ppc64/kernel/ppc_defs.h --- linux-2.4.8-ac9/arch/ppc64/kernel/ppc_defs.h Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_defs.h Wed Dec 31 18:00:00 1969 @@ -1,116 +0,0 @@ -/* The source for this file is automatically generated. Do not edit! */ -#define STATE 0 -#define NEXT_TASK 136 -#define COUNTER 64 -#define PROCESSOR 100 -#define SIGPENDING 16 -#define THREAD 1448 -#define MM 88 -#define ACTIVE_MM 152 -#define TASK_STRUCT_SIZE 1904 -#define KSP 0 -#define PACA 24 -#define PACA_SIZE 2080 -#define RTAS_ARGS_SIZE 88 -#define ITLPPACA_SIZE 640 -#define ITLPREGSAVE_SIZE 1024 -#define DCACHEL1LINESIZE 42 -#define DCACHEL1LOGLINESIZE 44 -#define DCACHEL1LINESPERPAGE 46 -#define ICACHEL1LINESIZE 48 -#define ICACHEL1LOGLINESIZE 50 -#define ICACHEL1LINESPERPAGE 52 -#define PHYSICALMEMORYSIZE 56 -#define PACAPACAINDEX 48 -#define PACAPROCSTART 52 -#define PACAKSAVE 40 -#define PACATHREAD 72 -#define PACACURRENT 16 -#define PACASAVEDMSR 56 -#define PACASAVEDLR 64 -#define PACASTABREAL 80 -#define PACASTABVIRT 88 -#define PACAR1 120 -#define PACAR21 24 -#define PACAR22 32 -#define PACALPQUEUE 128 -#define PACALPPACA 376 -#define PACATOC 112 -#define LPPACA 376 -#define LPREGSAV 1016 -#define LPPACASRR0 144 -#define LPPACASRR1 152 -#define LPPACADECRINT 132 -#define LPPACAIPIINT 131 -#define LPQCUREVENTPTR 16 -#define LPQOVERFLOW 0 -#define LPEVENTFLAGS 0 -#define PROMENTRY 0 -#define PROMARGS 32 -#define RTASBASE 8 -#define RTASENTRY 0 -#define RTASSIZE 16 -#define PGDIR 24 -#define LAST_SYSCALL 32 -#define PT_REGS 8 -#define PT_TRACESYS 2 -#define TASK_FLAGS 8 -#define TASK_PTRACE 48 -#define TASK_PERSONALITY 184 -#define NEED_RESCHED 40 -#define THREAD_FPR0 52 -#define THREAD_FPSCR 320 -#define THREAD_FLAGS 40 -#define PPC_FLAG_32BIT 1 -#define TASK_UNION_SIZE 16384 -#define STACK_FRAME_OVERHEAD 112 -#define INT_FRAME_SIZE 752 -#define GPR0 112 -#define GPR1 120 -#define GPR2 128 -#define GPR3 136 -#define GPR4 144 -#define GPR5 152 -#define GPR6 160 -#define GPR7 168 -#define GPR8 176 -#define GPR9 184 -#define GPR10 192 -#define GPR11 200 -#define GPR12 208 -#define GPR13 216 -#define GPR14 224 -#define GPR15 232 -#define GPR16 240 -#define GPR17 248 -#define GPR18 256 -#define GPR19 264 -#define GPR20 272 -#define GPR21 280 -#define GPR22 288 -#define GPR23 296 -#define GPR24 304 -#define GPR25 312 -#define GPR26 320 -#define GPR27 328 -#define GPR28 336 -#define GPR29 344 -#define GPR30 352 -#define GPR31 360 -#define _NIP 368 -#define _MSR 376 -#define _CTR 392 -#define _LINK 400 -#define _CCR 416 -#define _MQ 424 -#define _XER 408 -#define _DAR 440 -#define _DSISR 448 -#define _DEAR 440 -#define _ESR 448 -#define ORIG_GPR3 384 -#define RESULT 456 -#define TRAP 432 -#define CLONE_VM 256 -#define IRQ_CPUSTAT_SHIFT 7 -#define SOFTIRQ_PENDING 0 diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/ppc_ksyms.c linuxppc64_2_4/arch/ppc64/kernel/ppc_ksyms.c --- linux-2.4.8-ac9/arch/ppc64/kernel/ppc_ksyms.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_ksyms.c Tue Aug 21 22:52:27 2001 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -45,7 +46,6 @@ #ifdef CONFIG_SMP #include #endif /* CONFIG_SMP */ -//#include /* Tell string.h we don't want memcpy etc. as cpp defines */ #define EXPORT_SYMTAB_STROPS @@ -59,7 +59,6 @@ extern void ProgramCheckException(struct pt_regs *regs); extern void SingleStepException(struct pt_regs *regs); extern int sys_sigreturn(struct pt_regs *regs); -extern void do_lost_interrupts(unsigned long); extern int do_signal(sigset_t *, struct pt_regs *); long long __ashrdi3(long long, int); @@ -68,7 +67,9 @@ int abs(int); extern unsigned long ret_to_user_hook; +#if 0 /* Add back in when we fix clear_page */ EXPORT_SYMBOL(clear_page); +#endif EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(syscall_trace); EXPORT_SYMBOL(transfer_to_handler); @@ -79,25 +80,17 @@ EXPORT_SYMBOL(ProgramCheckException); EXPORT_SYMBOL(SingleStepException); EXPORT_SYMBOL(sys_sigreturn); -EXPORT_SYMBOL(ppc_n_lost_interrupts); -EXPORT_SYMBOL(ppc_lost_interrupts); -EXPORT_SYMBOL(do_lost_interrupts); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(disable_irq_nosync); #ifdef CONFIG_SMP EXPORT_SYMBOL(kernel_flag); +EXPORT_SYMBOL(synchronize_irq); #endif /* CONFIG_SMP */ EXPORT_SYMBOL(isa_io_base); EXPORT_SYMBOL(isa_mem_base); EXPORT_SYMBOL(pci_dram_offset); -EXPORT_SYMBOL(DMA_MODE_READ); -EXPORT_SYMBOL(DMA_MODE_WRITE); -#if defined(CONFIG_ALL_PPC) -EXPORT_SYMBOL(_prep_type); -EXPORT_SYMBOL(ucSystemType); -#endif #if !__INLINE_BITOPS EXPORT_SYMBOL(set_bit); @@ -168,6 +161,10 @@ #ifdef CONFIG_PCI EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); +EXPORT_SYMBOL(pci_map_single); +EXPORT_SYMBOL(pci_unmap_single); +EXPORT_SYMBOL(pci_map_sg); +EXPORT_SYMBOL(pci_unmap_sg); #endif /* CONFIG_PCI */ EXPORT_SYMBOL(start_thread); @@ -178,19 +175,11 @@ EXPORT_SYMBOL(giveup_fpu); EXPORT_SYMBOL(enable_kernel_fp); EXPORT_SYMBOL(flush_icache_range); -EXPORT_SYMBOL(xchg_u32); #ifdef CONFIG_SMP EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); EXPORT_SYMBOL(__global_save_flags); EXPORT_SYMBOL(__global_restore_flags); -EXPORT_SYMBOL(_spin_lock); -EXPORT_SYMBOL(_spin_unlock); -EXPORT_SYMBOL(spin_trylock); -EXPORT_SYMBOL(_read_lock); -EXPORT_SYMBOL(_read_unlock); -EXPORT_SYMBOL(_write_lock); -EXPORT_SYMBOL(_write_unlock); #endif #ifndef CONFIG_MACH_SPECIFIC @@ -198,7 +187,6 @@ #endif EXPORT_SYMBOL(ppc_md); -#if defined(CONFIG_ALL_PPC) EXPORT_SYMBOL(find_devices); EXPORT_SYMBOL(find_type_devices); EXPORT_SYMBOL(find_compatible_devices); @@ -209,12 +197,7 @@ EXPORT_SYMBOL(find_pci_device_OFnode); EXPORT_SYMBOL(find_all_nodes); EXPORT_SYMBOL(get_property); -EXPORT_SYMBOL(pci_io_base); -EXPORT_SYMBOL(pci_device_loc); -#endif /* defined(CONFIG_ALL_PPC) */ -#if defined(CONFIG_SCSI) && defined(CONFIG_ALL_PPC) -EXPORT_SYMBOL(note_scsi_host); -#endif + EXPORT_SYMBOL(kd_mksound); EXPORT_SYMBOL_NOVERS(sys_ctrler); /* tibit */ #ifdef CONFIG_NVRAM @@ -237,21 +220,15 @@ EXPORT_SYMBOL(screen_info); #endif -EXPORT_SYMBOL(int_control); -EXPORT_SYMBOL(timer_interrupt_intercept); EXPORT_SYMBOL(timer_interrupt); -EXPORT_SYMBOL(do_IRQ_intercept); EXPORT_SYMBOL(irq_desc); void ppc_irq_dispatch_handler(struct pt_regs *, int); EXPORT_SYMBOL(ppc_irq_dispatch_handler); -EXPORT_SYMBOL(decrementer_count); EXPORT_SYMBOL(get_wchan); EXPORT_SYMBOL(console_drivers); -EXPORT_SYMBOL(console_lock); #ifdef CONFIG_XMON EXPORT_SYMBOL(xmon); #endif -EXPORT_SYMBOL(down_read_failed); #if defined(CONFIG_KGDB) || defined(CONFIG_XMON) extern void (*debugger)(struct pt_regs *regs); @@ -270,4 +247,5 @@ #endif EXPORT_SYMBOL(ret_to_user_hook); -EXPORT_SYMBOL(do_softirq); + +EXPORT_SYMBOL(tb_ticks_per_usec); diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/proc_pmc.c linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c --- linux-2.4.8-ac9/arch/ppc64/kernel/proc_pmc.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c Tue Aug 14 14:55:53 2001 @@ -25,10 +25,7 @@ * End Change Activity */ -#ifndef _PMC_PROC_H -#include -#endif - +#include #include #include #include diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/prom.c linuxppc64_2_4/arch/ppc64/kernel/prom.c --- linux-2.4.8-ac9/arch/ppc64/kernel/prom.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/prom.c Wed Aug 15 15:36:50 2001 @@ -425,7 +425,7 @@ if ( (_naca->processorCount = num_cpus) < 1 ) PROM_BUG(); - _naca->physicalMemorySize = _lmb->memory.size; + _naca->physicalMemorySize = lmb_phys_mem_size(); /* * Hardcode to GP size. I am not sure where to get this info @@ -491,9 +491,20 @@ unsigned long i, offset = reloc_offset(); struct prom_t *_prom = PTRRELOC(&prom); union lmb_reg_property reg; - unsigned long lmb_base, lmb_size; + unsigned long mem_size, lmb_base, lmb_size; unsigned long num_regs, bytes_per_reg = (_prom->encode_phys_size*2)/8; +#if 1 + /* Fix me: 630 3G-4G IO hack here... -Peter (PPPBBB) */ + unsigned long io_base = 3UL<<30; + unsigned long io_size = 1UL<<30; + unsigned long have_630 = 1; /* assume we have a 630 */ + +#else + unsigned long io_base = ; + unsigned long io_size = ; +#endif + lmb_init(); for (node = 0; prom_next_node(&node); ) { @@ -516,11 +527,23 @@ lmb_size = reg.addr64[i].size; } + if ( lmb_addrs_overlap(lmb_base,lmb_size, + io_base,io_size) ) { + /* If we really have dram here, then we don't + * have a 630! -Peter + */ + have_630 = 0; + } if ( lmb_add(lmb_base, lmb_size) < 0 ) prom_print(RELOC("Too many LMB's, discarding this one...\n")); + else + mem_size =+ lmb_size; } } + + if ( have_630 && lmb_addrs_overlap(0,mem_size,io_base,io_size) ) + lmb_add_io(io_base, io_size); lmb_analyze(); diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/ptrace.c linuxppc64_2_4/arch/ppc64/kernel/ptrace.c --- linux-2.4.8-ac9/arch/ppc64/kernel/ptrace.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ptrace.c Tue Aug 21 22:54:49 2001 @@ -110,32 +110,7 @@ goto out_tsk; if (request == PTRACE_ATTACH) { - if (child == current) - goto out_tsk; - if (((current->uid != child->euid) || - (current->uid != child->suid) || - (current->uid != child->uid) || - (current->gid != child->egid) || - (current->gid != child->sgid) || - (!cap_issubset(child->cap_permitted, current->cap_permitted)) || - (current->gid != child->gid)) - && !capable(CAP_SYS_PTRACE)) - goto out_tsk; - /* the same process cannot be attached many times */ - if (child->ptrace & PT_PTRACED) - goto out_tsk; - child->ptrace |= PT_PTRACED; - - write_lock_irq(&tasklist_lock); - if (child->p_pptr != current) { - REMOVE_LINKS(child); - child->p_pptr = current; - SET_LINKS(child); - } - write_unlock_irq(&tasklist_lock); - - send_sig(SIGSTOP, child, 1); - ret = 0; + ret = ptrace_attach(child); goto out_tsk; } ret = -ESRCH; diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/ptrace32.c linuxppc64_2_4/arch/ppc64/kernel/ptrace32.c --- linux-2.4.8-ac9/arch/ppc64/kernel/ptrace32.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ptrace32.c Tue Aug 21 22:54:49 2001 @@ -110,32 +110,7 @@ goto out_tsk; if (request == PTRACE_ATTACH) { - if (child == current) - goto out_tsk; - if (((current->uid != child->euid) || - (current->uid != child->suid) || - (current->uid != child->uid) || - (current->gid != child->egid) || - (current->gid != child->sgid) || - (!cap_issubset(child->cap_permitted, current->cap_permitted)) || - (current->gid != child->gid)) - && !capable(CAP_SYS_PTRACE)) - goto out_tsk; - /* the same process cannot be attached many times */ - if (child->ptrace & PT_PTRACED) - goto out_tsk; - child->ptrace |= PT_PTRACED; - - write_lock_irq(&tasklist_lock); - if (child->p_pptr != current) { - REMOVE_LINKS(child); - child->p_pptr = current; - SET_LINKS(child); - } - write_unlock_irq(&tasklist_lock); - - send_sig(SIGSTOP, child, 1); - ret = 0; + ret = ptrace_attach(child); goto out_tsk; } ret = -ESRCH; diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/rtas-eventscan.c linuxppc64_2_4/arch/ppc64/kernel/rtas-eventscan.c --- linux-2.4.8-ac9/arch/ppc64/kernel/rtas-eventscan.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/rtas-eventscan.c Wed Aug 22 10:51:36 2001 @@ -9,6 +9,7 @@ */ #include +#include #include /* for ppc_md */ #include @@ -31,14 +32,17 @@ do { /* implement me */ } while(0) /* ****************************************************************** */ +char rtas_event_buffer[NR_CPUS][1024]; long rtas_event_scan(void) { struct rtas_error_log *log = rtas.event_scan.log; unsigned long size = rtas.event_scan.log_size; - char buf[1024]; long cnt = 0; + char *buf; + + buf = rtas_event_buffer[smp_processor_id()]; for(;;) { long status = 1; diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/smp.c linuxppc64_2_4/arch/ppc64/kernel/smp.c --- linux-2.4.8-ac9/arch/ppc64/kernel/smp.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/smp.c Tue Aug 14 23:18:46 2001 @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include "open_pic.h" @@ -71,6 +72,8 @@ volatile unsigned long __initdata tb_sync_flag = 0; volatile unsigned long __initdata tb_offset = 0; +extern unsigned char stab_array[]; + int start_secondary(void *); extern int cpu_idle(void *unused); void smp_call_function_interrupt(void); @@ -93,7 +96,8 @@ static void smp_xics_message_pass(int target, int msg, unsigned long data, int wait); static int smp_xics_probe(void); -/* static void smp_xics_setup_cpu(int cpu_nr); */ +void xics_setup_cpu(void); +void xics_cause_IPI(int cpu); /* * XICS only has a single IPI, so encode the messages per CPU @@ -187,8 +191,10 @@ np = 0; for (i=0; i < maxPacas; ++i) { lpPaca = xPaca[i].xLpPacaPtr; - if ( lpPaca->xDynProcStatus < 2 ) + if ( lpPaca->xDynProcStatus < 2 ) { ++np; + last_jiffy_stamp(i) = last_jiffy_stamp(0); + } } smp_tb_synchronized = 1; @@ -472,19 +478,26 @@ /* Wait for response */ timeout = 8000000; while (atomic_read(&data.started) != cpus) { + HMT_low(); if (--timeout == 0) { printk("smp_call_function on cpu %d: other cpus not responding (%d)\n", smp_processor_id(), atomic_read(&data.started)); +#ifdef CONFIG_XMON xmon(0); +#endif goto out; } barrier(); udelay(1); +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif } if (wait) { timeout = 1000000; while (atomic_read(&data.finished) != cpus) { + HMT_low(); if (--timeout == 0) { printk("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n", smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started)); @@ -492,11 +505,15 @@ } barrier(); udelay(1); +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif } } ret = 0; out: + HMT_medium(); spin_unlock_bh(&call_lock); return ret; } @@ -528,6 +545,7 @@ int i, cpu_nr; struct task_struct *p; unsigned long sp; + unsigned long arpn; printk("Entering SMP Mode...\n"); PPCDBG(PPCDBG_SMP, "smp_boot_cpus: start. NR_CPUS = 0x%lx\n", NR_CPUS); @@ -560,11 +578,12 @@ * Other processor's tables are created and * initialized here. */ - paca->xStab_data.virt = - (unsigned long)btmalloc(PAGE_SIZE); - paca->xStab_data.real = ___pa(paca->xStab_data.virt); + + paca->xStab_data.virt = (unsigned long)&stab_array[PAGE_SIZE * (i-1)]; + arpn = physRpn_to_absRpn(___pa(paca->xStab_data.virt) >> 12); + paca->xStab_data.real = arpn << 12; paca->xHwProcNum = smp_hw_index[i]; - memset(paca->xStab_data.virt, 0, PAGE_SIZE); + memset((void *)paca->xStab_data.virt, 0, PAGE_SIZE); } PPCDBG(PPCDBG_SMP, "\tProcessor %d, stab virt = 0x%lx, stab real = 0x%lx\n", @@ -596,12 +615,17 @@ /* Probe arch for CPUs */ cpu_nr = smp_ops->probe(); + printk("Probe found %d CPUs\n", cpu_nr); + /* * only check for cpus we know exist. We keep the callin map * with cpus at the bottom -- Cort */ if (cpu_nr > max_cpus) cpu_nr = max_cpus; + + printk("Waiting for %d CPUs\n", cpu_nr-1); + for ( i = 1 ; i < cpu_nr; i++ ) { int c; struct pt_regs regs; @@ -640,7 +664,7 @@ sp = ((unsigned long)p) + sizeof(union task_union) - STACK_FRAME_OVERHEAD; PPCDBG(PPCDBG_SMP, "\tstack pointer virt = 0x%lx\n", sp); - current_set[i].sp_real = ___pa(sp); + current_set[i].sp_real = (void *)___pa(sp); PPCDBG(PPCDBG_SMP,"\tstack pointer real = 0x%lx\n", current_set[i].sp_real); @@ -652,8 +676,12 @@ * use this value that I found through experimentation. * -- Cort */ - for ( c = 5000; c && !cpu_callin_map[i] ; c-- ) + for ( c = 5000; c && !cpu_callin_map[i] ; c-- ) { +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif udelay(100); + } if ( cpu_callin_map[i] ) { @@ -705,8 +733,12 @@ */ cpu_callin_map[cpu] = 1; - while(!smp_commenced) + while(!smp_commenced) { +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif barrier(); + } __sti(); } diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/stab.c linuxppc64_2_4/arch/ppc64/kernel/stab.c --- linux-2.4.8-ac9/arch/ppc64/kernel/stab.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/stab.c Mon Aug 20 08:53:38 2001 @@ -44,8 +44,12 @@ if ( skip_esid_c ) esid += (REGION_STRIDE >> SID_SHIFT); - last_esid = GET_ESID(KERNELBASE) + - ((REGION_COUNT - 2) * (REGION_STRIDE >> SID_SHIFT)); + /* + * Put entries into the STAB for the first segment of the 4 kernel + * regions (0xC - 0xF). These are left in the table during flush. + * All other kernel segments (including bolted) are demand faulted. + */ + last_esid = GET_ESID(KERNELBASE) + (2 * (REGION_STRIDE >> SID_SHIFT)); for (; esid <= last_esid; esid += (REGION_STRIDE >> SID_SHIFT)) { /* Map the esid to an EA & find the vsid we need. */ @@ -116,9 +120,10 @@ castout_ste = ste + (castout_entry - 8); } - if(REGION_ID((castout_ste->dw0.dw0.esid) << SID_SHIFT) == - USER_REGION_ID) { - /* Found a non-kernel entry to castout */ + if((((castout_ste->dw0.dw0.esid) >> 32) == 0) || + (((castout_ste->dw0.dw0.esid) & 0xffffffff) > 0)) { + /* Found an entry to castout. It is either a user */ + /* region, or a secondary kernel segment. */ break; } @@ -295,10 +300,7 @@ if(!__is_processor(PV_POWER4)) { unsigned long entry; - STE *ste = stab; - - /* Never flush the first four entries (kernel segments) */ - ste += (REGION_COUNT-1); + STE *ste; /* Force previous translations to complete. DRENG */ __asm__ __volatile__ ("isync" : : : "memory"); @@ -311,10 +313,12 @@ PMC_SW_PROCESSOR(stab_invalidations); } } else { - /* Invalidate all entries except the four - * kernel segments. - */ - for(entry = REGION_COUNT - 1; + /* Invalidate all entries. */ + ste = stab; + + /* Never flush the first four entries. */ + ste += 4; + for(entry = 4; entry < (PAGE_SIZE / sizeof(STE)); entry++, ste++) { ste->dw0.dw0.v = 0; diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/kernel/time.c linuxppc64_2_4/arch/ppc64/kernel/time.c --- linux-2.4.8-ac9/arch/ppc64/kernel/time.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/time.c Fri Aug 17 12:56:41 2001 @@ -79,13 +79,14 @@ extern rwlock_t xtime_lock; extern int piranha_simulator; -unsigned long tb_ticks_per_jiffy; -unsigned long tb_ticks_per_usec; +unsigned long tb_ticks_per_jiffy = 5000000; /* initial sane values */ +unsigned long tb_ticks_per_usec = 500; unsigned long tb_to_us; unsigned long tb_last_stamp; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; extern unsigned long wall_jiffies; +extern unsigned long lpEvent_count; static long time_offset = 0; @@ -152,6 +153,9 @@ unsigned long cpu = smp_processor_id(); unsigned long jiffy_stamp = last_jiffy_stamp(cpu); + struct Paca * paca; + struct ItLpQueue * lpq; + irq_enter(cpu); if (!user_mode(regs)) @@ -203,6 +207,12 @@ if (ppc_md.heartbeat && !ppc_md.heartbeat_count--) ppc_md.heartbeat(); + + paca = (struct Paca *)mfspr(SPRG3); + paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; + lpq = paca->lpQueuePtr; + if ( lpq ) + lpEvent_count += ItLpQueue_process( lpq, regs ); irq_exit(cpu); return 1; /* lets ret_from_int know we can do checks */ @@ -223,6 +233,12 @@ * during a decrementer interrupt on processor 0 */ delta = tb_ticks_since(tb_last_stamp); + +#if CONFIG_PPC_ISERIES + /* Temporary hack to fix timer problems. MIKEC */ + delta = 0; +#endif + #ifdef CONFIG_SMP /* As long as timebases are not in sync, gettimeofday can only * have jiffy resolution on SMP. (except iSeries where the @@ -271,6 +287,12 @@ */ tb_delta = tb_ticks_since(last_jiffy_stamp(smp_processor_id())); tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; + +#if CONFIG_PPC_ISERIES + /* Temporary hack to fix timer problems. MIKEC */ + tb_delta = 0; +#endif + new_sec = tv->tv_sec; new_usec = tv->tv_usec - tb_delta / tb_ticks_per_usec; /* new_usec = tv->tv_usec - mulhwu(tb_to_us, tb_delta); */ diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/mm/btmalloc.c linuxppc64_2_4/arch/ppc64/mm/btmalloc.c --- linux-2.4.8-ac9/arch/ppc64/mm/btmalloc.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/mm/btmalloc.c Fri Aug 17 14:32:14 2001 @@ -15,14 +15,11 @@ #include #include +unsigned long btmalloc_debug = 0; +unsigned long btmalloc_highwater = 0; + extern pgd_t *bolted_pgd; -extern struct vmalloc_struct btmalloc_ops; -extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, - struct vmalloc_struct *v); extern pte_t *find_linux_pte(pgd_t *pgdir, unsigned long ea); -extern void build_valid_hpte(unsigned long vsid, unsigned long ea, - unsigned long pa, pte_t *ptep, unsigned hpteflags, - unsigned bolted ); /* * This lock guarantees that only one task will be attempting @@ -30,158 +27,133 @@ */ static spinlock_t bolted_pte_lock = SPIN_LOCK_UNLOCKED; -pmd_t *pmd_alloc_b(pgd_t *pgd, unsigned long address) -{ - pmd_t *pmd; - void *page; - unsigned long ea, pa, vsid; - struct vm_struct *area; +/* This lock protects the btmlist and serializes the removal + * of entries from the bolted page table + */ +rwlock_t btmlist_lock = RW_LOCK_UNLOCKED; - if (!pgd_present(*pgd)) { - spin_unlock(&bolted_pte_lock); - page = (void *)__get_free_page(GFP_KERNEL); - spin_lock(&bolted_pte_lock); - if (!page) - return NULL; +struct vm_struct * btmlist = NULL; - /* - * Because we dropped the lock, we should re-check the - * entry, as somebody else could have populated it.. - */ - if (pgd_present(*pgd)) { - __free_pages(page, 0); - goto out; - } +struct vm_struct *get_btm_area(unsigned long size, unsigned long flags); +long btmallocread(char *buf, char *addr, unsigned long count); - clear_page(page); - /* allocate space in the bolted address range */ - area = __get_vm_area(PAGE_SIZE, 0, &btmalloc_ops); - ea = (unsigned long)area->addr; - pa = __pa(page); - vsid = get_kernel_vsid(ea); - build_valid_hpte(vsid, ea, pa, NULL, - _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1); - pmd = (pmd_t *)ea; - pgd_set(pgd, pmd); - } -out: - return pmd_offset(pgd, address); -} +extern void build_valid_hpte(unsigned long vsid, unsigned long ea, + unsigned long pa, pte_t * ptep, + unsigned hpteflags, unsigned bolted ); -pte_t *pte_alloc_b(pmd_t *pmd, unsigned long address) +void* get_free_bolted_pages(int gfp_mask, unsigned long order) { - pte_t *pte; void *page; - struct vm_struct *area; - unsigned long ea, pa, vsid; - - if (!pmd_present(*pmd)) { - spin_unlock(&bolted_pte_lock); - page = (void *)__get_free_page(GFP_KERNEL); - spin_lock(&bolted_pte_lock); - if (!page) - return NULL; - - /* - * Because we dropped the lock, we should re-check the - * entry, as somebody else could have populated it.. - */ - if (pmd_present(*pmd)) { - __free_pages(page, 0); - goto out; - } - - clear_page(page); - /* allocate space in the bolted address range */ - area = __get_vm_area(PAGE_SIZE, 0, &btmalloc_ops); - ea = (unsigned long)area->addr; - pa = __pa(page); - vsid = get_kernel_vsid(ea); - build_valid_hpte(vsid, ea, pa, NULL, - _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1); - pte = (pte_t *)ea; - pmd_set(pmd, pte); - } -out: - return pte_offset(pmd, address); -} - -static unsigned long btmalloc_start(void) -{ - return BTMALLOC_START; -} -static unsigned long btmalloc_end(void) -{ - return BTMALLOC_END; + page = btmalloc(PAGE_SIZE * (1 << order)); + return(page); } -static int btmalloc_area_pages(unsigned long address, unsigned long size, - int gfp_mask, pgprot_t prot); -static void btfree_area_pages(unsigned long address, unsigned long size); - -struct vmalloc_struct btmalloc_ops = { - lock: RW_LOCK_UNLOCKED, - start: btmalloc_start, - end: btmalloc_end, - alloc_area_pages: btmalloc_area_pages, - free_area_pages: btfree_area_pages, -}; - -static void btfree_area_pages(unsigned long address, unsigned long size) +static int local_free_bolted_pages(void* page, unsigned long num) { - pte_t *ptep; - pte_t pte; - unsigned long num; - - num = (size >> PAGE_SHIFT); - - while(num--) { - ptep = find_linux_pte(bolted_pgd, address); - if (!ptep) { - write_unlock(&btmalloc_ops.lock); - panic("btfree_area_pages - page being freed (%016lx) is not bolted", address); - } - pte = *ptep; - pte_clear(ptep); - flush_hash_page( 0, address, pte ); - __free_pages(pte_page(pte), 0); - address += PAGE_SIZE; + int rc = 0; + u32 cntr = 0; + char* pageAddr = (char*)page; + pte_t * ptep, pte; + + /* Need to find the memmap array entry for the first real page of + * the range (this is where the reference count is maintained). + * It is assumed that the btmlist_lock is held to prevent changes + * to the bolted area page table while we search it + */ + ptep = find_linux_pte( bolted_pgd, (unsigned long)pageAddr ); + + if ( !ptep ) { + write_unlock(&btmlist_lock); + panic("free_bolted_pages - bolted page is NOT in the bolted page table"); + } + + /* find_linux_pte has assured us that the pte is PRESENT + * Check the page reference count on the first page of the range + * to insure that we only free the range when the last reference + * is freed. + */ + if ( page_count(pte_page(*ptep)) == 1 ) { + /* we are the last user of this range of pages - actually free the pages. */ + for (cntr=0; cntrcount)); + /* put_page_testzero(pte_page(pte)); */ + } + + return rc; } -static int btmalloc_area_pages(unsigned long address, unsigned long size, - int gfp_mask, pgprot_t prot) +void* btmalloc (unsigned long size) { pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; - unsigned long eaddr; + + unsigned long ea, eaddr; + struct vm_struct *area; unsigned long pa, vsid, pg_count, page; - - pg_count = (size >> PAGE_SHIFT); + + size = PAGE_ALIGN(size); + if (!size || (size >> PAGE_SHIFT) > num_physpages) { + BUG(); + return NULL; + } + + /* Get a virtual address region in the bolted space */ + area = get_btm_area(size, 0); + if (!area) { + BUG(); + return NULL; + } + + ea = (unsigned long) area->addr; + pg_count = (size >> PAGE_SHIFT); /* Create a Linux page table entry and an HPTE for each page */ - for(page = 0; page < pg_count; page++) { - pa = get_free_page(GFP_KERNEL) - PAGE_OFFSET; - eaddr = address + (page * PAGE_SIZE); + for(page = 0; page < pg_count; page++) { + + pa = get_free_page(GFP_KERNEL) - PAGE_OFFSET; - /* - * We need a lock here to avoid multiple tasks doing + eaddr = ea + (page * PAGE_SIZE); + + /* We need a lock here to avoid multiple tasks doing * pmd_alloc_b and/or pte_alloc_b simultaneously */ spin_lock(&bolted_pte_lock); - - /* - * Get a pointer to the linux page table entry for this page + /* Get a pointer to the linux page table entry for this page * allocating pmd or pte pages along the way as needed */ pgdp = pgd_offset_b(eaddr); pmdp = pmd_alloc_b(pgdp, eaddr); ptep = pte_alloc_b(pmdp, eaddr); - - /* - * At this point we are done allocating any new pmd/pte pages + /* At this point we are done allocating any new pmd/pte pages * We own the pte (ptep) by nature of owning the eaddr so we * no longer need the lock */ @@ -190,26 +162,256 @@ /* Clear any old hpte and set the new linux pte */ set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, PAGE_KERNEL)); + PPCDBG(PPCDBG_MM, "In btmalloc: ea = %lx, pa = %lx\n", eaddr, pa); + vsid = get_kernel_vsid(eaddr); - build_valid_hpte(vsid, eaddr, pa, ptep, - _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1); + build_valid_hpte( vsid, eaddr, pa, ptep, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1 ); + } + + if(ea > btmalloc_highwater) btmalloc_highwater = ea; + if(btmalloc_debug) { + udbg_printf("btmalloc : 0x%lx (0x%lx) 0x%lx\n", + ea, ___pa(ea), size); + if(btmalloc_debug > 1) xmon(0); + } + + return (void*)ea; +} + +/* + * get_btm_area + * + * Get a virtual region in the bolted space + * + * This function is also called when allocating pages + * for pmd's/pte's for the bolted page table itself. + * + */ +struct vm_struct *get_btm_area(unsigned long size, unsigned long flags) +{ + unsigned long addr; + struct vm_struct **p, *tmp, *area; + + area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + return NULL; + + write_lock(&btmlist_lock); + + addr = BTMALLOC_START; + for (p = &btmlist; (tmp = *p) ; p = &tmp->next) { + if (size + addr < (unsigned long) tmp->addr) + break; + addr = tmp->size + (unsigned long) tmp->addr; + if (addr > BTMALLOC_END-size) { + write_unlock(&btmlist_lock); + kfree(area); + return NULL; + } + } + + area->flags = flags; + area->addr = (void *)addr; + area->size = size; + area->next = *p; + *p = area; + write_unlock(&btmlist_lock); + return area; +} + +/* + * Free a range of bolted pages that were allocated with btmalloc + */ +char btfree_dummy; +extern void * _get_SP(void); + +void btfree(void * addr) { + + struct vm_struct **p, *tmp; + unsigned long size; + + if ( !addr ) { + udbg_printf("btfree - bad address, returning w/o freeing - bad address=%p\n", addr); + return; + } + + if ( ( PAGE_SIZE-1 ) & (unsigned long)addr ) { + udbg_printf("btfree - trying to btfree() bad address=%p\n", addr); + printk(KERN_ERR "Trying to btfree() bad address (%p)\n", addr); + return; + } + + size = 0; + + write_lock( &btmlist_lock ); + + /* Scan the bolted memory list for an entry matching + * the address to be freed, get the size (in bytes) + * and free the entry. The list lock is not dropped + * until the page table entries are removed. + */ + for ( p = &btmlist; (tmp = *p); p = &tmp->next ) { + if ( tmp->addr == addr ) { + size = tmp->size; + /* *p = tmp->next; + kfree(tmp); + */ + break; + } + } + + /* If no entry found, it is an error */ + if ( !size ) { + write_unlock(&btmlist_lock); + udbg_printf("no entry found for 0x%lx\n", addr); + printk(KERN_ERR "Trying to btfree() bad address (%p)\n", addr ); + btfree_dummy = *((char *)0); + return; + } + + if(btmalloc_debug) { + udbg_printf("btfree : 0x%lx (0x%lx) 0x%lx\n", + addr, ___pa(addr), size); + if(btmalloc_debug > 1) xmon(0); + } + + /* Free up the bolted pages and remove the page table entries */ + if ( local_free_bolted_pages( addr, size >> PAGE_SHIFT ) ) { + /* if we actually freed the pages (not just decremented use count) + * then free the list entry + */ + *p = tmp->next; + kfree(tmp); + } + + write_unlock(&btmlist_lock); +} + +long btmallocread(char *buf, char *addr, unsigned long count) { + struct vm_struct *tmp; + char *vaddr, *buf_start = buf; + unsigned long n; + + /* Don't allow overflow */ + if ((unsigned long) addr + count < count) + count = -(unsigned long) addr; + + read_lock(&btmlist_lock); + for (tmp = btmlist; tmp; tmp = tmp->next) { + vaddr = (char *) tmp->addr; + if (addr >= vaddr + tmp->size - PAGE_SIZE) + continue; + while (addr < vaddr) { + if (count == 0) + goto finished; + *buf = '\0'; + buf++; + addr++; + count--; + } + n = vaddr + tmp->size - PAGE_SIZE - addr; + do { + if (count == 0) + goto finished; + *buf = *addr; + buf++; + addr++; + count--; + } while (--n > 0); } + finished: + read_unlock(&btmlist_lock); + return buf - buf_start; +} - return 0; + +pmd_t *pmd_alloc_b(pgd_t *pgd, unsigned long address) +{ + pmd_t *pmd; + void *page; + unsigned long ea, pa, vsid; + struct vm_struct *area; + + address = (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1); + if (pgd_none(*pgd)) { + // allocate space in the bolted address range + area = get_btm_area( 4096, 0 ); + ea = (unsigned long)area->addr; + page = (void *)__get_free_page(GFP_KERNEL); + clear_page(page); + pa = ((unsigned long)page - PAGE_OFFSET ); + vsid = get_kernel_vsid( ea ); + + build_valid_hpte( vsid, ea, pa, NULL, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1 ); + pmd = (pmd_t *)ea; + pgd_set(pgd, pmd); + return pmd + address; + } + if (pgd_bad(*pgd)) { + __bad_pmd(pgd); + return NULL; + } + return (pmd_t *) pgd_page(*pgd) + address; } -void btfree(void *addr) +pte_t *pte_alloc_b(pmd_t *pmd, unsigned long address) { - __vfree(addr, &btmalloc_ops); + pte_t *pte; + void *page; + struct vm_struct *area; + unsigned long ea, pa, vsid; + + address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); + if (pmd_none(*pmd)) { + // allocate space in the bolted address range + area = get_btm_area( 4096, 0 ); + ea = (unsigned long)area->addr; + page = (void *)__get_free_page(GFP_KERNEL); + clear_page(page); + pa = ((unsigned long)page - PAGE_OFFSET ); + vsid = get_kernel_vsid( ea ); + + build_valid_hpte( vsid, ea, pa, NULL, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1 ); + pte = (pte_t *)ea; + pmd_set(pmd, pte); + return pte + address; + } + if (pmd_bad(*pmd)) { + __bad_pte(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + address; } -void *btmalloc(unsigned long size) +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving a inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +pte_t *empty_bad_page_table; + +pte_t * __bad_pagetable(void) { - return ___vmalloc(size, 0, 0, &btmalloc_ops); + clear_page(empty_bad_page_table); + return empty_bad_page_table; } -long btread(char *buf, char *addr, unsigned long count) +void *empty_bad_page; + +pte_t __bad_page(void) { - return __vread(buf, addr, count, &btmalloc_ops); + clear_page(empty_bad_page); + return pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED)); } diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/mm/init.c linuxppc64_2_4/arch/ppc64/mm/init.c --- linux-2.4.8-ac9/arch/ppc64/mm/init.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/mm/init.c Mon Aug 20 08:54:55 2001 @@ -119,20 +119,18 @@ int do_check_pgt_cache(int low, int high) { int freed = 0; - if(pgtable_cache_size > high) { + + if (pgtable_cache_size > high) { do { - if(pgd_quicklist) - free_pgd_slow(get_pgd_fast()), freed++; - if(pmd_quicklist) - free_pmd_slow(get_pmd_fast()), freed++; - if(pte_quicklist) - { - free_pte_slow(pte_alloc_one_fast(NULL,0)); - freed++; - } - } while(pgtable_cache_size > low); + if (pgd_quicklist) + free_page((unsigned long)pgd_alloc_one_fast(0)), ++freed; + if (pmd_quicklist) + free_page((unsigned long)pmd_alloc_one_fast(0, 0)), ++freed; + if (pte_quicklist) + free_page((unsigned long)pte_alloc_one_fast(0, 0)), ++freed; + } while (pgtable_cache_size > low); } - return freed; + return freed; } void show_mem(void) @@ -277,8 +275,8 @@ ea = (unsigned long)(area->addr); } else { - ea = ioremap_bot; - ioremap_bot += size; + ea = ioremap_bot; + ioremap_bot += size; } if ((flags & _PAGE_PRESENT) == 0) @@ -329,33 +327,33 @@ pte_t *ptep; unsigned long vsid; - if(mem_init_done) { - spin_lock(&init_mm.page_table_lock); - pgdp = pgd_offset_i(ea); - pmdp = pmd_alloc(&init_mm, pgdp, ea); - ptep = pte_alloc(&init_mm, pmdp, ea); - set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); - spin_unlock(&init_mm.page_table_lock); - } else { - /* If the mm subsystem is not fully up, we cannot create a linux - * page table entry for this mapping. Simply bolt an entry in the - * hardware page table. - */ - vsid = get_kernel_vsid(ea); - make_pte(htab_data.htab, - (vsid << 28) | (ea & 0xFFFFFFF), // va (NOT the ea) - pa, - _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX, - htab_data.htab_hash_mask); - } + if (mem_init_done) { + spin_lock(&init_mm.page_table_lock); + pgdp = pgd_offset_i(ea); + pmdp = pmd_alloc(&init_mm, pgdp, ea); + ptep = pte_alloc(&init_mm, pmdp, ea); + set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); + spin_unlock(&init_mm.page_table_lock); + } else { + /* If the mm subsystem is not fully up, we cannot create a + * linux page table entry for this mapping. Simply bolt an + * entry in the hardware page table. + */ + vsid = get_kernel_vsid(ea); + make_pte(htab_data.htab, + (vsid << 28) | (ea & 0xFFFFFFF), // va (NOT the ea) + pa, + _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX, + htab_data.htab_hash_mask); + } } void local_flush_tlb_all(void) { -/* - * Implemented to just flush the vmalloc area. vmalloc is the only user of flush_tlb_all - */ + /* Implemented to just flush the vmalloc area. + * vmalloc is the only user of flush_tlb_all. + */ local_flush_tlb_range( NULL, VMALLOC_START, VMALLOC_END ); } @@ -367,7 +365,7 @@ for ( mp = mm->mmap; mp != NULL; mp = mp->vm_next ) local_flush_tlb_range( mm, mp->vm_start, mp->vm_end ); } - else + else /* MIKEC: It is not clear why this is needed */ local_flush_tlb_range( mm, USER_START, USER_END ); } @@ -428,10 +426,10 @@ unsigned long context; if ( start >= end ) - return; + panic("flush_tlb_range: start (%016lx) greater than end (%016lx)\n", start, end ); if ( REGION_ID(start) != REGION_ID(end) ) - panic("flush_tlb_range start (%016lx) and end (%016lx) not in same region\n", start, end ); + panic("flush_tlb_range: start (%016lx) and end (%016lx) not in same region\n", start, end ); context = 0; @@ -450,7 +448,7 @@ context = mm->context; break; default: - return; + panic("flush_tlb_range: invalid region for start (%016lx) and end (%016lx)\n", start, end); } @@ -577,7 +575,7 @@ */ bootmap_pages = bootmem_bootmap_pages(total_pages); - start = __a2p(lmb_alloc(bootmap_pages< - * Changes to accomodate Power Macintoshes. - * Cort Dougan - * Rewrites. - * Grant Erickson - * General rework and split from mm/init.c. - * Dave Engebretsen - * PPC 64b rework. - * - * Module name: mem_pieces.c - * - * Description: - * Routines and data structures for manipulating and representing - * phyiscal memory extents (i.e. address/length pairs). - * - */ - -#include -#include -#include -#include -#include - -#include "mem_pieces.h" - -#include - -extern struct mem_pieces phys_avail; - -void mem_pieces_print(struct mem_pieces *); - -/* - * Scan a region for a piece of a given size with the required alignment. - */ -void __init * -mem_pieces_find(unsigned long size, unsigned int align) -{ - long i; - unsigned long a, e; - struct mem_pieces *mp = &phys_avail; - - for (i = 0; i < mp->n_regions; ++i) { - a = mp->regions[i].address; - e = a + mp->regions[i].size; - a = (a + align - 1) & -align; - if (a + size <= e) { - mem_pieces_remove(mp, a, size, 1); - return __va(a); - } - } - panic("Couldn't find %lu bytes at %u alignment\n", size, align); - - return NULL; -} - -/* - * Remove some memory from an array of pieces - */ -void __init -mem_pieces_remove(struct mem_pieces *mp, - unsigned long start, - unsigned long size, - int must_exist) -{ - long i, j; - unsigned long end, rs, re; - struct reg_property *rp; - - end = start + size; - for (i = 0, rp = mp->regions; i < mp->n_regions; ++i, ++rp) { - if (end > rp->address && start < rp->address + rp->size) - break; - } - if (i >= mp->n_regions) { - if (must_exist) - printk("mem_pieces_remove: [%lx,%lx] not in any region\n", start, end); - return; - } - for (; i < mp->n_regions && end > rp->address; ++i, ++rp) { - rs = rp->address; - re = rs + rp->size; - if (must_exist && (start < rs || end > re)) { - printk("mem_pieces_remove: bad overlap [%lx,%lx] with", - start, end); - mem_pieces_print(mp); - must_exist = 0; - } - if (start > rs) { - rp->size = start - rs; - if (end < re) { - /* need to split this entry */ - if (mp->n_regions >= MEM_PIECES_MAX) - panic("eek... mem_pieces overflow"); - for (j = mp->n_regions; j > i + 1; --j) - mp->regions[j] = mp->regions[j-1]; - ++mp->n_regions; - rp[1].address = end; - rp[1].size = re - end; - } - } else { - if (end < re) { - rp->address = end; - rp->size = re - end; - } else { - /* need to delete this entry */ - for (j = i; j < mp->n_regions - 1; ++j) - mp->regions[j] = mp->regions[j+1]; - --mp->n_regions; - --i; - --rp; - } - } - } -} - -void __init -mem_pieces_print(struct mem_pieces *mp) -{ - int i; - - PPCDBG(PPCDBG_MMINIT, "\tmem_pieces_print\n"); - - for (i = 0; i < mp->n_regions; ++i) { - PPCDBG(PPCDBG_MMINIT, "\t\t[0x%lx, 0x%lx)\n", - mp->regions[i].address, - mp->regions[i].address + mp->regions[i].size); - } -} - -/* - * Add some memory to an array of pieces - */ -void __init -mem_pieces_append(struct mem_pieces *mp, - unsigned long start, - unsigned long size) -{ - struct reg_property *rp; - - if (mp->n_regions >= MEM_PIECES_MAX) - return; - rp = &mp->regions[mp->n_regions++]; - rp->address = start; - rp->size = size; -} - -void __init -mem_pieces_sort(struct mem_pieces *mp) -{ - unsigned long a, s; - long 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; - } -} - -void __init -mem_pieces_coalesce(struct mem_pieces *mp) -{ - unsigned long a, s, ns; - long i, j, d; - - d = 0; - for (i = 0; i < mp->n_regions; i = j) { - a = mp->regions[i].address; - s = mp->regions[i].size; - for (j = i + 1; j < mp->n_regions - && mp->regions[j].address - a <= s; ++j) { - ns = mp->regions[j].address + mp->regions[j].size - a; - if (ns > s) - s = ns; - } - mp->regions[d].address = a; - mp->regions[d].size = s; - ++d; - } - mp->n_regions = d; -} diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/mm/mem_pieces.h linuxppc64_2_4/arch/ppc64/mm/mem_pieces.h --- linux-2.4.8-ac9/arch/ppc64/mm/mem_pieces.h Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/mm/mem_pieces.h Wed Dec 31 18:00:00 1969 @@ -1,52 +0,0 @@ -/* - * Copyright (c) 1996 Paul Mackerras - * Changes to accomodate Power Macintoshes. - * Cort Dougan - * Rewrites. - * Grant Erickson - * General rework and split from mm/init.c. - * Dave Engebretsen - * PPC 64b rework. - * - * Module name: mem_pieces.h - * - * Description: - * Routines and data structures for manipulating and representing - * phyiscal memory extents (i.e. address/length pairs). - * - */ - -#ifndef __MEM_PIECES_H__ -#define __MEM_PIECES_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -/* Type Definitions */ - -#define MEM_PIECES_MAX 32 - -struct mem_pieces { - int n_regions; - struct reg_property regions[MEM_PIECES_MAX]; -}; - -/* Function Prototypes */ - -extern void *mem_pieces_find(unsigned long size, unsigned int align); -extern void mem_pieces_remove(struct mem_pieces *mp, unsigned long start, - unsigned long size, int must_exist); -extern void mem_pieces_append(struct mem_pieces *mp, unsigned long start, - unsigned long size); -extern void mem_pieces_coalesce(struct mem_pieces *mp); -extern void mem_pieces_sort(struct mem_pieces *mp); - -#ifdef __cplusplus -} -#endif - -#endif /* __MEM_PIECES_H__ */ diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/mymacros.h linuxppc64_2_4/arch/ppc64/mymacros.h --- linux-2.4.8-ac9/arch/ppc64/mymacros.h Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/mymacros.h Wed Dec 31 18:00:00 1969 @@ -1,39 +0,0 @@ - -#undef PPC64_ELF_SECTIONS -#ifndef PPC64_ELF_SECTIONS - -/* The toolchain cannot handle this yet, so comment them *all* out. */ -#ifdef __section__ -# undef __section__ -# define __section__(X) -#endif - -#if 0 - /* These are commented out in asm-ppc64/init.h - * These will/may go back in once we get ELF named - * sections in gcc 3.X -Peter. - */ - __chrp, __chrpdata, __chrpfunc(x), __openfirmware, - __openfirmwaredata, __openfirmwarefunc(x) -#endif - -#endif /* PPC64_ELF_SECTIONS */ - -#define DEBUG_DELAY -#if 0 -#define PIRANHA -#define DEBUG_YABOOT -#define DEBUG_PROM -#endif - - -#if 1 -/* For 64-bit apps, temporarily reduce the size of the address space - * available to user application. This allow us to use strace without - * having to compile a strace64 program. This shouldn't affect anyone - * other than Steve Munroe, Peter Bergner. I will back this hack out - * later... -Peter - */ -#define PPC64_32B_ADDR_SPACE -#endif - diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/vmlinux.lds linuxppc64_2_4/arch/ppc64/vmlinux.lds --- linux-2.4.8-ac9/arch/ppc64/vmlinux.lds Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/vmlinux.lds Tue Aug 21 14:47:57 2001 @@ -69,8 +69,15 @@ __start___ksymtab = .; /* Kernel symbol table */ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; - . = ALIGN(32); + + . = ALIGN(4096); + .data.page_aligned : { *(.data.page_aligned) } + + . = ALIGN(128); .data.cacheline_aligned : { *(.data.cacheline_aligned) } . = ALIGN(4096); @@ -121,6 +128,7 @@ . = ALIGN(4096); __toc_end = .; + . = ALIGN(4096); __bss_start = .; .bss : { diff -uNr --exclude=CVS linux-2.4.8-ac9/arch/ppc64/xmon/xmon.c linuxppc64_2_4/arch/ppc64/xmon/xmon.c --- linux-2.4.8-ac9/arch/ppc64/xmon/xmon.c Thu Aug 23 09:17:16 2001 +++ linuxppc64_2_4/arch/ppc64/xmon/xmon.c Fri Aug 17 13:55:51 2001 @@ -942,7 +942,6 @@ #if 0 || stack[2] == (unsigned) &ret_from_intercept || stack[2] == (unsigned) &ret_from_syscall_2 - || stack[2] == (unsigned) &lost_irq_ret || stack[2] == (unsigned) &do_bottom_half_ret || stack[2] == (unsigned) &do_signal_ret #endif @@ -961,6 +960,11 @@ printf(" %s+0x%x", tab.name, delta); } printf("\n"); + if (regs.gpr[1] < sp) { + printf("\n", regs.gpr[1]); + break; + } + sp = regs.gpr[1]; if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) break; diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/block/Config.in linuxppc64_2_4/drivers/block/Config.in --- linux-2.4.8-ac9/drivers/block/Config.in Thu Aug 23 09:17:18 2001 +++ linuxppc64_2_4/drivers/block/Config.in Fri Aug 17 13:03:16 2001 @@ -28,6 +28,12 @@ tristate ' Atari SLM laser printer support' CONFIG_ATARI_SLM fi fi +if [ "$CONFIG_PPC_ISERIES" = "y" ]; then + dep_tristate 'iSeries Virtual I/O disk support' CONFIG_VIODASD $CONFIG_PPC_ISERIES + if [ "$CONFIG_VIODASD" = "y" -o "$CONFIG_VIODASD" = "m" ]; then + bool 'iSeries Virtual disk IDE emulation' CONFIG_VIODASD_IDE + fi +fi dep_tristate 'XT hard disk support' CONFIG_BLK_DEV_XD $CONFIG_ISA dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARPORT if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/block/Makefile linuxppc64_2_4/drivers/block/Makefile --- linux-2.4.8-ac9/drivers/block/Makefile Thu Aug 23 09:17:18 2001 +++ linuxppc64_2_4/drivers/block/Makefile Mon Jun 18 18:18:47 2001 @@ -20,6 +20,7 @@ obj-$(CONFIG_BLK_DEV_FD) += floppy.o obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o +obj-$(CONFIG_VIODASD) += viodasd.o obj-$(CONFIG_BLK_DEV_SWIM_IOP) += swim_iop.o obj-$(CONFIG_ATARI_ACSI) += acsi.o obj-$(CONFIG_ATARI_SLM) += acsi_slm.o diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/block/genhd.c linuxppc64_2_4/drivers/block/genhd.c --- linux-2.4.8-ac9/drivers/block/genhd.c Thu Jul 19 19:48:15 2001 +++ linuxppc64_2_4/drivers/block/genhd.c Mon Jun 18 18:18:47 2001 @@ -30,6 +30,7 @@ extern int atmdev_init(void); extern int i2o_init(void); extern int cpqarray_init(void); +extern void ieee1394_init(void); int __init device_init(void) { @@ -48,6 +49,9 @@ /* This has to be done before scsi_dev_init */ soc_probe(); #endif +#ifdef CONFIG_IEEE1394 + ieee1394_init(); +#endif #ifdef CONFIG_BLK_CPQ_DA cpqarray_init(); #endif @@ -59,6 +63,9 @@ #endif #ifdef CONFIG_VT console_map_init(); +#endif +#ifdef CONFIG_VIODASD + viodasd_init(); #endif return 0; } diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/block/ll_rw_blk.c linuxppc64_2_4/drivers/block/ll_rw_blk.c --- linux-2.4.8-ac9/drivers/block/ll_rw_blk.c Thu Aug 23 09:17:19 2001 +++ linuxppc64_2_4/drivers/block/ll_rw_blk.c Fri Aug 10 13:08:53 2001 @@ -1229,6 +1229,9 @@ #ifdef CONFIG_BLK_DEV_XD xd_init(); #endif +#ifdef CONFIG_VIOCD + viocd_init(); +#endif #ifdef CONFIG_BLK_DEV_MFM mfm_init(); #endif diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/block/viodasd.c linuxppc64_2_4/drivers/block/viodasd.c --- linux-2.4.8-ac9/drivers/block/viodasd.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/block/viodasd.c Fri Aug 24 10:28:37 2001 @@ -0,0 +1,1356 @@ +/* + * viodasd.c + * Copyright (C) 2001 Dave Boutcher IBM Corporation + *************************************************************************** + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *************************************************************************** + * This routine provides access to disk space (termed "DASD" in historical + * IBM terms) owned and managed by an OS/400 partition running on the + * same box as this Linux partition. + * + * All disk operations are performed by sending messages back and forth to + * the OS/400 partition. The format of the messages is defined in + * include/asm-ppc/iSeries/vio.h + * + * This device driver can either use it's own major number, or it can + * pretend to be an IDE drive (Major #3). Currently it doesn't + * emulate all the other IDE majors. This is controlled with a + * CONFIG option. You can either call this an elegant solution to the + * fact that a lot of software doesn't recognize a new disk major number... + * or you can call this a really ugly hack. Your choice. + */ + +#include +#include + +/*************************************************************************** + * Decide if we are using our own major or pretending to be an IDE drive + * + * If we are using our own majors, we only support 3 partitions per physical + * disk....so with minor numbers 0-255 we get a maximum of 64 disks. If we + * are emulating IDE, we get 16 partitions per disk, with a maximum of 16 + * disks + ***************************************************************************/ +#ifdef CONFIG_VIODASD_IDE +#define MAJOR_NR IDE0_MAJOR +#define PARTITION_SHIFT 6 +#define do_viodasd_request do_hd_request +static int numdsk = 16; +static int viodasd_max_disk = 16; +#define VIOD_DEVICE_NAME "hd" +#define VIOD_GENHD_NAME "hd" +#else +#define MAJOR_NR VIODASD_MAJOR +#define PARTITION_SHIFT 3 +static int numdsk = 32; +static int viodasd_max_disk = 32; +#define VIOD_DEVICE_NAME "viod" +#ifdef CONFIG_DEVFS_FS +#define VIOD_GENHD_NAME "viod" +#else +#define VIOD_GENHD_NAME "iSeries/vd" +#endif /* CONFIG_DEVFS */ +#endif /* CONFIG_VIODASD_IDE */ + + +#define VIODASD_VERS "1.02" +#define LOCAL_END_REQUEST + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif +#ifndef _ISERIES_PROC_H +#include +#endif + +MODULE_DESCRIPTION("iSeries Virtual DASD"); +MODULE_AUTHOR("Dave Boutcher"); + +/*************************************************************************** + * In a perfect world we will perform better if we get page-aligned I/O + * requests, in multiples of pages. At least peg our block size fo the + * actual page size. + ***************************************************************************/ +static int blksize = HVPAGESIZE; /* in bytes */ + +static DECLARE_WAIT_QUEUE_HEAD(viodasd_wait); +struct viodasd_waitevent { + struct semaphore *sem; + int rc; + int changed; /* Used only for check_change */ +}; + +/*************************************************************************** + * All our disk-related global structures + ***************************************************************************/ +static struct hd_struct *viodasd_partitions; +static int *viodasd_sizes; +static int *viodasd_sectsizes; +static int *viodasd_maxsectors; +extern struct gendisk viodasd_gendsk; + +/*************************************************************************** + * Figure out the biggest I/O request (in sectors) we can accept + ***************************************************************************/ +#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA) + +/*************************************************************************** + * Keep some statistics on what's happening for the PROC file system + ***************************************************************************/ +static struct { + long tot; + long nobh; + long ntce[VIOMAXBLOCKDMA]; +} viod_stats[64][2]; + +/*************************************************************************** + * Number of disk I/O requests we've sent to OS/400 + ***************************************************************************/ +static int numReqOut; + +/*************************************************************************** + * Our internal printk macro + ***************************************************************************/ +#define err_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERR: " fmt, __FILE__, __LINE__ , ## args) + +/*************************************************************************** + * This is our internal structure for keeping track of disk devices + ***************************************************************************/ +struct viodasd_device { + int useCount; + u16 cylinders; + u16 tracks; + u16 sectors; + u16 bytesPerSector; + u64 size; + int readOnly; +} *viodasd_devices; + +/*************************************************************************** + * When we get a disk I/O request we take it off the general request queue + * and put it here. + ***************************************************************************/ +static LIST_HEAD(reqlist); + +/*************************************************************************** + *************************************************************************** + * CODE STARTS HERE + *************************************************************************** + ***************************************************************************/ + +/*************************************************************************** + * Handle reads from the proc file system + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + int i; + int j; + +#if defined(MODULE) + len += sprintf(buf+len,"viod Module opened %d times. Major number %d\n", + MOD_IN_USE, + MAJOR_NR); +#endif + len += sprintf(buf+len,"viod %d devices\n", + numdsk); + + for (i=0; i<16; i++) + { + if (viod_stats[i][0].tot || viod_stats[i][1].tot) + { + len += sprintf(buf+len,"DISK %2.2d: rd %-10.10ld wr %-10.10ld (no buffer list rd %-10.10ld wr %-10.10ld\n", + i, viod_stats[i][0].tot,viod_stats[i][1].tot,viod_stats[i][0].nobh,viod_stats[i][1].nobh); + + len += sprintf(buf+len,"rd DMA: "); + + for (j=0; jnlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viodasd_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viodasd", iSeries_proc); +} + +/*************************************************************************** + * End a request + ***************************************************************************/ +static void viodasd_end_request(struct request *req, int uptodate) { + + if (end_that_request_first(req, uptodate, VIOD_DEVICE_NAME)) + return; + + end_that_request_last(req); +} + +/*************************************************************************** + * This rebuilds the partition information for a single disk device + ***************************************************************************/ +static int viodasd_revalidate(kdev_t dev) +{ + int i; + int device_no = DEVICE_NR(dev); + int part0 = (device_no << PARTITION_SHIFT); + int npart = (1<=0;i--) + { + minor = part0 +i; + + if (viodasd_partitions[minor].nr_sects != 0) + { + devp = MKDEV(MAJOR_NR, minor); + fsync_dev(devp); + + sb = get_super(devp); + if (sb) invalidate_inodes(sb); + + invalidate_buffers(devp); + } + + viodasd_partitions[minor].start_sect = 0; + viodasd_partitions[minor].nr_sects = 0; + } + + grok_partitions(&viodasd_gendsk, device_no, npart, viodasd_devices[device_no].size>>9); + + return 0; +} + +/*************************************************************************** + * This is the actual open code. It gets called from the external + * open entry point, as well as from the init code when we're figuring + * out what disks we have + ***************************************************************************/ +static int internal_open(int device_no) +{ + int i; + struct viodasd_waitevent we; + + HvLpEvent_Rc hvrc; + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) + { + err_printk("Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code */ + if (we.rc != 0) + { + err_printk("bad rc opening disk: %d\n",(int)we.rc); + return we.rc; + } + + /* If this is the first open of this device, update the device information */ + /* If this is NOT the first open, assume that it isn't changing */ + if (viodasd_devices[device_no].useCount == 0) + { + if (viodasd_devices[device_no].size > 0) + { + /* divide by 512 */ + u64 tmpint = viodasd_devices[device_no].size >> 9; + viodasd_partitions[device_no << PARTITION_SHIFT].nr_sects = tmpint; + /* Now the value divided by 1024 */ + tmpint = tmpint >> 1; + viodasd_sizes[device_no << PARTITION_SHIFT] = tmpint; + + for (i = (device_no << PARTITION_SHIFT); + i < ((device_no+1) << PARTITION_SHIFT); + i++) + viodasd_sectsizes[i] = viodasd_devices[device_no].bytesPerSector; + + } + } + else + { + /* If the size of the device changed, wierd things are happening! */ + if (viodasd_sizes[device_no<> 10) + { + err_printk("disk size change (%dK to %dK) for device %d\n", + viodasd_sizes[device_no<> 10, + device_no); + } + } + + /* Bump the use count */ + viodasd_devices[device_no].useCount++; + + return 0; +} + +/*************************************************************************** + * This is the actual release code. It gets called from the external + * release entry point, as well as from the init code when we're figuring + * out what disks we have + ***************************************************************************/ +static int internal_release(int device_no) +{ + + /* Send the event to OS/400. We DON'T expect a response */ + HvLpEvent_Rc hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockclose, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + viodasd_devices[device_no].useCount--; + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + return 0; +} + +/*************************************************************************** + * External open entry point. + ***************************************************************************/ +static int viodasd_open(struct inode *ino, struct file *fil) +{ + int device_no; + + /* Do a bunch of sanity checks */ + if (!ino) + { + err_printk("no inode provided in open\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) + { + err_printk("Wierd error...wrong major number on open\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) + { + err_printk("Invalid minor device number %d in open\n",device_no); + return -ENODEV; + } + + /* Call the actual open code */ + if (internal_open(device_no) == 0) + { + if (fil && fil->f_mode) + { + if (fil->f_mode & 2) + { + if (viodasd_devices[device_no].readOnly) { + internal_release( device_no ); + return -EROFS; + } + } + } + MOD_INC_USE_COUNT; + return 0; + } + else + { + return -EIO; + } +} + +/*************************************************************************** + * External release entry point. + ***************************************************************************/ +static int viodasd_release(struct inode *ino, struct file *fil) +{ + int device_no; + + /* Do a bunch of sanity checks */ + if (!ino) + { + err_printk("no inode provided in release\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) + { + err_printk("Wierd error...wrong major number on release\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) + { + return -ENODEV; + } + + /* Just to be paranoid, sync the device */ + fsync_dev(ino->i_rdev); + + /* Call the actual release code */ + internal_release(device_no); + + MOD_DEC_USE_COUNT; + return 0; +} + +/*************************************************************************** + * External ioctl entry point. + ***************************************************************************/ +static int viodasd_ioctl(struct inode *ino, struct file *fil, unsigned int cmd, unsigned long arg) +{ + int device_no; + int err; + HvLpEvent_Rc hvrc; + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Sanity checks */ + if (!ino) + { + err_printk("no inode provided in ioctl\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) + { + err_printk("Wierd error...wrong major number on ioctl\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) + { + err_printk("Invalid minor device number %d in ioctl\n",device_no); + return -ENODEV; + } + + switch(cmd) { + case BLKGETSIZE: + /* return the device size in sectors */ + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *)arg, sizeof(long)); + if (err) return err; + + put_user(viodasd_partitions[MINOR(ino->i_rdev)].nr_sects, (long *)arg); + return 0; + + case FDFLUSH: + case BLKFLSBUF: + if (!suser()) return -EACCES; + fsync_dev(ino->i_rdev); + invalidate_buffers(ino->i_rdev); + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockflush, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)&Semaphore, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + err_printk("bad rc on sync signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + down(&Semaphore); + + return 0; + + case BLKRAGET: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *)arg, sizeof(long)); + if (err) return err; + put_user(read_ahead[MAJOR_NR], (long *)arg); + return 0; + + case BLKRASET: + if (!suser()) return -EACCES; + if (arg > 0x00ff) return -EINVAL; + read_ahead[MAJOR_NR] = arg; + return 0; + + case BLKRRPART: + viodasd_revalidate(ino->i_rdev); + return 0; + + case HDIO_GETGEO: + { + unsigned char sectors; + unsigned char heads; + unsigned short cylinders; + + struct hd_geometry *geo = (struct hd_geometry *)arg; + if (geo == NULL) return -EINVAL; + + err = verify_area(VERIFY_WRITE, geo, sizeof(*geo)); + if (err) return err; + + sectors = viodasd_devices[device_no].sectors; + if (sectors == 0) + sectors = 32; + + heads = viodasd_devices[device_no].tracks; + if (heads == 0) + heads = 64; + + cylinders = viodasd_devices[device_no].cylinders; + if (cylinders == 0) + cylinders = viodasd_partitions[MINOR(ino->i_rdev)].nr_sects / (sectors * heads); + + put_user(sectors , &geo->sectors ); + put_user(heads , &geo->heads ); + put_user(cylinders, &geo->cylinders); + + put_user(viodasd_partitions[MINOR(ino->i_rdev)].start_sect,(long *)&geo->start); + + return 0; + } + +#define PRTIOC(x) case x: err_printk("got unsupported FD ioctl " #x "\n"); \ + return -EINVAL; + + PRTIOC(FDCLRPRM); + PRTIOC(FDSETPRM); + PRTIOC(FDDEFPRM); + PRTIOC(FDGETPRM); + PRTIOC(FDMSGON); + PRTIOC(FDMSGOFF); + PRTIOC(FDFMTBEG); + PRTIOC(FDFMTTRK); + PRTIOC(FDFMTEND); + PRTIOC(FDSETEMSGTRESH); + PRTIOC(FDSETMAXERRS); + PRTIOC(FDGETMAXERRS); + PRTIOC(FDGETDRVTYP); + PRTIOC(FDSETDRVPRM); + PRTIOC(FDGETDRVPRM); + PRTIOC(FDGETDRVSTAT); + PRTIOC(FDPOLLDRVSTAT); + PRTIOC(FDRESET); + PRTIOC(FDGETFDCSTAT); + PRTIOC(FDWERRORCLR); + PRTIOC(FDWERRORGET); + PRTIOC(FDRAWCMD); + PRTIOC(FDEJECT); + PRTIOC(FDTWADDLE); + + } + + return -EINVAL; +} + +/*************************************************************************** + * Send an actual I/O request to OS/400 + ***************************************************************************/ +static int send_request(struct request *req) +{ + u64 sect_size; + u64 start; + u64 len; + int direction; + int nsg; + u16 viocmd; + HvLpEvent_Rc hvrc; + struct vioblocklpevent *bevent = (struct vioblocklpevent *)vio_block_event_buffer; + struct scatterlist sg[VIOMAXBLOCKDMA]; + struct buffer_head *bh; + int sgindex; + int device_no = DEVICE_NR(req->rq_dev); + int statindex; + + /* Note that this SHOULD always be 512...but lets be architecturally correct */ + sect_size = hardsect_size[MAJOR_NR][device_no]; + + /* Figure out teh starting sector and length */ + start = (req->sector + viodasd_partitions[MINOR(req->rq_dev)].start_sect) * sect_size; + len = req->nr_sectors * sect_size; + + /* More paranoia checks */ + if ((req->sector + req->nr_sectors) > + (viodasd_partitions[MINOR(req->rq_dev)].start_sect + + viodasd_partitions[MINOR(req->rq_dev)].nr_sects)) + { + err_printk("Invalid request offset & length\n"); + err_printk("req->sector: %ld, req->nr_sectors: %ld\n", req->sector, req->nr_sectors); + err_printk("RQ_DEV: %d, minor: %d\n", req->rq_dev, MINOR(req->rq_dev)); + return -1; + } + + if (req->cmd == READ) + { + direction = PCI_DMA_FROMDEVICE; + viocmd = viomajorsubtype_blockio | vioblockread; + statindex = 0; + } + else + { + direction = PCI_DMA_TODEVICE; + viocmd = viomajorsubtype_blockio | vioblockwrite; + statindex = 1; + } + + /* Update totals */ + viod_stats[device_no][statindex].tot++; + + /* Now build the scatter-gather list */ + memset(&sg, 0x00, sizeof(sg)); + sgindex = 0; + + /* See if this is a swap I/O (without a bh pointer) or a regular I/O */ + if (req->bh) + { + /* OK...this loop takes buffers from the request and adds them to the SG + until we're done, or until we hit a maximum. If we hit a maximum we'll + just finish this request later */ + bh = req->bh; + while ((bh) && (sgindex < VIOMAXBLOCKDMA)) + { + sg[sgindex].address = bh->b_data; + sg[sgindex].length = bh->b_size; + + sgindex++; + bh = bh->b_reqnext; + } + nsg = pci_map_sg(NULL, sg, sgindex, direction); + if ((nsg == 0) || (sg[0].dma_length == 0) || (sg[0].dma_address == 0xFFFFFFFF)) + { + err_printk("error getting sg tces\n"); + return -1; + } + + } + else + { + /* Update stats */ + viod_stats[device_no][statindex].nobh++; + + sg[0].dma_address = pci_map_single(NULL, req->buffer, + len, + direction); + if (sg[0].dma_address == 0xFFFFFFFF) + { + err_printk("error allocating tce for address %p len %ld\n", + req->buffer, + (long)len); + return -1; + } + sg[0].dma_length = len; + nsg = 1; + } + + /* Update stats */ + viod_stats[device_no][statindex].ntce[sgindex]++; + + /* This optimization handles a single DMA block */ + if (sgindex == 1) + { + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | viocmd, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)req->buffer, + VIOVERSION << 16, + ((u64)device_no << 48), + start, + ((u64)sg[0].dma_address)<<32, + sg[0].dma_length); + } + else + { + /* Now build up the actual request. Note that we store the pointer */ + /* to the request buffer in the correlation token so we can match */ + /* this response up later */ + memset(bevent,0x00,sizeof(struct vioblocklpevent)); + bevent->event.xFlags.xValid = 1; + bevent->event.xFlags.xFunction = HvLpEvent_Function_Int; + bevent->event.xFlags.xAckInd = HvLpEvent_AckInd_DoAck; + bevent->event.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck; + bevent->event.xType = HvLpEvent_Type_VirtualIo; + bevent->event.xSubtype = viocmd; + bevent->event.xSourceLp = HvLpConfig_getLpIndex(); + bevent->event.xTargetLp = viopath_hostLp; + bevent->event.xSizeMinus1 = offsetof(struct vioblocklpevent, + u.rwData.dmaInfo) + + (sizeof(bevent->u.rwData.dmaInfo[0])*(sgindex))-1; + bevent->event.xSizeMinus1 = sizeof(struct vioblocklpevent)-1; + bevent->event.xSourceInstanceId = viopath_sourceinst(viopath_hostLp); + bevent->event.xTargetInstanceId = viopath_targetinst(viopath_hostLp); + bevent->event.xCorrelationToken = (u64)(unsigned long)req->buffer; + bevent->mVersion = VIOVERSION; + bevent->mDisk = device_no; + bevent->u.rwData.mOffset = start; + + /* Copy just the dma information from the sg list into the request */ + for (sgindex = 0; sgindex < nsg; sgindex++) + { + bevent->u.rwData.dmaInfo[sgindex].mToken = sg[sgindex].dma_address; + bevent->u.rwData.dmaInfo[sgindex].mLen = sg[sgindex].dma_length; + } + + /* Send the request */ + hvrc = HvCallEvent_signalLpEvent(&bevent->event); + } + + if (hvrc != HvLpEvent_Rc_Good) + { + err_printk("hv error on op %d\n",(int)hvrc); + return -1; + } + else + { + /* If the request was successful, bump the number of outstanding */ + numReqOut++; + } + return 0; +} + +/*************************************************************************** + * This is the external request processing routine + ***************************************************************************/ +static void do_viodasd_request(request_queue_t *q) +{ + int device_no; + struct request *req; + for (;;) { + + INIT_REQUEST; + + device_no = CURRENT_DEV; + if (device_no > numdsk) + { + err_printk("Invalid device # %d\n",CURRENT_DEV); + viodasd_end_request(CURRENT, 0); + continue; + } + + if (viodasd_gendsk.sizes == NULL) + { + err_printk("Ouch! viodasd_gendsk.sizes is NULL\n"); + viodasd_end_request(CURRENT, 0); + continue; + } + + /* If the queue is plugged, don't dequeue anything right now */ + if ((q) && (q->plugged)) + { + return; + } + + /* If we already have the maximum number of requests outstanding to OS/400 + just bail out. We'll come back later */ + if (numReqOut >= VIOMAXREQ) + return; + + /* get the current request, then dequeue it from the queue */ + req = CURRENT; + blkdev_dequeue_request(req); + + /* Try sending the request */ + if (send_request(req) == 0) + { + list_add_tail(&req->queue, &reqlist); + } + else + { + viodasd_end_request(req, 0); + } + } +} + +/*************************************************************************** + * Check for changed disks + ***************************************************************************/ +static int viodasd_check_change(kdev_t dev) +{ + struct viodasd_waitevent we; + HvLpEvent_Rc hvrc; + int device_no = DEVICE_NR(dev); + + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) + { + err_printk("Invalid hosting partition\n"); + return -EIO; + } + + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockcheck, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code. If bad, assume no change */ + if (we.rc != 0) + { + err_printk("bad rc on check_change. Assuming no change\n"); + return 0; + } + + return we.changed; +} + +/*************************************************************************** + * Our file operations table + ***************************************************************************/ +static struct block_device_operations viodasd_fops = { + open: viodasd_open, + release: viodasd_release, + ioctl: viodasd_ioctl, + check_media_change: viodasd_check_change, + revalidate: viodasd_revalidate +}; + +/*************************************************************************** + * Our gendisk table + ***************************************************************************/ +struct gendisk viodasd_gendsk = { + 0, /* major - fill in later */ + "viodasd", + PARTITION_SHIFT, + 1<xFlags.xFunction == HvLpEvent_Function_Int) + { + err_printk("Yikes! got an int in viodasd event handler!\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + + switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) + { + + /* Handle a response to an open request. We get all the disk information + * in the response, so update it. The correlation token contains a pointer to + * a waitevent structure that has a semaphore in it. update the return code + * in the waitevent structure and post the semaphore to wake up the guy who + * sent the request */ + case vioblockopen: + pwe = (struct viodasd_waitevent *)(unsigned long)event->xCorrelationToken; + pwe->rc = event->xRc; + if (event->xRc == HvLpEvent_Rc_Good) + { + viodasd_devices[bevent->mDisk].size = bevent->u.openData.mDiskLen; + viodasd_devices[bevent->mDisk].cylinders = bevent->u.openData.mCylinders; + viodasd_devices[bevent->mDisk].tracks = bevent->u.openData.mTracks; + viodasd_devices[bevent->mDisk].sectors = bevent->u.openData.mSectors; + viodasd_devices[bevent->mDisk].bytesPerSector = bevent->u.openData.mBytesPerSector; + viodasd_devices[bevent->mDisk].readOnly = bevent->mFlags & vioblockflags_ro; + + if (viodasd_max_disk != bevent->u.openData.mMaxDisks) + { + viodasd_max_disk = bevent->u.openData.mMaxDisks; + } + } + up(pwe->sem); + break; + + case vioblockclose: + break; + + /* For read and write requests, decrement the number of outstanding requests, + * Free the DMA buffers we allocated, and find the matching request by + * using the buffer pointer we stored in the correlation token. + */ + case vioblockread: + case vioblockwrite: + + /* Free the DMA buffers */ + i = 0; + nsect = 0; + memset(sg, 0x00, sizeof(sg)); + + maxsg = (((bevent->event.xSizeMinus1 + 1) - + offsetof(struct vioblocklpevent, + u.rwData.dmaInfo)) / + sizeof(bevent->u.rwData.dmaInfo[0])); + + + while ((iu.rwData.dmaInfo[i].mLen > 0) && + (i < VIOMAXBLOCKDMA)) + { + sg[i].dma_address = bevent->u.rwData.dmaInfo[i].mToken; + sg[i].dma_length = bevent->u.rwData.dmaInfo[i].mLen; + nsect += bevent->u.rwData.dmaInfo[i].mLen; + i++; + } + + pci_unmap_sg(NULL, + sg, + i, + (bevent->event.xSubtype == (viomajorsubtype_blockio | vioblockread)) ? + PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + + + /* Since this is running in interrupt mode, we need to make sure we're not + * stepping on any global I/O operations + */ + spin_lock_irqsave(&io_request_lock, flags); + + /* Decrement the number of outstanding requests */ + numReqOut--; + + /* Now find the matching request in OUR list (remember we moved the request + * from the global list to our list when we got it) + */ + req = blkdev_entry_to_request(reqlist.next); + while ((&req->queue != &reqlist) && + ((u64)(unsigned long)req->buffer != bevent->event.xCorrelationToken)) + req = blkdev_entry_to_request(req->queue.next); + + if (&req->queue == &reqlist) + { + err_printk("Yikes! Could not find matching buffer %p in reqlist\n", + req->buffer); + break; + } + + /* Remove the request from our list */ + list_del(&req->queue); + + /* Calculate the number of sectors from the length in bytes */ + nsect = nsect >> 9; + if (!req->bh) { + if (event->xRc != HvLpEvent_Rc_Good) + { + err_printk("rw error %d:%d\n",event->xRc,bevent->mSubTypeRc); + viodasd_end_request(req,0); + } + else + { + if (nsect != req->current_nr_sectors) + { + err_printk("Yikes...non bh i/o # sect doesn't match!!!\n"); + } + viodasd_end_request(req, 1); + } + } + else { + while ((nsect > 0) && (req->bh)) + { + nsect -= req->current_nr_sectors; + viodasd_end_request(req, 1); + } + if (nsect) + { + err_printk("Yikes...sectors left over on a request!!!\n"); + } + + /* If the original request could not handle all the buffers, re-send + * the request + */ + if (req->bh) + { + if (send_request(req) == 0) + { + list_add_tail(&req->queue, &reqlist); + } + else + { + viodasd_end_request(req, 0); + } + } + + } + + /* Finally, send more requests */ + do_viodasd_request(NULL); + + spin_unlock_irqrestore(&io_request_lock, flags); + break; + + case vioblockflush: + up((void*)(unsigned long)event->xCorrelationToken); + break; + + case vioblockcheck: + pwe = (struct viodasd_waitevent *)(unsigned long)event->xCorrelationToken; + pwe->rc = event->xRc; + pwe->changed = bevent->u.check.changed; + up(pwe->sem); + break; + + default: + err_printk("invalid subtype!"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +/*************************************************************************** + * This routine tries to clean up anything we allocated/registered + ***************************************************************************/ +static void cleanup2(void) +{ + int i; + +#define CLEANIT(x) if (x) {kfree(x); x=NULL;} + + for (i=0; i + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *************************************************************************** + * This routine provides access to CD ROM drives owned and managed by an + * OS/400 partition running on the same box as this Linux partition. + * + * All operations are performed by sending messages back and forth to + * the OS/400 partition. The format of the messages is defined in + * include/asm-ppc/iSeries/vio.h + * + * This device driver can either use it's own major number, or it can + * pretend to be an AZTECH drive. This is controlled with a + * CONFIG option. You can either call this an elegant solution to the + * fact that a lot of software doesn't recognize a new CD major number... + * or you can call this a really ugly hack. Your choice. + * + */ + +/* +Changelog (absent entries indicate repeats): + Ver Date Who What + ==== =========== =================== ============================== + 2001 Jul 12 devilbis@us.ibm.com moved unitinfo to kernel mem for pci + 1.03 2001 Jul 13 added viopath_close on exit + successfully used as a module + added MOD_(INC|DEC)_USE_COUNT + set version to 1.03 + 2001 Jul 23 restructured do_viocd_request + asynchronous request support: + request queue + by-request end_request + 1.04 2001 Aug 01 multiple concurrent CD requests + upped version to 1.04 +*/ + +#include +#include + +/*************************************************************************** + * Decide if we are using our own major or pretending to be an AZTECH drive + ***************************************************************************/ +#ifdef CONFIG_VIOCD_AZTECH +#define MAJOR_NR AZTECH_CDROM_MAJOR +#define do_viocd_request do_aztcd_request +#else +#define MAJOR_NR VIOCD_MAJOR +#endif + +#define VIOCD_VERS "1.04" + +#include +#include +#include +#include +#include +#include +#include + +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif +#ifndef _ISERIES_PROC_H +#include +#endif + +/*************************************************************************** + * Our internal printk macro + ***************************************************************************/ +#define err_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERR: " fmt, __FILE__, __LINE__ , ## args) + +/*************************************************************************** + * Should probably make this a module parameter....sigh + ***************************************************************************/ +#define VIOCD_MAX_CD 8 +int viocd_blocksizes[VIOCD_MAX_CD]; + +/*************************************************************************** + * This is the structure we use to exchange info between driver and interrupt + * handler + ***************************************************************************/ +struct viocd_waitevent { + struct semaphore *sem; + int rc; + int changed; +}; + +/*************************************************************************** + * These are our internal structures for keeping track of devices + ***************************************************************************/ +static int viocd_numdev; + +struct cdrom_info { + char rsrcname[10]; + char type[4]; + char model[3]; +}; +static struct cdrom_info *viocd_unitinfo = NULL; + +static struct { + u32 useCount; + u32 blocksize; + u32 mediasize; +} viocd_diskinfo[VIOCD_MAX_CD]; + +static struct cdrom_device_info viocd_info[VIOCD_MAX_CD]; + +static spinlock_t viocd_lock = SPIN_LOCK_UNLOCKED; + +#define MAX_CD_REQ 1 +static LIST_HEAD(reqlist); + +/*************************************************************************** + *************************************************************************** + * CODE STARTS HERE + *************************************************************************** + ***************************************************************************/ + +/*************************************************************************** + * End a request + ***************************************************************************/ +static int viocd_end_request(struct request *req, int uptodate) { + if (end_that_request_first(req, uptodate, DEVICE_NAME)) + { + /* printk("we weren't really done with %p\n", req); */ + return 0; + } + end_that_request_last(req); + return 1; +} + + +/*************************************************************************** + * Get info on CD devices from OS/400 + ***************************************************************************/ +static void get_viocd_info(void) +{ + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + int i; + DECLARE_MUTEX_LOCKED(Semaphore); + struct viocd_waitevent we; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return; + + if (viocd_unitinfo == NULL) + viocd_unitinfo = kmalloc(sizeof(struct cdrom_info) * VIOCD_MAX_CD, GFP_KERNEL); + + memset(viocd_unitinfo, 0x00, sizeof(struct cdrom_info) * VIOCD_MAX_CD); + + dmaaddr = pci_map_single(NULL, viocd_unitinfo, + sizeof(struct cdrom_info) * VIOCD_MAX_CD, + PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) + { + err_printk("error allocating tce\n"); + return; + } + + we.sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdgetinfo, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + dmaaddr, + 0, + sizeof(struct cdrom_info) * VIOCD_MAX_CD, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + err_printk("hv error on op %d\n", (int)hvrc); + return; + } + + down(&Semaphore); + + if (we.rc) + { + err_printk("bad rc %d on getinfo\n", we.rc); + return; + } + + + for (i=0; + ((i < VIOCD_MAX_CD) && (viocd_unitinfo[i].rsrcname[0])); + i++) + { + viocd_numdev++; + } +} + +/*************************************************************************** + * Open a device + ***************************************************************************/ +static int viocd_open(struct cdrom_device_info * cdi, int purpose) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + int device_no = MINOR(cdi->dev); + HvLpEvent_Rc hvrc; + struct viocd_waitevent we; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return -ENODEV; + + if (device_no >= viocd_numdev) + return -ENODEV; + + we.sem = &Semaphore; + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n", (int)hvrc); + return -EIO; + } + + down(&Semaphore); + + if (we.rc) + return -EIO; + + if (viocd_diskinfo[device_no].useCount == 0) + { + if (viocd_diskinfo[device_no].blocksize > 0) + { + viocd_blocksizes[device_no] = viocd_diskinfo[device_no].blocksize; + } + } + MOD_INC_USE_COUNT; + return 0; +} + +/*************************************************************************** + * Release a device + ***************************************************************************/ +static void viocd_release(struct cdrom_device_info * cdi) +{ + int device_no = MINOR(cdi->dev); + HvLpEvent_Rc hvrc; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid || device_no >= viocd_numdev) + return; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdclose, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n", (int)hvrc); + return; + } + + MOD_DEC_USE_COUNT; +} + +/*************************************************************************** + * Send a read or write request to OS/400 + ***************************************************************************/ +static int send_request(struct request *req) +{ + HvLpEvent_Rc hvrc; + dma_addr_t dmaaddr; + int device_no = DEVICE_NR(req->rq_dev); + long start = req->sector * 512, + len = req->current_nr_sectors * 512; + char reading = req->cmd == READ; + + dmaaddr = pci_map_single(NULL, req->buffer, len, + reading ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + if (dmaaddr == 0xFFFFFFFF) + { + err_printk("error allocating tce for address %p len %ld\n", + req->buffer, len); + return -1; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | + /* FIXME: add viocdwrite */ (reading ? viocdread : viocdread), + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)req->buffer, + VIOVERSION << 16, + ((u64)device_no << 48) | + dmaaddr, + start, + len, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + err_printk("hv error on op %d\n", (int)hvrc); + return -1; + } + return 0; +} + + +/*************************************************************************** + * Do a request + ***************************************************************************/ +static int rwreq; +static void do_viocd_request(request_queue_t *q) +{ + for (;;) { + struct request *req; + char err_str[80] = ""; + int device_no; + + INIT_REQUEST; + if (rwreq >= MAX_CD_REQ) { + /* printk("viocd: already %d requests--no more\n", rwreq); */ + return; + } + + device_no = CURRENT_DEV; + + /* remove the current request from the queue */ + req = CURRENT; + blkdev_dequeue_request(req); + + /* check for any kind of error */ + if (req->cmd != READ) + strcpy(err_str, "Invalid write request for CD"); + else if (device_no > viocd_numdev) + sprintf(err_str, "Invalid device number %d", device_no); +/* + else if (rwreq > 0) + strcpy(err_str, "multiple r/w req!"); +*/ + else if(send_request(req) < 0) + strcpy(err_str, "unable to send message to OS/400!"); + + /* if we had any sort of error, log it and cancel the request */ + if (*err_str) + { + err_printk("%s\n", err_str); + viocd_end_request(req, 0); + } + else + { + spin_lock(&viocd_lock); + list_add_tail(&req->queue, &reqlist); + ++rwreq; + /* printk("enqueued req %p for total of %d\n", req->buffer, rwreq); */ + spin_unlock(&viocd_lock); + } + } +} + +/*************************************************************************** + * Check if the CD changed + ***************************************************************************/ +static int viocd_media_changed(struct cdrom_device_info * cdi, int disc_nr) +{ + struct viocd_waitevent we; + HvLpEvent_Rc hvrc; + int device_no = MINOR(cdi->dev); + + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) + { + err_printk("Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdcheck, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n", (int)hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code. If bad, assume no change */ + if (we.rc != 0) + { + err_printk("bad rc on check_change. Assuming no change\n"); + return 0; + } + + return we.changed; +} + +/*************************************************************************** + * This routine handles incoming CD LP events + ***************************************************************************/ +static void vioHandleCDEvent(struct HvLpEvent *event) +{ + struct viocdlpevent *bevent = (struct viocdlpevent *)event; + struct viocd_waitevent *pwe; + + if (event == NULL) + { + /* Notification that a partition went away! */ + return; + } + + // First, we should NEVER get an int here...only acks + if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + err_printk("Yikes! got an int in viocd event handler!\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + + switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) + { + case viocdgetinfo: + case viocdopen: + pwe = (struct viocd_waitevent *)(u32)event->xCorrelationToken; + pwe->rc = event->xRc; + viocd_diskinfo[bevent->mDisk].blocksize = bevent->mBlockSize; + viocd_diskinfo[bevent->mDisk].mediasize = bevent->mMediaSize; + + up(pwe->sem); + break; + + case viocdclose: + break; + + case viocdread: { + int flags; + struct request *req = blkdev_entry_to_request(reqlist.next); + + /* Since this is running in interrupt mode, we need to make sure we're not + * stepping on any global I/O operations + */ + spin_lock_irqsave(&io_request_lock, flags); + + + pci_unmap_single(NULL, + bevent->mToken, + bevent->mLen, + PCI_DMA_FROMDEVICE); + + /* find the event to which this is a response */ + while ((&req->queue != &reqlist) && + ((u64)(u32)req->buffer != bevent->event.xCorrelationToken)) + req = blkdev_entry_to_request(req->queue.next); + + /* if the event was not there, then what are we responding to?? */ + if(&req->queue == &reqlist) + { + err_printk("Yikes! we didn't ever enqueue this guy!\n"); + spin_unlock_irqrestore(&io_request_lock, flags); + break; + } + + /* we don't need to keep it around anymore... */ + spin_lock(&viocd_lock); + list_del(&req->queue); + --rwreq; + /* printk("down requests to %d (dequeued req %p)\n", rwreq, req->buffer); */ + spin_unlock(&viocd_lock); + + { + char stat = event->xRc == HvLpEvent_Rc_Good; + int nsect = bevent->mLen >> 9; + + if(!stat) + err_printk("request %p failed with rc %d:0x%08x\n", req->buffer, event->xRc, bevent->mSubTypeRc); + while((nsect > 0) && (req->bh)) + { + nsect -= req->current_nr_sectors; + viocd_end_request(req, stat); + } + /* we weren't done yet */ + if(req->bh) + { + if(send_request(req) < 0) + { + err_printk("couldn't re-submit req %p\n", req->buffer); + viocd_end_request(req, 0); + } + else + { + spin_lock(&viocd_lock); + list_add_tail(&req->queue, &reqlist); + ++rwreq; + /* printk("re-enqueued req %p for %d total\n", req->buffer, rwreq); */ + spin_unlock(&viocd_lock); + } + } + } + + /* FIXME: why is this here?? */ + do_viocd_request(NULL); + spin_unlock_irqrestore(&io_request_lock, flags); + break; + } + case viocdcheck: + pwe = (struct viocd_waitevent *)(u32)event->xCorrelationToken; + pwe->rc = event->xRc; + pwe->changed = bevent->mFlags; + up(pwe->sem); + break; + + default: + err_printk("invalid subtype!"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +/*************************************************************************** + * Our file operations table + ***************************************************************************/ +static struct cdrom_device_ops viocd_dops = { + open: viocd_open, + release: viocd_release, + media_changed: viocd_media_changed, + capability: CDC_CD_R +}; + +/*************************************************************************** + * Handle reads from the proc file system + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + int i; + + for (i=0; inlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viocd_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viocd", iSeries_proc); +} + +/*************************************************************************** + * Initialize the whole device driver. Handle module and non-module + * versions + ***************************************************************************/ +__init int viocd_init(void) +{ + int i; + int rc; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return -ENODEV; + + rc = viopath_open(viopath_hostLp, viomajorsubtype_cdio); + if (rc) + { + err_printk("error opening path to host partition %d\n", viopath_hostLp); + return rc; + } + + /* + * Initialize our request handler + */ + rwreq = 0; + vio_setCDHandler(vioHandleCDEvent); + +/* memset(viocd_unitinfo, 0x00, sizeof(struct cdrom_info) * VIOCD_MAX_CD); */ + memset(viocd_diskinfo, 0x00, sizeof(viocd_diskinfo)); + + get_viocd_info(); + + if (viocd_numdev == 0) + { + viopath_close(viopath_hostLp, viomajorsubtype_cdio); + return 0; + } + + printk("%s: iSeries Virtual CD vers %s, major %d, max disks %d, hosting partition %d\n", + DEVICE_NAME, VIOCD_VERS, MAJOR_NR, VIOCD_MAX_CD, viopath_hostLp); + + if (devfs_register_blkdev(MAJOR_NR, "viocd", &cdrom_fops) != 0) + { + printk("Unable to get major %d for viocd CD-ROM\n", + MAJOR_NR); + return -EIO; + } + + blksize_size[MAJOR_NR] = viocd_blocksizes; + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); + read_ahead[MAJOR_NR] = 4; + + memset(&viocd_info, 0x00, sizeof(viocd_info)); + for (i=0; iprocess_char_map) || tty->lnext) { + if (!test_bit(c, tty->process_char_map) || tty->lnext) { finish_erasing(tty); tty->lnext = 0; if (L_ECHO(tty)) { @@ -659,7 +659,7 @@ handle_newline: spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, &tty->read_flags); + set_bit(tty->read_head, tty->read_flags); put_tty_queue_nolock(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; @@ -811,38 +811,38 @@ memset(tty->process_char_map, 0, 256/8); if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', &tty->process_char_map); + set_bit('\r', tty->process_char_map); if (I_INLCR(tty)) - set_bit('\n', &tty->process_char_map); + set_bit('\n', tty->process_char_map); if (L_ICANON(tty)) { - set_bit(ERASE_CHAR(tty), &tty->process_char_map); - set_bit(KILL_CHAR(tty), &tty->process_char_map); - set_bit(EOF_CHAR(tty), &tty->process_char_map); - set_bit('\n', &tty->process_char_map); - set_bit(EOL_CHAR(tty), &tty->process_char_map); + set_bit(ERASE_CHAR(tty), tty->process_char_map); + set_bit(KILL_CHAR(tty), tty->process_char_map); + set_bit(EOF_CHAR(tty), tty->process_char_map); + set_bit('\n', tty->process_char_map); + set_bit(EOL_CHAR(tty), tty->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); set_bit(LNEXT_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); set_bit(EOL2_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); } } if (I_IXON(tty)) { - set_bit(START_CHAR(tty), &tty->process_char_map); - set_bit(STOP_CHAR(tty), &tty->process_char_map); + set_bit(START_CHAR(tty), tty->process_char_map); + set_bit(STOP_CHAR(tty), tty->process_char_map); } if (L_ISIG(tty)) { - set_bit(INTR_CHAR(tty), &tty->process_char_map); - set_bit(QUIT_CHAR(tty), &tty->process_char_map); - set_bit(SUSP_CHAR(tty), &tty->process_char_map); + set_bit(INTR_CHAR(tty), tty->process_char_map); + set_bit(QUIT_CHAR(tty), tty->process_char_map); + set_bit(SUSP_CHAR(tty), tty->process_char_map); } - clear_bit(__DISABLED_CHAR, &tty->process_char_map); + clear_bit(__DISABLED_CHAR, tty->process_char_map); sti(); tty->raw = 0; tty->real_raw = 0; @@ -1058,7 +1058,7 @@ int eol; eol = test_and_clear_bit(tty->read_tail, - &tty->read_flags); + tty->read_flags); c = tty->read_buf[tty->read_tail]; spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = ((tty->read_tail+1) & diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/char/tty_io.c linuxppc64_2_4/drivers/char/tty_io.c --- linux-2.4.8-ac9/drivers/char/tty_io.c Thu Aug 23 09:17:21 2001 +++ linuxppc64_2_4/drivers/char/tty_io.c Fri Aug 10 13:08:57 2001 @@ -2237,6 +2237,11 @@ * set up the console device so that later boot sequences can * inform about problems etc.. */ + +#ifdef CONFIG_VIOCONS + viocons_init(); +#endif + #ifdef CONFIG_VT con_init(); #endif @@ -2334,6 +2339,10 @@ /* console calls tty_register_driver() before kmalloc() works. * Thus, we can't devfs_register() then. Do so now, instead. */ +#ifdef CONFIG_VIOCONS + viocons_init2(); +#endif + #ifdef CONFIG_VT con_init_devfs(); #endif diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/char/viocons.c linuxppc64_2_4/drivers/char/viocons.c --- linux-2.4.8-ac9/drivers/char/viocons.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/char/viocons.c Thu Jul 19 14:02:56 2001 @@ -0,0 +1,1407 @@ +/* + * drivers/char/viocons.c + * + * iSeries Virtual Terminal + * + * Author: Dave Boutcher + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif + +#ifndef _HVLPEVENT_H +#include +#endif + +#ifndef _HVCALLEVENT_H +#include "asm/iSeries/HvCallEvent.h" +#endif +#ifndef _HVLPCONFIG_H +#include "asm/iSeries/HvLpConfig.h" +#endif +#include "asm/iSeries/HvCall.h" +#ifndef _ISERIES_PROC_H +#include +#endif + +/*************************************************************************** + * Check that the tty_driver_data actually points to our stuff + ***************************************************************************/ +#define VIOTTY_PARANOIA_CHECK 1 +#define VIOTTY_MAGIC (0x0DCB) + +static int debug; + +static DECLARE_WAIT_QUEUE_HEAD(viocons_wait_queue); + +static int viotty_major = 229; + +#define VTTY_PORTS 10 +#define VIOTTY_SERIAL_START 65 + +static u64 sndMsgSeq[VTTY_PORTS]; +static u64 sndMsgAck[VTTY_PORTS]; + +static spinlock_t consolelock; + +/*************************************************************************** + * When we get writes faster than we can send it to the partition, + * buffer the data here. There is one set of buffers for each virtual + * port. + ***************************************************************************/ +/* Note that bufferUsed is a bit map of used buffers. + * It had better have enough bits to hold NUM_BUF + * the bitops assume it is a multiple of unsigned long + */ +#define NUM_BUF (8) +#define OVERFLOW_SIZE VIOCHAR_MAX_DATA + +static struct overflowBuffers { + unsigned long bufferUsed; + u8 *buffer[NUM_BUF]; + int bufferBytes[NUM_BUF]; + int curbuf; + int bufferOverflow; + int overflowMessage; +} overflow[VTTY_PORTS]; + +static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp); + +static struct tty_driver viotty_driver; +static struct tty_driver viottyS_driver; +static int viotty_refcount; + +static struct tty_struct *viotty_table[VTTY_PORTS]; +static struct tty_struct *viottyS_table[VTTY_PORTS]; +static struct termios *viotty_termios[VTTY_PORTS]; +static struct termios *viottyS_termios[VTTY_PORTS]; +static struct termios *viotty_termios_locked[VTTY_PORTS]; +static struct termios *viottyS_termios_locked[VTTY_PORTS]; + +void hvlog(char *fmt, ...) +{ + int i; + static char buf[256]; + va_list args; + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + HvCall_writeLogBuffer(buf, i); + HvCall_writeLogBuffer("\r",1); + +} + +/*************************************************************************** + * Our port information. We store a pointer to one entry in the + * tty_driver_data + ***************************************************************************/ +static struct port_info_tag { + int magic; + struct tty_struct *tty; + HvLpIndex lp; + u8 vcons; + u8 port; +} port_info[VTTY_PORTS]; + +/*************************************************************************** + * Make sure we're pointing to a valid port_info structure. Shamelessly + * plagerized from serial.c + ***************************************************************************/ +static inline int viotty_paranoia_check(struct port_info_tag *pi, + kdev_t device, const char *routine) +{ +#ifdef VIOTTY_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for port_info struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null port_info for (%s) in %s\n"; + + if (!pi) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (pi->magic != VIOTTY_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +/*************************************************************************** + * Handle reads from the proc file system. Right now we just dump the + * state of the first TTY + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + struct tty_struct *tty = viotty_table[0]; + struct termios *termios; + if (tty == NULL) + { + len += sprintf(buf+len,"no tty\n"); + *eof = 1; + return len; + } + + len += sprintf(buf+len,"tty info: COOK_OUT %ld COOK_IN %ld, NO_WRITE_SPLIT %ld\n", + tty->flags & TTY_HW_COOK_OUT, + tty->flags & TTY_HW_COOK_IN, + tty->flags & TTY_NO_WRITE_SPLIT); + + termios = tty->termios; + if (termios == NULL) + { + len += sprintf(buf+len,"no termios\n"); + *eof = 1; + return len; + } +len+= sprintf(buf+len,"INTR_CHAR %2.2x\n",INTR_CHAR(tty)); +len+= sprintf(buf+len,"QUIT_CHAR %2.2x\n",QUIT_CHAR(tty)); +len+= sprintf(buf+len,"ERASE_CHAR %2.2x\n",ERASE_CHAR(tty)); +len+= sprintf(buf+len,"KILL_CHAR %2.2x\n",KILL_CHAR(tty)); +len+= sprintf(buf+len,"EOF_CHAR %2.2x\n",EOF_CHAR(tty)); +len+= sprintf(buf+len,"TIME_CHAR %2.2x\n",TIME_CHAR(tty)); +len+= sprintf(buf+len,"MIN_CHAR %2.2x\n",MIN_CHAR(tty)); +len+= sprintf(buf+len,"SWTC_CHAR %2.2x\n",SWTC_CHAR(tty)); +len+= sprintf(buf+len,"START_CHAR %2.2x\n",START_CHAR(tty)); +len+= sprintf(buf+len,"STOP_CHAR %2.2x\n",STOP_CHAR(tty)); +len+= sprintf(buf+len,"SUSP_CHAR %2.2x\n",SUSP_CHAR(tty)); +len+= sprintf(buf+len,"EOL_CHAR %2.2x\n",EOL_CHAR(tty)); +len+= sprintf(buf+len,"REPRINT_CHAR %2.2x\n",REPRINT_CHAR(tty)); +len+= sprintf(buf+len,"DISCARD_CHAR %2.2x\n",DISCARD_CHAR(tty)); +len+= sprintf(buf+len,"WERASE_CHAR %2.2x\n",WERASE_CHAR(tty)); +len+= sprintf(buf+len,"LNEXT_CHAR %2.2x\n",LNEXT_CHAR(tty)); +len+= sprintf(buf+len,"EOL2_CHAR %2.2x\n",EOL2_CHAR(tty)); + +len+= sprintf(buf+len,"I_IGNBRK %4.4x\n",I_IGNBRK(tty)); +len+= sprintf(buf+len,"I_BRKINT %4.4x\n",I_BRKINT(tty)); +len+= sprintf(buf+len,"I_IGNPAR %4.4x\n",I_IGNPAR(tty)); +len+= sprintf(buf+len,"I_PARMRK %4.4x\n",I_PARMRK(tty)); +len+= sprintf(buf+len,"I_INPCK %4.4x\n",I_INPCK(tty)); +len+= sprintf(buf+len,"I_ISTRIP %4.4x\n",I_ISTRIP(tty)); +len+= sprintf(buf+len,"I_INLCR %4.4x\n",I_INLCR(tty)); +len+= sprintf(buf+len,"I_IGNCR %4.4x\n",I_IGNCR(tty)); +len+= sprintf(buf+len,"I_ICRNL %4.4x\n",I_ICRNL(tty)); +len+= sprintf(buf+len,"I_IUCLC %4.4x\n",I_IUCLC(tty)); +len+= sprintf(buf+len,"I_IXON %4.4x\n",I_IXON(tty)); +len+= sprintf(buf+len,"I_IXANY %4.4x\n",I_IXANY(tty)); +len+= sprintf(buf+len,"I_IXOFF %4.4x\n",I_IXOFF(tty)); +len+= sprintf(buf+len,"I_IMAXBEL %4.4x\n",I_IMAXBEL(tty)); + +len+= sprintf(buf+len,"O_OPOST %4.4x\n",O_OPOST(tty)); +len+= sprintf(buf+len,"O_OLCUC %4.4x\n",O_OLCUC(tty)); +len+= sprintf(buf+len,"O_ONLCR %4.4x\n",O_ONLCR(tty)); +len+= sprintf(buf+len,"O_OCRNL %4.4x\n",O_OCRNL(tty)); +len+= sprintf(buf+len,"O_ONOCR %4.4x\n",O_ONOCR(tty)); +len+= sprintf(buf+len,"O_ONLRET %4.4x\n",O_ONLRET(tty)); +len+= sprintf(buf+len,"O_OFILL %4.4x\n",O_OFILL(tty)); +len+= sprintf(buf+len,"O_OFDEL %4.4x\n",O_OFDEL(tty)); +len+= sprintf(buf+len,"O_NLDLY %4.4x\n",O_NLDLY(tty)); +len+= sprintf(buf+len,"O_CRDLY %4.4x\n",O_CRDLY(tty)); +len+= sprintf(buf+len,"O_TABDLY %4.4x\n",O_TABDLY(tty)); +len+= sprintf(buf+len,"O_BSDLY %4.4x\n",O_BSDLY(tty)); +len+= sprintf(buf+len,"O_VTDLY %4.4x\n",O_VTDLY(tty)); +len+= sprintf(buf+len,"O_FFDLY %4.4x\n",O_FFDLY(tty)); + +len+= sprintf(buf+len,"C_BAUD %4.4x\n",C_BAUD(tty)); +len+= sprintf(buf+len,"C_CSIZE %4.4x\n",C_CSIZE(tty)); +len+= sprintf(buf+len,"C_CSTOPB %4.4x\n",C_CSTOPB(tty)); +len+= sprintf(buf+len,"C_CREAD %4.4x\n",C_CREAD(tty)); +len+= sprintf(buf+len,"C_PARENB %4.4x\n",C_PARENB(tty)); +len+= sprintf(buf+len,"C_PARODD %4.4x\n",C_PARODD(tty)); +len+= sprintf(buf+len,"C_HUPCL %4.4x\n",C_HUPCL(tty)); +len+= sprintf(buf+len,"C_CLOCAL %4.4x\n",C_CLOCAL(tty)); +len+= sprintf(buf+len,"C_CRTSCTS %4.4x\n",C_CRTSCTS(tty)); + +len+= sprintf(buf+len,"L_ISIG %4.4x\n",L_ISIG(tty)); +len+= sprintf(buf+len,"L_ICANON %4.4x\n",L_ICANON(tty)); +len+= sprintf(buf+len,"L_XCASE %4.4x\n",L_XCASE(tty)); +len+= sprintf(buf+len,"L_ECHO %4.4x\n",L_ECHO(tty)); +len+= sprintf(buf+len,"L_ECHOE %4.4x\n",L_ECHOE(tty)); +len+= sprintf(buf+len,"L_ECHOK %4.4x\n",L_ECHOK(tty)); +len+= sprintf(buf+len,"L_ECHONL %4.4x\n",L_ECHONL(tty)); +len+= sprintf(buf+len,"L_NOFLSH %4.4x\n",L_NOFLSH(tty)); +len+= sprintf(buf+len,"L_TOSTOP %4.4x\n",L_TOSTOP(tty)); +len+= sprintf(buf+len,"L_ECHOCTL %4.4x\n",L_ECHOCTL(tty)); +len+= sprintf(buf+len,"L_ECHOPRT %4.4x\n",L_ECHOPRT(tty)); +len+= sprintf(buf+len,"L_ECHOKE %4.4x\n",L_ECHOKE(tty)); +len+= sprintf(buf+len,"L_FLUSHO %4.4x\n",L_FLUSHO(tty)); +len+= sprintf(buf+len,"L_PENDIN %4.4x\n",L_PENDIN(tty)); +len+= sprintf(buf+len,"L_IEXTEN %4.4x\n",L_IEXTEN(tty)); + + *eof = 1; + return len; +} + +/*************************************************************************** + * Handle writes to our proc file system. Right now just turns on and off + * our debug flag + ***************************************************************************/ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + if (count) + { + if (buffer[0] == '1') + { + printk("viocons: debugging on\n"); + debug = 1; + } + else + { + printk("viocons: debugging off\n"); + debug = 0; + } + } + return count; +} + +/*************************************************************************** + * setup our proc file system entries + ***************************************************************************/ +void viocons_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = create_proc_entry("viocons", S_IFREG|S_IRUSR, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viocons_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viocons", iSeries_proc); +} + +/*************************************************************************** + * Add data to our pending-send buffers + ***************************************************************************/ +static int bufferAdd(u8 port, const char *buf, size_t len, int userFlag) +{ + size_t bleft = len; + size_t curlen; + char *cbuf = (char *)buf; + int nextbuf; + struct overflowBuffers *pov = &overflow[port]; + while (bleft > 0) + { + /* + * If there is no space left in the current buffer, we have + * filled everything up, so return. If we filled the previous + * buffer we would already have moved to the next one. + */ + if (pov->bufferBytes[pov->curbuf] == OVERFLOW_SIZE) + { + hvlog("buffer %d full. no more space\n",pov->curbuf); + pov->bufferOverflow++; + pov->overflowMessage = 1; + return len - bleft; + } + + /* + * Turn on the "used" bit for this buffer. If it's already on, that's + * fine. + */ + set_bit(pov->curbuf,&pov->bufferUsed); + + /* + * See if this buffer has been allocated. If not, allocate it + */ + if (pov->buffer[pov->curbuf] == NULL) + pov->buffer[pov->curbuf] = + kmalloc(OVERFLOW_SIZE, GFP_ATOMIC); + + /* + * Figure out how much we can copy into this buffer + */ + if (bleft < (OVERFLOW_SIZE - pov->bufferBytes[pov->curbuf])) + curlen = bleft; + else + curlen = OVERFLOW_SIZE - pov->bufferBytes[pov->curbuf]; + + /* + * Copy the data into the buffer + */ + if (userFlag) + copy_from_user(pov->buffer[pov->curbuf]+ + pov->bufferBytes[pov->curbuf],cbuf,curlen); + else + memcpy(pov->buffer[pov->curbuf]+ + pov->bufferBytes[pov->curbuf],cbuf,curlen); + + pov->bufferBytes[pov->curbuf] += curlen; + cbuf += curlen; + bleft -= curlen; + + /* + * Now see if we've filled this buffer + */ + if (pov->bufferBytes[pov->curbuf] == OVERFLOW_SIZE) + { + nextbuf = (pov->curbuf+1)%NUM_BUF; + + /* + * Move to the next buffer if it hasn't been used yet + */ + if (test_bit(nextbuf,&pov->bufferUsed) == 0) + { + pov->curbuf = nextbuf; + } + } + } + return len; +} + +/*************************************************************************** + * Send pending data + ***************************************************************************/ +void sendBuffers(u8 port, HvLpIndex lp) +{ + HvLpEvent_Rc hvrc; + int nextbuf; + struct viocharlpevent *viochar = (struct viocharlpevent *)vio_char_event_buffer; + int flags; + struct overflowBuffers *pov = &overflow[port]; + + spin_lock_irqsave(&consolelock, + flags); + + if (pov->bufferUsed == 0) + { + hvlog("in sendbuffers, but no buffers used\n"); + spin_unlock_irqrestore(&consolelock, + flags); + + return; + } + + /* + * curbuf points to the buffer we're filling. We want to start sending AFTER + * this one. + */ + nextbuf = (pov->curbuf + 1) % NUM_BUF; + + /* + * Loop until we find a buffer with the bufferUsed bit on + */ + while (test_bit(nextbuf,&pov->bufferUsed) == 0) + nextbuf = (nextbuf + 1) % NUM_BUF; + + initDataEvent(viochar, lp); + + /* + * While we have buffers with data, and our send window is open, send them + */ + while ((test_bit(nextbuf, &pov->bufferUsed)) && + ((sndMsgSeq[port] - sndMsgAck[port]) immediateDataLen = pov->bufferBytes[nextbuf]; + viochar->event.xCorrelationToken = sndMsgSeq[port]++; + viochar->event.xSizeMinus1 = offsetof(struct viocharlpevent, immediateData) + + viochar->immediateDataLen; + + memcpy(viochar->immediateData,pov->buffer[nextbuf],viochar->immediateDataLen); + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) + { + /* + * MUST unlock the spinlock before doing a printk + */ + spin_unlock_irqrestore(&consolelock, + flags); + + printk("viocons: error sending event! %d\n",(int)hvrc); + return; + } + + /* + * clear the bufferUsed bit, zero the number of bytes in this buffer, + * and move to the next buffer + */ + clear_bit(nextbuf, &pov->bufferUsed); + pov->bufferBytes[nextbuf] = 0; + nextbuf = (nextbuf + 1) % NUM_BUF; + } + + + /* + * If we have emptied all the buffers, start at 0 again. + * this will re-use any allocated buffers + */ + if (pov->bufferUsed == 0) + { + pov->curbuf = 0; + + if (pov->overflowMessage) + pov->overflowMessage = 0; + + if (port_info[port].tty) + { + if ((port_info[port].tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + (port_info[port].tty->ldisc.write_wakeup)) + (port_info[port].tty->ldisc.write_wakeup) (port_info[port].tty); + wake_up_interruptible(&port_info[port].tty->write_wait); + } + } + + spin_unlock_irqrestore(&consolelock, + flags); + +} + +/*************************************************************************** + * Our internal writer. Gets called both from the console device and + * the tty device. the tty pointer will be NULL if called from the console. + ***************************************************************************/ +static int internal_write(struct tty_struct * tty, const char *buf, size_t len, int userFlag) +{ + HvLpEvent_Rc hvrc; + size_t bleft = len; + size_t curlen; + const char *curbuf = buf; + struct viocharlpevent *viochar = (struct viocharlpevent *)vio_char_event_buffer; + unsigned long flags; + struct port_info_tag * pi = NULL; + HvLpIndex lp; + u8 port; + + if (tty) + { + pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_internal_write")) + return -ENODEV; + + lp = pi->lp; + port = pi->port; + } + else + { + /* + * If this is the console device, use the lp from the first port entry + */ + port = 0; + lp = port_info[0].lp; + } + + /* + * Always put console output in the hypervisor console log + */ + if (port == 0) + HvCall_writeLogBuffer(buf, len); + + /* + * If the path to this LP is closed, don't bother doing anything more. + * just dump the data on the floor + */ + if (!viopath_isactive(lp)) + return len; + + /* + * If there is already data queued for this port, send it + */ + if (overflow[port].bufferUsed) + sendBuffers(port, lp); + + spin_lock_irqsave(&consolelock, + flags); + + initDataEvent(viochar, lp); + + /* Got the lock, don't cause console output */ + while ((bleft > 0) && + (overflow[port].bufferUsed == 0) && + ((sndMsgSeq[port] - sndMsgAck[port]) VIOCHAR_MAX_DATA) + curlen = VIOCHAR_MAX_DATA; + else + curlen = bleft; + + viochar->immediateDataLen = curlen; + viochar->event.xCorrelationToken = sndMsgSeq[port]++; + + if (userFlag) + copy_from_user(viochar->immediateData,curbuf,curlen); + else + memcpy(viochar->immediateData,curbuf,curlen); + + viochar->event.xSizeMinus1 = offsetof(struct viocharlpevent, immediateData) + + curlen; + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) + { + /* + * MUST unlock the spinlock before doing a printk + */ + spin_unlock_irqrestore(&consolelock, + flags); + + printk("viocons: error sending event! %d\n",(int)hvrc); + return len - bleft; + } + + curbuf += curlen; + bleft -= curlen; + } + + /* + * If we didn't send it all, buffer it + */ + if (bleft > 0) + { + bleft -= bufferAdd(port, curbuf,bleft, userFlag); + } + spin_unlock_irqrestore(&consolelock, + flags); + + return len - bleft; +} + +/*************************************************************************** + * Initialize the common fields in a charLpEvent + ***************************************************************************/ +static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp) +{ + memset(viochar, 0x00, sizeof(struct viocharlpevent)); + + viochar->event.xFlags.xValid = 1; + viochar->event.xFlags.xFunction = HvLpEvent_Function_Int; + viochar->event.xFlags.xAckInd = HvLpEvent_AckInd_NoAck; + viochar->event.xFlags.xAckType = HvLpEvent_AckType_DeferredAck; + viochar->event.xType = HvLpEvent_Type_VirtualIo; + viochar->event.xSubtype = viomajorsubtype_chario | viochardata; + viochar->event.xSourceLp = HvLpConfig_getLpIndex(); + viochar->event.xTargetLp = lp; + viochar->event.xSizeMinus1 = sizeof(struct viocharlpevent); + viochar->event.xSourceInstanceId = viopath_sourceinst(lp); + viochar->event.xTargetInstanceId = viopath_targetinst(lp); +} + + +/*************************************************************************** + * console device write + ***************************************************************************/ +static void viocons_write(struct console *co, const char *s, + unsigned count) +{ + /*** Ryan S. Arnold : ryanarn@us.ibm.com *** 05/03/2001 *********************** + * This parser will ensure that all single instances of either \n or \r are + * matched into carriage return/line feed combinations. It also allows for + * instances where there already exist \n\r combinations as well as the + * reverse, \r\n combinations. + */ + + int index; + char charptr[1]; + int foundcr; + int slicebegin; + int sliceend; + + foundcr = 0; + slicebegin = 0; + sliceend = 0; + + for ( index = 0; index < count; index++ ) + { + if( !foundcr && s[index] == 0x0a ) + { + if ( (slicebegin - sliceend > 0) && sliceend < count ) + { + internal_write(NULL, &s[slicebegin],sliceend - slicebegin, 0 ); + slicebegin = sliceend; + } + charptr[0] = '\r'; + internal_write(NULL, charptr, 1, 0); + } + if( foundcr && s[index] != 0x0a ) + { + if ( ( index - 2 ) >= 0 ) + { + if ( s[index - 2] != 0x0a ) + { + internal_write(NULL, &s[slicebegin],sliceend - slicebegin, 0 ); + slicebegin = sliceend; + charptr[0] = '\n'; + internal_write(NULL, charptr, 1, 0); + } + } + } + sliceend++; + + if( s[index] == 0x0d ) + foundcr = 1; + else + foundcr = 0; + } + + internal_write(NULL, &s[slicebegin], sliceend - slicebegin, 0 ); + + if ( count > 1) + { + if ( foundcr == 1 && s[count-1] != 0x0a ) + { + charptr[0] = '\n'; + internal_write(NULL, charptr, 1, 0); + } + else if ( s[count-1] == 0x0a && s[count-2] != 0x0d ) + { + + charptr[0] = '\r'; + internal_write(NULL, charptr, 1, 0); + } + } +} + +/*************************************************************************** + * Work out a the device associate with this console + ***************************************************************************/ +static kdev_t viocons_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, c->index + viotty_driver.minor_start); +} + +/*************************************************************************** + * console device read method + ***************************************************************************/ +static int viocons_read( struct console *co, const char *s, + unsigned count ) +{ + printk("In viocons_read\n"); + // Implement me + interruptible_sleep_on( &viocons_wait_queue ); + return 0; +} + +/*************************************************************************** + * console device wait until a key is pressed + ***************************************************************************/ +static int viocons_wait_key( struct console *co ) +{ + printk("In viocons_wait_key\n"); + // Implement me + interruptible_sleep_on( &viocons_wait_queue ); + return 0; +} + +/*************************************************************************** + * Do console device setup + ***************************************************************************/ +static int __init viocons_setup(struct console *co, char *options) +{ + printk("viocons: in viocons_setup\n"); + spin_lock_init(&consolelock); + return 0; +} + +/*************************************************************************** + * console device I/O methods + ***************************************************************************/ +static struct console viocons = { + name: "ttyS", + write: viocons_write, + read: viocons_read, + device: viocons_device, + wait_key: viocons_wait_key, + setup: viocons_setup, + flags: CON_PRINTBUFFER, +}; + + +/*************************************************************************** + * TTY Open method + ***************************************************************************/ +static int viotty_open(struct tty_struct *tty, struct file * filp) +{ + int port; + unsigned long flags; + MOD_INC_USE_COUNT; + port = MINOR(tty->device) - tty->driver.minor_start; + + if (port >= VIOTTY_SERIAL_START) + port -= VIOTTY_SERIAL_START; + + if ((port < 0) || (port >= VTTY_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + spin_lock_irqsave(&consolelock, + flags); + + /* + * If some other TTY is already connected here, reject the open + */ + if ((port_info[port].tty) && + (port_info[port].tty != tty)) + { + spin_unlock_irqrestore(&consolelock, + flags); + MOD_DEC_USE_COUNT; + printk("viocons: attempt to open device twice from different ttys\n"); + return -EBUSY; + } + tty->driver_data = &port_info[port]; + port_info[port].tty = tty; + spin_unlock_irqrestore(&consolelock, + flags); + + return 0; +} + +/*************************************************************************** + * TTY Close method + ***************************************************************************/ +static void viotty_close(struct tty_struct *tty, struct file * filp) +{ + unsigned long flags; + struct port_info_tag * pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_close")) + return; + + spin_lock_irqsave(&consolelock, + flags); + if (tty->count == 1) + { + pi->tty = NULL; + } + + spin_unlock_irqrestore(&consolelock, + flags); + + MOD_DEC_USE_COUNT; +} + +/*************************************************************************** + * TTY Write method + ***************************************************************************/ +static int viotty_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + return internal_write(tty, buf, count, from_user); +} + +/*************************************************************************** + * TTY put_char method + ***************************************************************************/ +static void viotty_put_char(struct tty_struct *tty, unsigned char ch) +{ + internal_write(tty, &ch, 1, 0); +} + +/*************************************************************************** + * TTY flush_chars method + ***************************************************************************/ +static void viotty_flush_chars(struct tty_struct *tty) +{ +} + +/*************************************************************************** + * TTY write_room method + ***************************************************************************/ +static int viotty_write_room(struct tty_struct *tty) +{ + int i; + int room = 0; + struct port_info_tag * pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_sendbuffers")) + return 0; + + // If no buffers are used, return the max size + if (overflow[pi->port].bufferUsed == 0) + return VIOCHAR_MAX_DATA * NUM_BUF; + + for (i=0; ((iport].bufferBytes[i]); + } + + if (room > VIOCHAR_MAX_DATA) + return VIOCHAR_MAX_DATA; + else + return room; +} + +/*************************************************************************** + * TTY chars_in_buffer_room method + ***************************************************************************/ +static int viotty_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +static void viotty_flush_buffer(struct tty_struct *tty) +{ +} + +static int viotty_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) + { + /* the ioctls below read/set the flags usually shown in the leds */ + /* don't use them - they will go away without warning */ + case KDGETLED: + case KDGKBLED: + return put_user(0, (char*)arg); + + case KDSKBLED: + return 0; + } + + return n_tty_ioctl(tty,file,cmd,arg); +} + +static void viotty_throttle(struct tty_struct * tty) +{ +} + +static void viotty_unthrottle(struct tty_struct * tty) +{ +} + +static void viotty_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ +} + +static void viotty_stop(struct tty_struct *tty) +{ +} + +static void viotty_start(struct tty_struct *tty) +{ +} + +static void viotty_hangup(struct tty_struct *tty) +{ +} + +static void viotty_break(struct tty_struct *tty, int break_state) +{ +} + +static void viotty_send_xchar(struct tty_struct *tty, char ch) +{ +} + +static void viotty_wait_until_sent(struct tty_struct *tty, int timeout) +{ +} + +/*************************************************************************** + * Handle an open charLpEvent. Could be either interrupt or ack + ***************************************************************************/ +static void vioHandleOpenEvent(struct HvLpEvent *event) +{ + unsigned long flags; + u8 eventRc; + u16 eventSubtypeRc; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtualDevice; + + if (event->xFlags.xFunction == HvLpEvent_Function_Ack) + { + if (port >= VTTY_PORTS) + return; + + spin_lock_irqsave(&consolelock, + flags); + /* Got the lock, don't cause console output */ + + if (event->xRc == HvLpEvent_Rc_Good) + { + sndMsgSeq[port] = sndMsgAck[port] = 0; + } + + port_info[port].lp = event->xTargetLp; + + spin_unlock_irqrestore(&consolelock, + flags); + + if(event->xCorrelationToken != 0) + { + unsigned long semptr = event->xCorrelationToken; + up((struct semaphore *)semptr); + } + else + printk("viocons: wierd...got open ack without semaphore\n"); + } + else + { + /* This had better require an ack, otherwise complain + */ + if (event->xFlags.xAckInd != HvLpEvent_AckInd_DoAck) + { + printk("viocons: viocharopen without ack bit!\n"); + return; + } + + spin_lock_irqsave(&consolelock, + flags); + /* Got the lock, don't cause console output */ + + /* Make sure this is a good virtual tty */ + if (port >= VTTY_PORTS) + { + eventRc = HvLpEvent_Rc_SubtypeError; + eventSubtypeRc = viorc_openRejected; + } + + /* If this is tty is already connected to a different + partition, fail */ + else if ((port_info[port].lp != HvLpIndexInvalid) && + (port_info[port].lp != event->xSourceLp)) + { + eventRc = HvLpEvent_Rc_SubtypeError; + eventSubtypeRc = viorc_openRejected; + } + else + { + port_info[port].lp = event->xSourceLp; + eventRc = HvLpEvent_Rc_Good; + eventSubtypeRc = viorc_good; + sndMsgSeq[port] = sndMsgAck[port] = 0; + } + + spin_unlock_irqrestore(&consolelock, + flags); + + /* Return the acknowledgement */ + HvCallEvent_ackLpEvent(event); + } +} + +/*************************************************************************** + * Handle a close open charLpEvent. Could be either interrupt or ack + ***************************************************************************/ +static void vioHandleCloseEvent(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtualDevice; + + if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + if (port >= VTTY_PORTS) + return; + + /* For closes, just mark the console partition invalid */ + spin_lock_irqsave(&consolelock, + flags); + /* Got the lock, don't cause console output */ + + if (port_info[port].lp == event->xSourceLp) + port_info[port].lp = HvLpIndexInvalid; + + spin_unlock_irqrestore(&consolelock, + flags); + printk("viocons: console close from %d\n", event->xSourceLp); + } + else + { + printk("viocons: got unexpected close ack\n"); + } +} + +/*************************************************************************** + * Handle a config charLpEvent. Could be either interrupt or ack + ***************************************************************************/ +static void vioHandleConfig( struct HvLpEvent *event ) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + int len; + + len = cevent->immediateDataLen; + HvCall_writeLogBuffer(cevent->immediateData, cevent->immediateDataLen); + + if ( cevent->immediateData[0] == 0x01 ) + { + printk("viocons: window resized to %d: %d: %d: %d\n", + cevent->immediateData[1], + cevent->immediateData[2], + cevent->immediateData[3], + cevent->immediateData[4]); + } + else + { + printk("viocons: unknown config event\n"); + } + return; +} + +/*************************************************************************** + * Handle a data charLpEvent. + ***************************************************************************/ +static void vioHandleData(struct HvLpEvent *event) +{ + struct tty_struct *tty; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + struct port_info_tag * pi; + int len; + u8 port = cevent->virtualDevice; + + if (port >= VTTY_PORTS) + { + printk("viocons: data on invalid virtual device %d\n",port); + return; + } + + tty = port_info[port].tty; + + if (tty == NULL) + { + printk("viocons: no tty for virtual device %d\n",port); + return; + } + + if (tty->magic != TTY_MAGIC) + { + printk("viocons: tty bad magic\n"); + return; + } + + /* + * Just to be paranoid, make sure the tty points back to this port + */ + pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "vioHandleData")) + return; + + len = cevent->immediateDataLen; + + if (len == 0) + return; + + /* + * Log port 0 data to the hypervisor log + */ + if (port == 0) + HvCall_writeLogBuffer(cevent->immediateData, cevent->immediateDataLen); + + /* Don't copy more bytes than there is room for in the buffer */ + if (tty->flip.count + len > TTY_FLIPBUF_SIZE) + { + len = TTY_FLIPBUF_SIZE - tty->flip.count; + printk("viocons: flip buffer overflow!\n"); + } + + memcpy (tty->flip.char_buf_ptr, + cevent->immediateData, + len); + memset(tty->flip.flag_buf_ptr, TTY_NORMAL, len); + + /* Update the kernel buffer end */ + tty->flip.count += len; + tty->flip.char_buf_ptr += len; + + tty->flip.flag_buf_ptr += len; + + tty_flip_buffer_push (tty); +} + +/*************************************************************************** + * Handle an ack charLpEvent. + ***************************************************************************/ +static void vioHandleAck(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + unsigned long flags; + u8 port = cevent->virtualDevice; + + if (port >= VTTY_PORTS) + { + printk("viocons: data on invalid virtual device\n"); + return; + } + + spin_lock_irqsave(&consolelock, + flags); + sndMsgAck[port] = event->xCorrelationToken; + spin_unlock_irqrestore(&consolelock, + flags); + + if (overflow[port].bufferUsed) + sendBuffers(port, port_info[port].lp); +} + +/*************************************************************************** + * Handle charLpEvents and route to the appropriate routine + ***************************************************************************/ +static void vioHandleCharEvent(struct HvLpEvent *event) +{ + int charminor; + + if (event == NULL) + { + return; + } + charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (charminor) + { + case viocharopen: + vioHandleOpenEvent(event); + break; + case viocharclose: + vioHandleCloseEvent(event); + break; + case viochardata: + vioHandleData(event); + break; + case viocharack: + vioHandleAck(event); + break; + case viocharconfig: + vioHandleConfig(event); + break; + default: + } +} + +/*************************************************************************** + * Send an open event + ***************************************************************************/ +static int viocons_sendOpen(HvLpIndex remoteLp, u8 port, void *sem) +{ + return HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_chario | viocharopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(remoteLp), + viopath_targetinst(remoteLp), + (u64)(unsigned long)sem, + VIOVERSION << 16, + ((u64)port << 48), + 0, 0, 0); + +} + +int __init viocons_init2(void) +{ +// extern char saved_command_line[]; + DECLARE_MUTEX_LOCKED(Semaphore); + int rc; + + /* + * Now open to the primary LP + */ + printk("viocons: init2 - open path to primary\n"); + rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), viomajorsubtype_chario); + if (rc) + { + printk("viocons: error on viopath_open to primary %d\n",rc); + } + + /* + * And if the primary is not the same as the hosting LP, open to the + * hosting lp + */ + if ((viopath_hostLp != HvLpIndexInvalid) && + (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) + { + printk("viocons: init2 - open path to hosting (%d) (%d)\n", + viopath_hostLp, HvLpConfig_getPrimaryLpIndex()); + rc = viopath_open(viopath_hostLp, viomajorsubtype_chario); + if (rc) + { + printk("viocons: error on viopath_open to hostlp %d\n",rc); + } + } + + vio_setCharHandler(vioHandleCharEvent); + + printk("viocons major is %d\n",TTY_MAJOR); + + /* First, try to open the console to the hosting lp. + * Wait on a semaphore for the response. + */ + if ((viopath_isactive(viopath_hostLp)) && + (viocons_sendOpen(viopath_hostLp, 0, &Semaphore) == 0)) + { + printk("viocons: init2 - open cons to hosting\n"); + down(&Semaphore); + } + + /* + * If we don't have an active console, try the primary + */ + if ((!viopath_isactive(port_info[0].lp)) && + (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && + (viocons_sendOpen(HvLpConfig_getPrimaryLpIndex(), 0, &Semaphore) == 0)) + { + printk("viocons: init2 - open cons to primary\n"); + down(&Semaphore); + } + + printk("viocons: init2 - initializing tty\n"); + + /* Initialize the tty_driver structure */ + memset(&viotty_driver, 0, sizeof(struct tty_driver)); + viotty_driver.magic = TTY_DRIVER_MAGIC; + viotty_driver.driver_name = "vioconsole"; +#if defined(CONFIG_DEVFS_FS) + viotty_driver.name = "tty%d"; +#else + viotty_driver.name = "tty"; +#endif + viotty_driver.major = TTY_MAJOR; + viotty_driver.minor_start = 1; + viotty_driver.name_base = 1; + viotty_driver.num = VTTY_PORTS; + viotty_driver.type = TTY_DRIVER_TYPE_CONSOLE; + viotty_driver.subtype = 1; + viotty_driver.init_termios = tty_std_termios; + viotty_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; + viotty_driver.refcount = &viotty_refcount; + viotty_driver.table = viotty_table; + viotty_driver.termios = viotty_termios; + viotty_driver.termios_locked = viotty_termios_locked; + + viotty_driver.open = viotty_open; + viotty_driver.close = viotty_close; + viotty_driver.write = viotty_write; + viotty_driver.put_char = viotty_put_char; + viotty_driver.flush_chars = viotty_flush_chars; + viotty_driver.write_room = viotty_write_room; + viotty_driver.chars_in_buffer = viotty_chars_in_buffer; + viotty_driver.flush_buffer = viotty_flush_buffer; + viotty_driver.ioctl = viotty_ioctl; + viotty_driver.throttle = viotty_throttle; + viotty_driver.unthrottle = viotty_unthrottle; + viotty_driver.set_termios = viotty_set_termios; + viotty_driver.stop = viotty_stop; + viotty_driver.start = viotty_start; + viotty_driver.hangup = viotty_hangup; + viotty_driver.break_ctl = viotty_break; + viotty_driver.send_xchar = viotty_send_xchar; + viotty_driver.wait_until_sent = viotty_wait_until_sent; + + viottyS_driver = viotty_driver; +#if defined(CONFIG_DEVFS_FS) + viottyS_driver.name = "ttyS%d"; +#else + viottyS_driver.name = "ttyS"; +#endif + viottyS_driver.major = TTY_MAJOR; + viottyS_driver.minor_start = VIOTTY_SERIAL_START; + viottyS_driver.type = TTY_DRIVER_TYPE_SERIAL; + viottyS_driver.table = viottyS_table; + viottyS_driver.termios = viottyS_termios; + viottyS_driver.termios_locked = viottyS_termios_locked; + + if (tty_register_driver(&viotty_driver)) + { + printk("Couldn't register viotty driver\n"); + } + + if (tty_register_driver(&viottyS_driver)) + { + printk("Couldn't register viottyS driver\n"); + } + + // Now create the vcs and vcsa devfs entries so mingetty works +#if defined(CONFIG_DEVFS_FS) + { + struct tty_driver temp_driver = viotty_driver; + int i; + + temp_driver.name = "vcs%d"; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs (&temp_driver, + 0, + i + temp_driver.minor_start); + + temp_driver.name = "vcsa%d"; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs (&temp_driver, + 0, + i + temp_driver.minor_start); + + // For compatibility with some earlier code only! + // This will go away!!! + temp_driver.name = "viocons/%d"; + temp_driver.name_base = 0; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs (&temp_driver, + 0, + i + temp_driver.minor_start); + } +#endif + + /* + * Create the proc entry + */ + iSeries_proc_callback(&viocons_proc_init); + + return 0; +} + +void __init viocons_init(void) +{ + int i; + printk("viocons registering console\n"); + + memset(&port_info, 0x00, sizeof(port_info)); + for (i=0; i + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *************************************************************************** + * This routine provides access to tape drives owned and managed by an OS/400 + * partition running on the same box as this Linux partition. + * + * All tape operations are performed by sending messages back and forth to + * the OS/400 partition. The format of the messages is defined in + * include/asm-ppc/iSeries/vio.h + * + */ + +/* +Changelog (absent entries indicate repeats): + Ver Date Who What + ==== =========== =================== ============================== + 2001 Jul 03 devilbis@us.ibm.com fix nvt vs vt bug + add automatic WEOF + 1.0 2001 Jul 05 add version number + set version to 1.0 + 2001 Jul 11 moved viotape_unitinfo to kernel mem + successfully loaded as a module + unregistered device in viotap_exit + 2002 Jul 12 unregistered proc entry on exit + unregistered individual tapes on exit + 1.1 2002 Jul 13 added viopath_close on exit + added MOD_(INC|DEC)_USE_COUNT + set version to 1.1 +*/ + +#undef VIOT_DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif + +#ifndef _HVLPEVENT_H +#include +#endif + +#ifndef _HVCALLEVENT_H +#include "asm/iSeries/HvCallEvent.h" +#endif +#ifndef _HVLPCONFIG_H +#include "asm/iSeries/HvLpConfig.h" +#endif +#ifndef _ISERIES_PROC_H +#include +#endif + +static int viotape_major = 230; +static int viotape_numdev = 0; + +/* version number for viotape driver */ +static unsigned int version_major = 1; +static unsigned int version_minor = 0; + +static u64 sndMsgSeq; +static u64 sndMsgAck; +static u64 rcvMsgSeq; +static u64 rcvMsgAck; + +/*************************************************************************** + * The minor number follows the conventions of the SCSI tape drives. The + * rewind and mode are encoded in the minor #. We use this struct to break + * them out + ***************************************************************************/ +struct viot_devinfo_struct { + int major; + int minor; + int devno; + int mode; + int rewind; +}; + +/*************************************************************************** + * Maximum # tapes we support + ***************************************************************************/ +#define VIOTAPE_MAX_TAPE 8 +#define MAX_PARTITIONS 4 + +/* defines for current tape state */ +#define VIOT_IDLE 0 +#define VIOT_READING 1 +#define VIOT_WRITING 2 + +/*************************************************************************** + * Our info on the tapes + ***************************************************************************/ +struct tape_descr { + char rsrcname[10]; + char type[4]; + char model[3]; +}; + +static struct tape_descr *viotape_unitinfo = NULL; + +static char *lasterr[VIOTAPE_MAX_TAPE]; + +static struct mtget viomtget[VIOTAPE_MAX_TAPE]; + +/* maintain the current state of each tape (and partition) + so that we know when to write EOF marks. +*/ +static struct { + unsigned char cur_part; + devfs_handle_t dev_handle; + struct { + unsigned char rwi; + } part_stat[MAX_PARTITIONS]; +} state[VIOTAPE_MAX_TAPE]; + +/*************************************************************************** + * We single-thread + ***************************************************************************/ +static struct semaphore reqSem; + +/*************************************************************************** + * When we send a request, we use this struct to get the response back + * from the interrupt handler + ***************************************************************************/ +struct opStruct { + void *buffer; + dma_addr_t dmaaddr; + size_t count; + int rc; + struct semaphore *sem; + struct opStruct *free; +}; + +static spinlock_t opStructListLock; +static struct opStruct *opStructList; + +/*************************************************************************** + *************************************************************************** + * CODE STARTS HERE + *************************************************************************** + ***************************************************************************/ + +/* forward declaration to resolve interdependence */ +static int chg_state(int index, unsigned char new_state, struct file *file); + +/*************************************************************************** + * Decode the kdev_t into its parts + ***************************************************************************/ +void getDevInfo(kdev_t dev, struct viot_devinfo_struct *devi) +{ + devi->major = MAJOR(dev); + devi->minor = MINOR(dev); + devi->devno = devi->minor & 0x1F; + devi->mode = (devi->minor & 0x60) >> 5; +/* if bit is set in the minor, do _not_ rewind automatically */ + devi->rewind = !(devi->minor & 0x80); +} + + +/*************************************************************************** + * Allocate an op structure from our pool + ***************************************************************************/ +static struct opStruct *getOpStruct(void) +{ + struct opStruct *newOpStruct; + spin_lock(&opStructListLock); + + if (opStructList == NULL) + { + newOpStruct = kmalloc(sizeof(struct opStruct), GFP_KERNEL); + } + else + { + newOpStruct = opStructList; + opStructList = opStructList->free; + } + + if (newOpStruct) + memset(newOpStruct,0x00,sizeof(struct opStruct)); + + spin_unlock(&opStructListLock); + + return newOpStruct; +} + +/*************************************************************************** + * Return an op structure to our pool + ***************************************************************************/ +static void freeOpStruct(struct opStruct *opStruct) +{ + spin_lock(&opStructListLock); + opStruct->free = opStructList; + opStructList = opStruct; + spin_unlock(&opStructListLock); +} + +/*************************************************************************** + * Map our tape return codes to errno values + ***************************************************************************/ +int tapeRcToErrno(int tapeRc, char *operation, int tapeno) +{ + int terrno; + char *tmsg; + + switch (tapeRc) + { + case 0: return 0; + case viotape_InvalidRange: terrno=EIO; tmsg = "Internal error"; break; + case viotape_InvalidToken: terrno=EIO; tmsg = "Internal error"; break; + case viotape_DMAError: terrno=EIO; tmsg = "DMA error"; break; + case viotape_UseError: terrno=EIO; tmsg = "Internal error"; break; + case viotape_ReleaseError: terrno=EIO; tmsg = "Internal error"; break; + case viotape_InvalidTape: terrno=EIO; tmsg = "Invalid tape device"; break; + case viotape_InvalidOp: terrno=EIO; tmsg = "Invalid operation"; break; + case viotape_TapeErr: terrno=EIO; tmsg = "Tape error"; break; + + case viotape_AllocTimedOut: terrno=EBUSY; tmsg = "Allocate timed out"; break; + case viotape_BOTEnc: terrno=EIO; tmsg = "Beginning of tape encountered"; break; + case viotape_BlankTape: terrno=EIO; tmsg = "Blank tape"; break; + case viotape_BufferEmpty: terrno=EIO; tmsg = "Buffer empty"; break; + case viotape_CleanCartFound: terrno=ENOMEDIUM; tmsg = "Cleaning cartridge found"; break; + case viotape_CmdNotAllowed: terrno=EIO; tmsg = "Command not allowed"; break; + case viotape_CmdNotSupported: terrno=EIO; tmsg = "Command not supported"; break; + case viotape_DataCheck: terrno=EIO; tmsg = "Data check"; break; + case viotape_DecompressErr: terrno=EIO; tmsg = "Decompression error"; break; + case viotape_DeviceTimeout: terrno=EBUSY; tmsg = "Device timeout"; break; + case viotape_DeviceUnavail: terrno=EIO; tmsg = "Device unavailable"; break; + case viotape_DeviceBusy: terrno=EBUSY; tmsg = "Device busy"; break; + case viotape_EndOfMedia: terrno=ENOSPC; tmsg = "End of media"; break; + case viotape_EndOfTape: terrno=ENOSPC; tmsg = "End of tape"; break; + case viotape_EquipCheck: terrno=EIO; tmsg = "Equipment check"; break; + case viotape_InsufficientRs: terrno=EOVERFLOW; tmsg = "Insufficient tape resources"; break; + case viotape_InvalidLogBlk: terrno=EIO; tmsg = "Invalid logical block location"; break; + case viotape_LengthError: terrno=EOVERFLOW; tmsg = "Length error"; break; + case viotape_LibDoorOpen: terrno=EBUSY; tmsg = "Door open"; break; + case viotape_LoadFailure: terrno=ENOMEDIUM; tmsg = "Load failure"; break; + case viotape_NotCapable: terrno=EIO; tmsg = "Not capable"; break; + case viotape_NotOperational: terrno=EIO; tmsg = "Not operational"; break; + case viotape_NotReady: terrno=EIO; tmsg = "Not ready"; break; + case viotape_OpCancelled: terrno=EIO; tmsg = "Operation cancelled"; break; + case viotape_PhyLinkErr: terrno=EIO; tmsg = "Physical link error"; break; + case viotape_RdyNotBOT: terrno=EIO; tmsg = "Ready but not beginning of tape"; break; + case viotape_TapeMark: terrno=EIO; tmsg = "Tape mark"; break; + case viotape_WriteProt: terrno=EROFS; tmsg = "Write protection error"; break; + default: + terrno = EIO; tmsg = "I/O error"; + } + + printk("viotape error on Device %d (%10.10s): %s\n", + tapeno, + viotape_unitinfo[tapeno].rsrcname, + tmsg); + + lasterr[tapeno] = tmsg; + + return -terrno; +} + +/*************************************************************************** + * Handle reads from the proc file system. + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + int i; + + len += sprintf(buf+len, "viotape driver version %d.%d\n", + version_major, version_minor); + + for (i=0; inlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viotape_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viotape", iSeries_proc); +} + + +/*************************************************************************** + * Get info on all tapes from OS/400 + ***************************************************************************/ +static void get_viotape_info(void) +{ + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + int i; + struct opStruct *op = getOpStruct(); + DECLARE_MUTEX_LOCKED(Semaphore); + if (op == NULL) + return; + + if (viotape_unitinfo == NULL) { +#ifdef VIOT_DEBUG + printk("viotape: trying to allocate pci buffer\n"); +#endif + viotape_unitinfo = kmalloc(sizeof(struct tape_descr) * VIOTAPE_MAX_TAPE, GFP_KERNEL); + } +#ifdef VIOT_DEBUG + printk("viotape: trying to clear pci buffer\n"); +#endif + memset(viotape_unitinfo, 0x00, sizeof(struct tape_descr) * VIOTAPE_MAX_TAPE); + memset(lasterr, 0x00, sizeof(lasterr)); + + op->sem = &Semaphore; + + dmaaddr = pci_map_single(NULL, viotape_unitinfo, + sizeof(struct tape_descr) * VIOTAPE_MAX_TAPE, + PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) + { + printk("viotape error allocating tce\n"); + return; + } + +#ifdef VIOT_DEBUG + printk("viotape: trying to request information\n"); +#endif + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapegetinfo, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + dmaaddr, + sizeof(struct tape_descr) * VIOTAPE_MAX_TAPE, + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + } + + down(&Semaphore); + + freeOpStruct(op); + +#ifdef VIOT_DEBUG + printk("viotape: checking pci buffer for information\n"); +#endif + + for (i=0; + ((i < VIOTAPE_MAX_TAPE) && (viotape_unitinfo[i].rsrcname[0])); + i++) + { + printk("found a tape %10.10s\n",viotape_unitinfo[i].rsrcname); + viotape_numdev++; + } +} + +/*************************************************************************** + * Don't support seek + ***************************************************************************/ +static long long viotap_llseek(struct file *file, long long offset, int origin) +{ + printk("viotape_llseek: not supported\n"); + return -EINVAL; /* not supported */ +} + +/*************************************************************************** + * Write + ***************************************************************************/ +static ssize_t viotap_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + HvLpEvent_Rc hvrc; + kdev_t dev = file->f_dentry->d_inode->i_rdev; + unsigned short flags = file->f_flags; + struct opStruct *op = getOpStruct(); + int noblock = ((flags & O_NONBLOCK) != 0); + int err; + struct viot_devinfo_struct devi; + DECLARE_MUTEX_LOCKED(Semaphore); + +#ifdef VIOT_DEBUG + printk("viotape: tried to call write\n"); +#endif + + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + /* We need to make sure we can send a request. We use + * a semaphore to keep track of # requests in use. If + * we are non-blocking, make sure we don't block on the + * semaphore + */ + if (noblock) + { + if (down_trylock(&reqSem)) + { + freeOpStruct(op); + return -EWOULDBLOCK; + } + } + else + { + down(&reqSem); + } + + /* Allocate a DMA buffer */ + op->buffer = pci_alloc_consistent(NULL, + count, + &op->dmaaddr); + + if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) + { + printk("viotape error allocating dma buffer for len %d\n", + count); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + op->count = count; + + /* Copy the data into the buffer */ + err = copy_from_user( op->buffer, (const void *) buf, count); + if (err) + { + printk("viotape: error on copy from user\n"); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + if (noblock) + { + op->sem = NULL; + } + else + { + op->sem = &Semaphore; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapewrite, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48) | + op->dmaaddr, + count, + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + + if (noblock) + return count; + + down(&Semaphore); + + err = op->rc; + + /* Free the buffer */ + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + + count = op->count; + + freeOpStruct(op); + up(&reqSem); + if (err) + return tapeRcToErrno(err, "write", devi.devno); + else + { + chg_state(devi.devno, VIOT_WRITING, file); + return count; + } +} + +/*************************************************************************** + * read + ***************************************************************************/ +static ssize_t viotap_read(struct file *file, char *buf, size_t count, loff_t *ptr) +{ + HvLpEvent_Rc hvrc; + kdev_t dev = file->f_dentry->d_inode->i_rdev; + unsigned short flags = file->f_flags; + struct opStruct *op = getOpStruct(); + int noblock = ((flags & O_NONBLOCK) != 0); + int err; + struct viot_devinfo_struct devi; + DECLARE_MUTEX_LOCKED(Semaphore); + +#ifdef VIOT_DEBUG + printk("viotape: tried to call read\n"); +#endif + + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + /* We need to make sure we can send a request. We use + * a semaphore to keep track of # requests in use. If + * we are non-blocking, make sure we don't block on the + * semaphore + */ + if (noblock) + { + if (down_trylock(&reqSem)) + { + freeOpStruct(op); + return -EWOULDBLOCK; + } + } + else + { + down(&reqSem); + } + + chg_state(devi.devno, VIOT_READING, file); + + /* Allocate a DMA buffer */ + op->buffer = pci_alloc_consistent(NULL, + count, + &op->dmaaddr); + + if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) + { + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + op->count = count; + + op->sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotaperead, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48) | + op->dmaaddr, + count, + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + + down(&Semaphore); + + if (op->rc == 0) + { + /* If we got data back */ + if (op->count) + { + /* Copy the data into the buffer */ + err = copy_to_user( buf, op->buffer, count); + if (err) + { + printk("error on copy_to_user\n"); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + } + } + + err = op->rc; + + /* Free the buffer */ + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + + count = op->count; + + freeOpStruct(op); + up(&reqSem); + if (err) + return tapeRcToErrno(err, "read", devi.devno); + else + return count; +} + +/*************************************************************************** + * read + ***************************************************************************/ +static int viotap_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + HvLpEvent_Rc hvrc; + int err; + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + struct opStruct *op = getOpStruct(); + struct viot_devinfo_struct devi; + if (op == NULL) + return -ENOMEM; + +#ifdef VIOT_DEBUG + printk("viotape: tried to call ioctl\n"); +#endif + + getDevInfo(dev, &devi); + + down(&reqSem); + + switch (cmd) + { + case MTIOCTOP: + { + struct mtop mtc; + u32 myOp; + + /* inode is null if and only if we (the kernel) made the request */ + if(inode == NULL) + memcpy(&mtc, (void *)arg, sizeof(struct mtop)); + else if (copy_from_user((char *) &mtc, (char *) arg, sizeof(struct mtop))) + { + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + switch(mtc.mt_op) + { + case MTRESET: myOp = VIOTAPOP_RESET; break; + case MTFSF: myOp = VIOTAPOP_FSF; break; + case MTBSF: myOp = VIOTAPOP_BSF; break; + case MTFSR: myOp = VIOTAPOP_FSR; break; + case MTBSR: myOp = VIOTAPOP_BSR; break; + case MTWEOF: myOp = VIOTAPOP_WEOF; break; + case MTREW: myOp = VIOTAPOP_REW; break; + case MTNOP: myOp = VIOTAPOP_NOP; break; + case MTEOM: myOp = VIOTAPOP_EOM; break; + case MTERASE: myOp = VIOTAPOP_ERASE; break; + case MTSETBLK: myOp = VIOTAPOP_SETBLK; break; + case MTSETDENSITY: myOp = VIOTAPOP_SETDENSITY; break; + case MTTELL: myOp = VIOTAPOP_GETPOS; break; + case MTSEEK: myOp = VIOTAPOP_SETPOS; break; + case MTSETPART: myOp = VIOTAPOP_SETPART; break; + default: + return -EIO; + } + +/* if we moved the head, we are no longer reading or writing */ + switch(mtc.mt_op) + { + case MTFSF: + case MTBSF: + case MTFSR: + case MTBSR: + case MTTELL: + case MTSEEK: + case MTREW: + chg_state(devi.devno, VIOT_IDLE, file); + } + + op->sem = &Semaphore; + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeop, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, + (((u64)myOp) << 32) | mtc.mt_count, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + down(&Semaphore); + + if (op->rc) + { + freeOpStruct(op); + up(&reqSem); + return tapeRcToErrno(op->rc, "tape operation", devi.devno); + } + else + { + freeOpStruct(op); + up(&reqSem); + return 0; + } + break; + } + + case MTIOCGET: + op->sem = &Semaphore; + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapegetstatus , + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, 0, 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + down(&Semaphore); + up(&reqSem); + if (op->rc) + { + freeOpStruct(op); + return tapeRcToErrno(op->rc, "get status", devi.devno); + } + else + { + freeOpStruct(op); + err = copy_to_user((void *)arg, &viomtget[dev], sizeof(viomtget[0])); + if (err) + { + freeOpStruct(op); + return -EFAULT; + } + return 0; + } + break; + case MTIOCPOS: + printk("Got an MTIOCPOS\n"); + default: + return -ENOSYS; + } + return 0; +} + +/*************************************************************************** + * Open + ***************************************************************************/ +static int viotap_open(struct inode *inode, struct file *file) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + HvLpEvent_Rc hvrc; + struct opStruct *op = getOpStruct(); + struct viot_devinfo_struct devi; + if (op == NULL) + return -ENOMEM; + +#ifdef VIOT_DEBUG + printk("viotape: tried to call open\n"); +#endif + + getDevInfo(dev, &devi); + + // Note: We currently only support one mode! + if ((devi.devno >= viotape_numdev) || + (devi.mode)) + { +#ifdef VIOT_DEBUG + printk("viotape: invalid devno %d or mode %d\n", devi.devno, devi.mode); +#endif + freeOpStruct(op); + return -ENODEV; + } + + op->sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + printk("viotape bad rc on signalLpEvent %d\n",(int)hvrc); + freeOpStruct(op); + return -EIO; + } + + down(&Semaphore); + + if (op->rc) + { +#ifdef VIOT_DEBUG + printk("viotape: bad return code in viotapopen: %d", op->rc); +#endif + freeOpStruct(op); + return tapeRcToErrno(op->rc, "open", devi.devno); + } + else + { + freeOpStruct(op); + MOD_INC_USE_COUNT; + return 0; + } +} + + +/*************************************************************************** + * Release + ***************************************************************************/ +static int viotap_release(struct inode *inode, struct file *file) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + HvLpEvent_Rc hvrc; + struct viot_devinfo_struct devi; + struct opStruct *op = getOpStruct(); + +#ifdef VIOT_DEBUG + printk("viotape: tried to call release\n"); +#endif + + if (op == NULL) + return -ENOMEM; + op->sem = &Semaphore; + + getDevInfo(dev, &devi); + + if (devi.devno >= viotape_numdev) + { + freeOpStruct(op); + return -ENODEV; + } + + chg_state(devi.devno, VIOT_IDLE, file); + + if (devi.rewind) + { + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeop, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, + ((u64)VIOTAPOP_REW) << 32, + 0); + down(&Semaphore); + + if (op->rc) + { + tapeRcToErrno(op->rc, "rewind", devi.devno); + } + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeclose, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + printk("viotape: bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + down(&Semaphore); + + if (op->rc) + { + printk("viotape: close failed\n"); + } + MOD_DEC_USE_COUNT; + return 0; +} + +struct file_operations viotap_fops = { + owner: THIS_MODULE, + llseek: viotap_llseek, + read: viotap_read, + write: viotap_write, + ioctl: viotap_ioctl, + open: viotap_open, + release: viotap_release, +}; + +/*************************************************************************** + * Handle interrupt events for tape + ***************************************************************************/ +static void vioHandleTapeEvent(struct HvLpEvent *event) +{ + int tapeminor; + struct opStruct *op; + struct viotapelpevent *tevent = (struct viotapelpevent *)event; + + if (event == NULL) + { + /* Notification that a partition went away! */ + if (!viopath_isactive(viopath_hostLp)) + { + /* TODO! Clean up */ + } + return; + } + + tapeminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (tapeminor) + { + case viotapegetinfo: + case viotapeopen: + case viotapeclose: + op = (struct opStruct *)(u32)event->xCorrelationToken; + op->rc = tevent->mSubTypeRc; + up(op->sem); + break; + case viotaperead: + case viotapewrite: + op = (struct opStruct *)(u32)event->xCorrelationToken; + op->rc = tevent->mSubTypeRc;; + op->count = tevent->mLen; + + if (op->sem) + { + up(op->sem); + } + else + { + freeOpStruct(op); + up(&reqSem); + } + break; + case viotapeop: + case viotapegetpos: + case viotapesetpos: + case viotapegetstatus: + op = (struct opStruct *)(u32)event->xCorrelationToken; + if (op) + { + op->count = tevent->u.tapeOp.mCount; + op->rc = tevent->mSubTypeRc;; + + if (op->sem) + { + up(op->sem); + } + } + break; + default: + printk("viotape: wierd ack\n"); + } +} + + +/*************************************************************************** + * Do initialization + ***************************************************************************/ +int __init viotap_init(void) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + int rc; + char tapename[32]; + int i; + + printk("viotape driver version %d.%d\n", version_major, version_minor); + + sndMsgSeq = sndMsgAck = 0; + rcvMsgSeq = rcvMsgAck = 0; + opStructList = NULL; + spin_lock_init(&opStructListLock); + + sema_init(&reqSem, VIOTAPE_MAXREQ); + + /* + * Open to our hosting lp + */ + if (viopath_hostLp == HvLpIndexInvalid) + return -1; + + printk("viotape: init - open path to hosting (%d)\n", viopath_hostLp); + + rc = viopath_open(viopath_hostLp, viomajorsubtype_tape); + if (rc) + { + printk("viotape: error on viopath_open to hostlp %d\n",rc); + } + + vio_setTapeHandler(vioHandleTapeEvent); + + printk("viotape major is %d\n",viotape_major); + + get_viotape_info(); + + if (devfs_register_chrdev(viotape_major, "viotape", &viotap_fops)) + { + printk("Error registering viotape device\n"); + return -1; + } + + for (i=0; i= 0x20400 -static struct pci_device_id acenic_pci_tbl[] __initdata = { - { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - /* - * Farallon used the DEC vendor ID on their cards incorrectly. - */ - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { } -}; -MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); -#endif - #ifndef wmb #define wmb() mb() @@ -521,230 +497,188 @@ "acenic.c: v0.81 04/20/2001 Jes Sorensen, linux-acenic@SunSITE.dk\n" " http://home.cern.ch/~jes/gige/acenic.html\n"; -static struct net_device *root_dev; - -static int probed __initdata = 0; - - -int __devinit acenic_probe (ACE_PROBE_ARG) +static int __devinit acenic_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) { -#ifdef NEW_NETINIT struct net_device *dev; -#endif - struct ace_private *ap; - struct pci_dev *pdev = NULL; int boards_found = 0; int version_disp; - if (probed) - return -ENODEV; - probed++; - if (!pci_present()) /* is PCI support present? */ return -ENODEV; version_disp = 0; - while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET<<8, pdev))) { + dev = init_etherdev(NULL, sizeof(struct ace_private)); - if (!((pdev->vendor == PCI_VENDOR_ID_ALTEON) && - ((pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE) || - (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)))&& - !((pdev->vendor == PCI_VENDOR_ID_3COM) && - (pdev->device == PCI_DEVICE_ID_3COM_3C985)) && - !((pdev->vendor == PCI_VENDOR_ID_NETGEAR) && - ((pdev->device == PCI_DEVICE_ID_NETGEAR_GA620) || - (pdev->device == PCI_DEVICE_ID_NETGEAR_GA620T))) && - /* - * Farallon used the DEC vendor ID on their cards by - * mistake for a while - */ - !((pdev->vendor == PCI_VENDOR_ID_DEC) && - (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX)) && - !((pdev->vendor == PCI_VENDOR_ID_SGI) && - (pdev->device == PCI_DEVICE_ID_SGI_ACENIC))) - continue; - - dev = init_etherdev(NULL, sizeof(struct ace_private)); - - if (dev == NULL) { - printk(KERN_ERR "acenic: Unable to allocate " - "net_device structure!\n"); - break; - } + if (dev == NULL) { + printk(KERN_ERR "acenic: Unable to allocate " + "net_device structure!\n"); + return -ENODEV; + } - SET_MODULE_OWNER(dev); + SET_MODULE_OWNER(dev); - if (!dev->priv) - dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL); - if (!dev->priv) { - printk(KERN_ERR "acenic: Unable to allocate memory\n"); - return -ENOMEM; - } - - ap = dev->priv; - ap->pdev = pdev; - - dev->irq = pdev->irq; - dev->open = &ace_open; - dev->hard_start_xmit = &ace_start_xmit; - dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; - if (1) { - static void ace_watchdog(struct net_device *dev); - dev->tx_timeout = &ace_watchdog; - dev->watchdog_timeo = 5*HZ; - } - dev->stop = &ace_close; - dev->get_stats = &ace_get_stats; - dev->set_multicast_list = &ace_set_multicast_list; - dev->do_ioctl = &ace_ioctl; - dev->set_mac_address = &ace_set_mac_addr; - dev->change_mtu = &ace_change_mtu; + if (!dev->priv) + dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL); + if (!dev->priv) { + printk(KERN_ERR "acenic: Unable to allocate memory\n"); + return -ENOMEM; + } - /* display version info if adapter is found */ - if (!version_disp) - { - /* set display flag to TRUE so that */ - /* we only display this string ONCE */ - version_disp = 1; - printk(version); - } + ap = dev->priv; + ap->pdev = pdev; - /* - * Enable master mode before we start playing with the - * pci_command word since pci_set_master() will modify - * it. - */ - pci_set_master(pdev); + dev->irq = pdev->irq; + dev->open = &ace_open; + dev->hard_start_xmit = &ace_start_xmit; + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; + if (1) { + static void ace_watchdog(struct net_device *dev); + dev->tx_timeout = &ace_watchdog; + dev->watchdog_timeo = 5*HZ; + } + dev->stop = &ace_close; + dev->get_stats = &ace_get_stats; + dev->set_multicast_list = &ace_set_multicast_list; + dev->do_ioctl = &ace_ioctl; + dev->set_mac_address = &ace_set_mac_addr; + dev->change_mtu = &ace_change_mtu; - pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); + /* display version info if adapter is found */ + if (!version_disp) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + version_disp = 1; + printk(version); + } - /* OpenFirmware on Mac's does not set this - DOH.. */ - if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { - printk(KERN_INFO "%s: Enabling PCI Memory Mapped " - "access - was not enabled by BIOS/Firmware\n", - dev->name); - ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; - pci_write_config_word(ap->pdev, PCI_COMMAND, - ap->pci_command); - wmb(); - } + /* + * Enable master mode before we start playing with the + * pci_command word since pci_set_master() will modify + * it. + */ + pci_set_master(pdev); - pci_read_config_byte(pdev, PCI_LATENCY_TIMER, - &ap->pci_latency); - if (ap->pci_latency <= 0x40) { - ap->pci_latency = 0x40; - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, - ap->pci_latency); - } + pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); - /* - * Remap the regs into kernel space - this is abuse of - * dev->base_addr since it was means for I/O port - * addresses but who gives a damn. - */ - dev->base_addr = pci_resource_start(pdev, 0); - ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); - if (!ap->regs) { - printk(KERN_ERR "%s: Unable to map I/O register, " - "AceNIC %i will be disabled.\n", - dev->name, boards_found); - break; - } + /* OpenFirmware on Mac's does not set this - DOH.. */ + if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { + printk(KERN_INFO "%s: Enabling PCI Memory Mapped " + "access - was not enabled by BIOS/Firmware\n", + dev->name); + ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; + pci_write_config_word(ap->pdev, PCI_COMMAND, + ap->pci_command); + wmb(); + } - switch(pdev->vendor) { - case PCI_VENDOR_ID_ALTEON: - strncpy(ap->name, "AceNIC Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: Alteon AceNIC ", dev->name); - break; - case PCI_VENDOR_ID_3COM: - strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); - break; - case PCI_VENDOR_ID_NETGEAR: - strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: NetGear GA620 ", dev->name); - break; - case PCI_VENDOR_ID_DEC: - if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { - strncpy(ap->name, "Farallon PN9000-SX " - "Gigabit Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Farallon PN9000-SX ", - dev->name); - break; - } - case PCI_VENDOR_ID_SGI: - strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: SGI AceNIC ", dev->name); - break; - default: - strncpy(ap->name, "Unknown AceNIC based Gigabit " - "Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, + &ap->pci_latency); + if (ap->pci_latency <= 0x40) { + ap->pci_latency = 0x40; + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, + ap->pci_latency); + } + + /* + * Remap the regs into kernel space - this is abuse of + * dev->base_addr since it was means for I/O port + * addresses but who gives a damn. + */ + dev->base_addr = pci_resource_start(pdev, 0); + ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); + if (!ap->regs) { + printk(KERN_ERR "%s: Unable to map I/O register, " + "AceNIC %i will be disabled.\n", + dev->name, boards_found); + return -ENODEV; + } + + switch(pdev->vendor) { + case PCI_VENDOR_ID_ALTEON: + strncpy(ap->name, "AceNIC Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: Alteon AceNIC ", dev->name); + break; + case PCI_VENDOR_ID_3COM: + strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); + break; + case PCI_VENDOR_ID_NETGEAR: + strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: NetGear GA620 ", dev->name); + break; + case PCI_VENDOR_ID_DEC: + if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { + strncpy(ap->name, "Farallon PN9000-SX " + "Gigabit Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Farallon PN9000-SX ", + dev->name); break; } - ap->name [sizeof (ap->name) - 1] = '\0'; - printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); + case PCI_VENDOR_ID_SGI: + strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: SGI AceNIC ", dev->name); + break; + default: + strncpy(ap->name, "Unknown AceNIC based Gigabit " + "Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + break; + } + ap->name [sizeof (ap->name) - 1] = '\0'; + printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); #ifdef __sparc__ - printk("irq %s\n", __irq_itoa(dev->irq)); + printk("irq %s\n", __irq_itoa(dev->irq)); #else - printk("irq %i\n", dev->irq); + printk("irq %i\n", dev->irq); #endif #ifdef CONFIG_ACENIC_OMIT_TIGON_I - if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { - printk(KERN_ERR "%s: Driver compiled without Tigon I" - " support - NIC disabled\n", dev->name); - ace_init_cleanup(dev); - kfree(dev); - continue; - } + if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { + printk(KERN_ERR "%s: Driver compiled without Tigon I" + " support - NIC disabled\n", dev->name); + ace_init_cleanup(dev); + kfree(dev); + return -ENODEV; + } #endif - if (ace_allocate_descriptors(dev)) { - /* - * ace_allocate_descriptors() calls - * ace_init_cleanup() on error. - */ - kfree(dev); - continue; - } + if (ace_allocate_descriptors(dev)) { + /* + * ace_allocate_descriptors() calls + * ace_init_cleanup() on error. + */ + kfree(dev); + return -ENODEV; + } #ifdef MODULE - if (boards_found >= ACE_MAX_MOD_PARMS) - ap->board_idx = BOARD_IDX_OVERFLOW; - else - ap->board_idx = boards_found; + if (boards_found >= ACE_MAX_MOD_PARMS) + ap->board_idx = BOARD_IDX_OVERFLOW; + else + ap->board_idx = boards_found; #else - ap->board_idx = BOARD_IDX_STATIC; + ap->board_idx = BOARD_IDX_STATIC; #endif - if (ace_init(dev)) { - /* - * ace_init() calls ace_init_cleanup() on error. - */ - kfree(dev); - continue; - } + pdev->driver_data = dev; - boards_found++; + if (ace_init(dev)) { + /* + * ace_init() calls ace_init_cleanup() on error. + */ + kfree(dev); + return -ENODEV; } - /* - * If we're at this point we're going through ace_probe() for - * the first time. Return success (0) if we've initialized 1 - * or more boards. Otherwise, return failure (-ENODEV). - */ - - if (boards_found > 0) - return 0; - else - return -ENODEV; + return 0; } @@ -759,136 +693,95 @@ MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i"); #endif - -static void __exit ace_module_cleanup(void) +static void __devexit acenic_remove_one(struct pci_dev *pdev) { + struct net_device *dev = pdev->driver_data; struct ace_private *ap; struct ace_regs *regs; - struct net_device *next; short i; - while (root_dev) { - ap = root_dev->priv; - next = ap->next; - - regs = ap->regs; + ap = dev->priv; + regs = ap->regs; - writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); - if (ap->version >= 2) - writel(readl(®s->CpuBCtrl) | CPU_HALT, - ®s->CpuBCtrl); - /* - * This clears any pending interrupts - */ - writel(1, ®s->Mb0Lo); + writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); + if (ap->version >= 2) + writel(readl(®s->CpuBCtrl) | CPU_HALT, + ®s->CpuBCtrl); + /* + * This clears any pending interrupts + */ + writel(1, ®s->Mb0Lo); - /* - * Make sure no other CPUs are processing interrupts - * on the card before the buffers are being released. - * Otherwise one might experience some `interesting' - * effects. - * - * Then release the RX buffers - jumbo buffers were - * already released in ace_close(). - */ - synchronize_irq(); + /* + * Make sure no other CPUs are processing interrupts + * on the card before the buffers are being released. + * Otherwise one might experience some `interesting' + * effects. + * + * Then release the RX buffers - jumbo buffers were + * already released in ace_close(). + */ + synchronize_irq(); - for (i = 0; i < RX_STD_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; + for (i = 0; i < RX_STD_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; - if (skb) { + if (skb) { #ifndef DUMMY_PCI_UNMAP - dma_addr_t mapping; + dma_addr_t mapping; - mapping = ap->skb->rx_std_skbuff[i].mapping; - pci_unmap_single(ap->pdev, mapping, - ACE_STD_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); + mapping = ap->skb->rx_std_skbuff[i].mapping; + pci_unmap_single(ap->pdev, mapping, + ACE_STD_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); #endif - ap->rx_std_ring[i].size = 0; - ap->skb->rx_std_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } + ap->rx_std_ring[i].size = 0; + ap->skb->rx_std_skbuff[i].skb = NULL; + dev_kfree_skb(skb); } - if (ap->version >= 2) { - for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; - - if (skb) { -#ifndef DUMMY_PCI_UNMAP - dma_addr_t mapping; + } + if (ap->version >= 2) { + for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; - mapping = ap->skb->rx_mini_skbuff[i].mapping; - pci_unmap_single(ap->pdev, mapping, - ACE_MINI_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); -#endif - ap->rx_mini_ring[i].size = 0; - ap->skb->rx_mini_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } - } - } - for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; if (skb) { #ifndef DUMMY_PCI_UNMAP dma_addr_t mapping; - mapping = ap->skb->rx_jumbo_skbuff[i].mapping; + mapping = ap->skb->rx_mini_skbuff[i].mapping; pci_unmap_single(ap->pdev, mapping, - ACE_JUMBO_BUFSIZE - (2 + 16), + ACE_MINI_BUFSIZE - (2 + 16), PCI_DMA_FROMDEVICE); #endif - - ap->rx_jumbo_ring[i].size = 0; - ap->skb->rx_jumbo_skbuff[i].skb = NULL; + ap->rx_mini_ring[i].size = 0; + ap->skb->rx_mini_skbuff[i].skb = NULL; dev_kfree_skb(skb); } } - - ace_init_cleanup(root_dev); - kfree(root_dev); - root_dev = next; } -} - - -int __init ace_module_init(void) -{ - int status; - - root_dev = NULL; + for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; + if (skb) { +#ifndef DUMMY_PCI_UNMAP + dma_addr_t mapping; -#ifdef NEW_NETINIT - status = acenic_probe(); -#else - status = acenic_probe(NULL); + mapping = ap->skb->rx_jumbo_skbuff[i].mapping; + pci_unmap_single(ap->pdev, mapping, + ACE_JUMBO_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); #endif - return status; -} + ap->rx_jumbo_ring[i].size = 0; + ap->skb->rx_jumbo_skbuff[i].skb = NULL; + dev_kfree_skb(skb); + } + } -#if (LINUX_VERSION_CODE < 0x02032a) -#ifdef MODULE -int init_module(void) -{ - return ace_module_init(); + ace_init_cleanup(dev); + kfree(dev); } - -void cleanup_module(void) -{ - ace_module_cleanup(); -} -#endif -#else -module_init(ace_module_init); -module_exit(ace_module_cleanup); -#endif - - static void ace_free_descriptors(struct net_device *dev) { struct ace_private *ap = dev->priv; @@ -1324,13 +1217,6 @@ goto init_error; } - /* - * Register the device here to be able to catch allocated - * interrupt handlers in case the firmware doesn't come up. - */ - ap->next = root_dev; - root_dev = dev; - #ifdef INDEX_DEBUG spin_lock_init(&ap->debug_lock); ap->last_tx = TX_RING_ENTRIES - 1; @@ -2525,7 +2411,7 @@ #if defined(CONFIG_X86) #define DMAADDR_OFFSET 0 typedef unsigned long long dmaaddr_high_t; -#elif defined(CONFIG_PPC) +#elif defined(CONFIG_PPC32) #define DMAADDR_OFFSET PCI_DRAM_OFFSET typedef unsigned long dmaaddr_high_t; #endif @@ -3338,3 +3224,45 @@ * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" * End: */ + +static struct pci_device_id acenic_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + /* + * Farallon used the DEC vendor ID on their cards incorrectly. + */ + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { } +}; +MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); + +static struct pci_driver acenic_driver = { + name: "acenic", + id_table: acenic_pci_tbl, + probe: acenic_init_one, + remove: acenic_remove_one, +}; + +static int __init acenic_init_module(void) +{ + return pci_module_init(&acenic_driver); +} + +static void __exit acenic_cleanup_module(void) +{ + pci_unregister_driver(&acenic_driver); +} + +module_init(acenic_init_module); +module_exit(acenic_cleanup_module); diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/net/acenic.h linuxppc64_2_4/drivers/net/acenic.h --- linux-2.4.8-ac9/drivers/net/acenic.h Thu Jun 28 16:47:10 2001 +++ linuxppc64_2_4/drivers/net/acenic.h Wed Aug 22 08:27:45 2001 @@ -582,7 +582,7 @@ aceaddr stats2_ptr; }; -#if defined(CONFIG_X86) || defined(CONFIG_PPC) +#if defined(CONFIG_X86) || defined(CONFIG_PPC32) /* Intel has null pci_unmap_single, no reasons to remember mapping. */ #define DUMMY_PCI_UNMAP #endif diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/net/pcnet32.c linuxppc64_2_4/drivers/net/pcnet32.c --- linux-2.4.8-ac9/drivers/net/pcnet32.c Thu Aug 23 09:17:27 2001 +++ linuxppc64_2_4/drivers/net/pcnet32.c Thu Aug 16 08:50:26 2001 @@ -294,7 +294,7 @@ static int pcnet32_probe_vlbus(int cards_found); static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *); -static int pcnet32_probe1(unsigned long, unsigned char, int, int, struct pci_dev *); +static int pcnet32_probe1(unsigned long, unsigned int, int, int, struct pci_dev *); static int pcnet32_open(struct net_device *); static int pcnet32_init_ring(struct net_device *); static int pcnet32_start_xmit(struct sk_buff *, struct net_device *); @@ -317,7 +317,7 @@ const char *name; u16 vendor_id, device_id, svid, sdid, flags; int io_size; - int (*probe1) (unsigned long, unsigned char, int, int, struct pci_dev *); + int (*probe1) (unsigned long, unsigned int, int, int, struct pci_dev *); }; @@ -440,7 +440,9 @@ static int __init pcnet32_probe_vlbus(int cards_found) { unsigned long ioaddr = 0; // FIXME dev ? dev->base_addr: 0; +#ifndef __powerpc__ unsigned int irq_line = 0; // FIXME dev ? dev->irq : 0; +#endif int *port; printk(KERN_INFO "pcnet32_probe_vlbus: cards_found=%d\n", cards_found); @@ -508,7 +510,7 @@ * pdev will be NULL when called from pcnet32_probe_vlbus. */ static int __devinit -pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx, struct pci_dev *pdev) +pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared, int card_idx, struct pci_dev *pdev) { struct pcnet32_private *lp; struct resource *res; @@ -527,12 +529,13 @@ pcnet32_dwio_reset(ioaddr); pcnet32_wio_reset(ioaddr); - /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */ - if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) { - a = &pcnet32_wio; + /* Important to do the check for dwio mode first. */ + if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { + a = &pcnet32_dwio; } else { - if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { - a = &pcnet32_dwio; + if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && + pcnet32_wio_check(ioaddr)) { + a = &pcnet32_wio; } else return -ENODEV; } @@ -643,19 +646,31 @@ for (i = 0; i < 6; i++) { promaddr[i] = inb(ioaddr + i); } + printk("csraddr: "); + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] ); + printk("\npromaddr: "); + for (i = 0; i < 6; i++) + printk(" %2.2x", promaddr[i] ); + printk("\n"); if( memcmp( promaddr, dev->dev_addr, 6) ) - { - printk(" warning PROM address does not match CSR address\n"); -#if defined(__i386__) - printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name); - memcpy(dev->dev_addr, promaddr, 6); -#endif - } + printk(" warning: PROM address does not match CSR address\n"); + if( !is_valid_ether_addr(dev->dev_addr) ) { + printk("bad csr addr\n"); + if (!is_valid_ether_addr(promaddr)) { + printk("bad promaddr\n"); + /* if neither ethernet address is not valid, force to 00:00:00:00:00:00 */ + for (i = 0; i < 6; i++) + dev->dev_addr[i]=0; + } + else + { + printk(" warning: Using PROM address\n"); + for (i = 0; i < 6; i++) + dev->dev_addr[i]=promaddr[i]; + } + } } - /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */ - if( !is_valid_ether_addr(dev->dev_addr) ) - for (i = 0; i < 6; i++) - dev->dev_addr[i]=0; for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i] ); @@ -885,7 +900,7 @@ lp->init_block.filter[1] = 0x00000000; if (pcnet32_init_ring(dev)) return -ENOMEM; - + /* Re-initialize the PCNET32, and start it when done. */ lp->a.write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff); lp->a.write_csr (ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16); @@ -967,7 +982,7 @@ } skb_reserve (rx_skbuff, 2); } - lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, rx_skbuff->len, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); lp->rx_ring[i].base = (u32)le32_to_cpu(lp->rx_dma_addr[i]); lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); lp->rx_ring[i].status = le16_to_cpu(0x8000); @@ -1301,7 +1316,8 @@ skb_put (skb, pkt_len); lp->rx_skbuff[entry] = newskb; newskb->dev = dev; - lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, newskb->len, PCI_DMA_FROMDEVICE); + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[entry], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); lp->rx_ring[entry].base = le32_to_cpu(lp->rx_dma_addr[entry]); rx_in_place = 1; } else @@ -1387,7 +1403,7 @@ for (i = 0; i < RX_RING_SIZE; i++) { lp->rx_ring[i].status = 0; if (lp->rx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], lp->rx_skbuff[i]->len, PCI_DMA_FROMDEVICE); + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); dev_kfree_skb(lp->rx_skbuff[i]); } lp->rx_skbuff[i] = NULL; diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/net/veth-proc.c linuxppc64_2_4/drivers/net/veth-proc.c --- linux-2.4.8-ac9/drivers/net/veth-proc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/net/veth-proc.c Fri May 4 17:12:47 2001 @@ -0,0 +1,73 @@ +/* File veth-proc.c created by Kyle A. Lucke on Wed Oct 18 2000. */ + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_PROC_H +#include +#endif +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VETH_H +#include "veth.h" +#endif + + +static struct proc_dir_entry * veth_proc_root = NULL; + +void veth_proc_init(struct proc_dir_entry *iSeries_proc) +{ + long i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + long vlanIndex = 0; + + + veth_proc_root = proc_mkdir("veth", iSeries_proc); + if (!veth_proc_root) return; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "lp%ld", i); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)i; + ent->read_proc = proc_veth_dump_connection; + ent->write_proc = NULL; + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "veth%ld", vlanIndex); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)vlanIndex; + ent->read_proc = proc_veth_dump_port; + ent->write_proc = NULL; + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } +} + diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/net/veth.c linuxppc64_2_4/drivers/net/veth.c --- linux-2.4.8-ac9/drivers/net/veth.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/net/veth.c Fri Aug 17 13:03:16 2001 @@ -0,0 +1,1720 @@ +/* File veth.c created by Kyle A. Lucke on Mon Aug 7 2000. */ + +/**************************************************************************/ +/* */ +/* IBM eServer iSeries Virtual Ethernet Device Driver */ +/* Copyright (C) 2001 Kyle A. Lucke (klucke@raleigh.ibm.com), IBM Corp. */ +/* */ +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +/* USA */ +/* */ +/* This module contains the implementation of a virtual ethernet device */ +/* for use with iSeries LPAR Linux. It utilizes low-level message passing*/ +/* provided by the hypervisor to enable an ethernet-like network device */ +/* that can be used to enable inter-partition communications on the same */ +/* physical iSeries. */ +/* */ +/* The iSeries LPAR hypervisor has currently defined the ability for a */ +/* partition to communicate on up to 16 different virtual ethernets, all */ +/* dynamically configurable, at least for an OS/400 partition. The */ +/* dynamic nature is not supported for Linux yet. */ +/* */ +/* Each virtual ethernet a given Linux partition participates in will */ +/* cause a network device with the form vethXX to be created, where XX is */ +/* a decimal number from 0 to 15, corresponding to the virtual ethernet */ +/* the given netdevice talks on. This is slightly different from the */ +/* standard eth0, eth1, etc. way of naming network devices, but without */ +/* this little naming convention, it would not be as easy to configure */ +/* the tcpip interfaces to a given veth device, and if the partition */ +/* was configured to use a new virtual ethernet at some point, the devices*/ +/* would most likely get renumbered. */ +/* */ +/* This driver (and others like it on other partitions) is responsible */ +/* routing packets to and from other partitions. The MAC addresses used */ +/* by the virtual ethernets contain meaning, and should not be modified. */ +/* Doing so could disable the ability of your Linux partition to */ +/* communicate with the other OS/400 partitions on your physical iSeries. */ +/* Similarly, setting the MAC address to something other than the */ +/* "virtual burned-in" address is not allowed, for the same reason. */ +/* */ +/* Notes: */ +/* */ +/* 1. Although there is the capability to talk on multiple shared */ +/* ethernets to communicate to the same partition, each shared */ +/* ethernet to a given partition X will use a finite, shared amount */ +/* of hypervisor messages to do the communication. So having 2 shared */ +/* ethernets to the same remote partition DOES NOT double the */ +/* available bandwidth. Each of the 2 shared ethernets will share the */ +/* same bandwidth available to another. */ +/* */ +/* 2. It is allowed to have a virtual ethernet that does not communicate */ +/* with any other partition. It won't do anything, but it's allowed. */ +/* */ +/* 3. There is no "loopback" mode for a virtual ethernet device. If you */ +/* send a packet to your own mac address, it will just be dropped, you */ +/* won't get it on the receive side. Such a thing could be done, */ +/* but my default driver DOES NOT do so. */ +/* */ +/* 4. Multicast addressing is implemented via broadcasting the multicast */ +/* frames to other partitions. It is the responsibility of the */ +/* receiving partition to filter the addresses desired. */ +/* */ +/* 5. This module utilizes several different bottom half handlers for */ +/* non-high-use path function (setup, error handling, etc.). Multiple */ +/* bottom halves were used because only one would not keep up to the */ +/* much faster iSeries device drivers this Linux driver is talking to. */ +/* All hi-priority work (receiving frames, handling frame acks) is done*/ +/* in the interrupt handler for maximum performance. */ +/* */ +/* Tunable parameters: */ +/* */ +/* VethBuffersToAllocate: This compile time option defaults to 120. It can*/ +/* be safely changed to something greater or less than the default. It */ +/* controls how much memory Linux will allocate per remote partition it is*/ +/* communicating with. The user can play with this to see how it affects */ +/* performance, packets dropped, etc. Without trying to understand the */ +/* complete driver, it can be thought of as the maximum number of packets */ +/* outstanding to a remote partition at a time. */ +/* */ +/**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VETH_H +#include "veth.h" +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VETH_PROC_H +#include +#endif +#ifndef _HVTYPES_H +#include +#endif +#ifndef _ISERIES_PROC_H +#include +#endif +#include +#include + + +#define veth_printk(fmt, args...) \ +printk(KERN_INFO "%s: " fmt, __FILE__, ## args) + +#define veth_error_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args) + +static const char __initdata *version = +"v0.9 02/15/2001 Kyle Lucke, klucke@raleigh.ibm.com, klucke@us.ibm.com\n"; + +static int probed __initdata = 0; +#define VethBuffersToAllocate 120 + +static struct VethFabricMgr *mFabricMgr = NULL; +static struct proc_dir_entry * veth_proc_root = NULL; + +DECLARE_MUTEX_LOCKED(VethProcSemaphore); + +static int veth_open(struct net_device *dev); +static int veth_close(struct net_device *dev); +static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static void veth_handleEvent(struct HvLpEvent *, struct pt_regs *); +static void veth_handleAck(struct HvLpEvent *); +static void veth_handleInt(struct HvLpEvent *); +static void veth_openConnections(void); +static void veth_openConnection(u8, int lockMe); +static void veth_closeConnection(u8, int lockMe); +static void veth_intFinishOpeningConnections(void *, int number); +static void veth_finishOpeningConnections(void *); +static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *); +static int veth_multicast_wanted(struct VethPort *port, u64 dest); +static void veth_set_multicast_list(struct net_device *dev); + +static void veth_sendCap(struct VethLpConnection *); +static void veth_sendMonitor(struct VethLpConnection *); +static void veth_takeCap(struct VethLpConnection *, struct VethLpEvent *); +static void veth_takeCapAck(struct VethLpConnection *, struct VethLpEvent *); +static void veth_takeMonitorAck(struct VethLpConnection *, struct VethLpEvent *); +static void veth_msgsInit(struct VethLpConnection *connection); +static void veth_recycleMsg(struct VethLpConnection *, u16); +static void veth_capBh(struct VethLpConnection *); +static void veth_capAckBh(struct VethLpConnection *); +static void veth_monitorAckBh(struct VethLpConnection *); +static void veth_takeFrames(struct VethLpConnection *, struct VethLpEvent *); +static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev); +static struct net_device_stats *veth_get_stats(struct net_device *dev); +static void veth_intFinishMsgsInit(void *, int); +static void veth_finishMsgsInit(struct VethLpConnection *connection); +static void veth_intFinishCapBh(void *, int); +static void veth_finishCapBh(struct VethLpConnection *connection); +static void veth_finishCapBhLocked(struct VethLpConnection *connection); +static void veth_finishSendCap(struct VethLpConnection *connection); +static void veth_timedAck(unsigned long connectionPtr); +#ifdef MODULE +static void veth_waitForEnd(void); +#endif +static void veth_failMe(struct VethLpConnection *connection); + +int __init veth_probe(void) +{ + struct net_device *dev= NULL; + struct VethPort *port = NULL; + int vlansFound = 0; + int displayVersion = 0; + + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + if (probed) + return -ENODEV; + probed = 1; + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + vlansFound++; + + dev = init_vethdev(NULL, sizeof(struct VethPort), vlanIndex); + + if (dev == NULL) { + veth_error_printk("Unable to allocate net_device structure!\n"); + break; + } + + if (!dev->priv) + dev->priv = kmalloc(sizeof(struct VethPort), GFP_KERNEL); + if (!dev->priv) { + veth_error_printk("Unable to allocate memory\n"); + return -ENOMEM; + } + + veth_printk("Found an ethernet device %s (veth=%d) (addr=%p)\n", dev->name, vlanIndex, dev); + port = mFabricMgr->mPorts[vlanIndex] = (struct VethPort *)dev->priv; + memset(port, 0, sizeof(struct VethPort)); + rwlock_init(&(port->mMcastGate)); + mFabricMgr->mPorts[vlanIndex]->mDev = dev; + + dev->dev_addr[0] = 0x02; + dev->dev_addr[1] = 0x01; + dev->dev_addr[2] = 0xFF; + dev->dev_addr[3] = vlanIndex; + dev->dev_addr[4] = 0xFF; + dev->dev_addr[5] = HvLpConfig_getLpIndex_outline(); + dev->mtu = 9000; + + memcpy(&(port->mMyAddress), dev->dev_addr, 6); + + dev->open = &veth_open; + dev->hard_start_xmit = &veth_start_xmit; + dev->stop = &veth_close; + dev->get_stats = veth_get_stats; + dev->set_multicast_list = &veth_set_multicast_list; + dev->do_ioctl = &veth_ioctl; + + /* display version info if adapter is found */ + if (!displayVersion) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + displayVersion = 1; + veth_printk("%s", version); + } + + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + if (vlansFound > 0) + return 0; + else + return -ENODEV; +} + +#ifdef MODULE +MODULE_AUTHOR("Kyle Lucke , "); +MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); + +DECLARE_MUTEX_LOCKED(VethModuleBhDone); +int VethModuleReopen = 1; + +void veth_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + int i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + char name[10] = ""; + sprintf(name, "lp%d", i); + remove_proc_entry(name, veth_proc_root); + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + char name[10] = ""; + sprintf(name, "veth%d", vlanIndex); + remove_proc_entry(name, veth_proc_root); + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + remove_proc_entry("veth", iSeries_proc); + + up(&VethProcSemaphore); +} + +void veth_waitForEnd(void) +{ + up(&VethModuleBhDone); +} + +void __exit veth_module_cleanup(void) +{ + int i; + struct VethFabricMgr *myFm = mFabricMgr; + struct tq_struct myBottomHalf; + struct net_device *thisOne = NULL; + + VethModuleReopen = 0; + + for (i = 0; i < HvMaxArchitectedLps; ++i) + { + veth_closeConnection(i, 1); + } + + myBottomHalf.routine = (void *)(void *)veth_waitForEnd; + + queue_task(&myBottomHalf, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + down(&VethModuleBhDone); + + HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); + + mb(); + mFabricMgr = NULL; + mb(); + + down(&VethProcSemaphore); + + iSeries_proc_callback(&veth_proc_delete); + + down(&VethProcSemaphore); + + for (i = 0; i < HvMaxArchitectedLps; ++i) + { + if (myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs > 0) + { + mf_deallocateLpEvents(myFm->mConnection[i].mRemoteLp, + HvLpEvent_Type_VirtualLan, + myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs, + NULL, + NULL); + } + + if (myFm->mConnection[i].mMsgs != NULL) + { + kfree(myFm->mConnection[i].mMsgs); + } + } + + for (i = 0; i < HvMaxArchitectedVirtualLans; ++i) + { + if (myFm->mPorts[i] != NULL) + { + thisOne = myFm->mPorts[i]->mDev; + myFm->mPorts[i] = NULL; + + mb(); + + if (thisOne != NULL) + { + veth_printk("Unregistering %s (veth=%d)\n", thisOne->name, i); + unregister_netdev(thisOne); + } + } + } + + kfree(myFm); +} + +module_exit(veth_module_cleanup); +#endif + + +void veth_proc_init(struct proc_dir_entry *iSeries_proc) +{ + int i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + + veth_proc_root = proc_mkdir("veth", iSeries_proc); + if (!veth_proc_root) return; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "lpar%d", i); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)i; + ent->read_proc = proc_veth_dump_connection; + ent->write_proc = NULL; + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "veth%d", vlanIndex); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)vlanIndex; + ent->read_proc = proc_veth_dump_port; + ent->write_proc = NULL; + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + up(&VethProcSemaphore); +} + +int __init veth_module_init(void) +{ + int status; + int i; + + mFabricMgr = kmalloc(sizeof(struct VethFabricMgr), GFP_KERNEL); + memset(mFabricMgr, 0, sizeof(struct VethFabricMgr)); + veth_printk("Initializing veth module, fabric mgr (address=%p)\n", mFabricMgr); + + mFabricMgr->mEyecatcher = 0x56455448464D4752ULL; + mFabricMgr->mThisLp = HvLpConfig_getLpIndex_outline(); + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + mFabricMgr->mConnection[i].mEyecatcher = 0x564554484C50434EULL; + veth_failMe(mFabricMgr->mConnection+i); + spin_lock_init(&mFabricMgr->mConnection[i].mAckGate); + spin_lock_init(&mFabricMgr->mConnection[i].mStatusGate); + } + + status = veth_probe(); + + if (status == 0) + { + veth_openConnections(); + } + + iSeries_proc_callback(&veth_proc_init); + + return status; +} + +module_init(veth_module_init); + +static void veth_failMe(struct VethLpConnection *connection) +{ + connection->mConnectionStatus.mSentCap = 0; + connection->mConnectionStatus.mCapAcked = 0; + connection->mConnectionStatus.mGotCap = 0; + connection->mConnectionStatus.mGotCapAcked = 0; + connection->mConnectionStatus.mSentMonitor = 0; + connection->mConnectionStatus.mFailed = 1; +} + +static int veth_open(struct net_device *dev) +{ + struct VethPort *port = (struct VethPort *)dev->priv; + + memset(&port->mStats, 0, sizeof(port->mStats)); + MOD_INC_USE_COUNT; + + netif_start_queue(dev); + + return 0; +} + +static int veth_close(struct net_device *dev) +{ + netif_stop_queue(dev); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct net_device_stats *veth_get_stats(struct net_device *dev) +{ + struct VethPort *port = (struct VethPort *)dev->priv; + + return(&port->mStats); +} + + +static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned char *frame = skb->data; + HvLpIndex remoteLp = frame[5]; + int i = 0; + int clone = 0; + + if (mFabricMgr == NULL) + { + veth_error_printk("NULL fabric manager with active ports!\n"); + netif_stop_queue(dev); + return 1; + } + + mb(); + + if ((*frame & 0x01) != 0x01) /* broadcast or multicast */ + { + if ((remoteLp != mFabricMgr->mThisLp) && + (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, remoteLp))) + veth_pTransmit(skb, remoteLp, dev); + } + else + { + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != mFabricMgr->mThisLp) + { + if (clone) + skb = skb_clone(skb, GFP_ATOMIC); + else + clone = 1; + + if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) + { + /* the ack handles deleting the skb */ + veth_pTransmit(skb, i, dev); + } + } + } + } + + return 0; +} + +static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev) +{ + struct VethLpConnection *connection = mFabricMgr->mConnection + remoteLp; + HvLpEvent_Rc returnCode; + + if (connection->mConnectionStatus.mFailed != 1) + { + struct VethMsg *msg = NULL; + VETHSTACKPOP(&(connection->mMsgStack), msg); + + if (msg != NULL) + { + if ((skb->len > 14) && + (skb->len <= 9018)) + { + dma_addr_t dma_addr = pci_map_single(NULL, + skb->data, + skb->len, + PCI_DMA_TODEVICE); + + if (dma_addr != -1) + { + msg->mSkb = skb; + msg->mEvent.mSendData.mAddress[0] = dma_addr; + msg->mEvent.mSendData.mLength[0] = skb->len; + msg->mEvent.mSendData.mEofMask = 0xFFFFFFFFUL; + + test_and_set_bit(0, &(msg->mInUse)); + + returnCode = HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFrames, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + msg->mIndex, + msg->mEvent.mFpData.mData1, + msg->mEvent.mFpData.mData2, + msg->mEvent.mFpData.mData3, + msg->mEvent.mFpData.mData4, + msg->mEvent.mFpData.mData5); + } + else + { + returnCode = -1; /* Bad return code */ + } + + if (returnCode != HvLpEvent_Rc_Good) + { + struct VethPort *port = (struct VethPort *)dev->priv; + + if (msg->mEvent.mSendData.mAddress[0]) + { + pci_unmap_single(NULL, dma_addr, skb->len, PCI_DMA_TODEVICE); + } + + dev_kfree_skb_irq(skb); + + msg->mSkb = NULL; + memset(&(msg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); + VETHSTACKPUSH(&(connection->mMsgStack), msg); + port->mStats.tx_dropped++; + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_packets++; + port->mStats.tx_bytes += skb->len; + } + } + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_dropped++; + } + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_dropped++; + } +} + +static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + + return -EOPNOTSUPP; +} + +static void veth_set_multicast_list(struct net_device *dev) +{ + char *addrs; + struct VethPort *port = (struct VethPort *)dev->priv; + u64 newAddress = 0; + unsigned long flags; + + write_lock_irqsave(&port->mMcastGate, flags); + + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + port->mPromiscuous = 1; + } else { + struct dev_mc_list *dmi = dev->mc_list; + + if (dev->flags & IFF_ALLMULTI) { + port->mAllMcast = 1; + } else { + int i; + /* Update table */ + port->mNumAddrs = 0; + + for (i = 0; ((i < dev->mc_count) && (i < 12)); i++) { /* for each address in the list */ + addrs = dmi->dmi_addr; + dmi = dmi->next; + if ((*addrs & 0x01) == 1) { /* multicast address? */ + memcpy(&newAddress, addrs, 6); + newAddress &= 0xFFFFFFFFFFFF0000; + + port->mMcasts[port->mNumAddrs] = newAddress; + mb(); + port->mNumAddrs = port->mNumAddrs + 1; + } + } + } + } + + write_unlock_irqrestore(&port->mMcastGate, flags); +} + + +static void veth_handleEvent(struct HvLpEvent *event, struct pt_regs *regs) +{ + if (event->xFlags.xFunction == HvLpEvent_Function_Ack) + { + veth_handleAck(event); + } + else if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + veth_handleInt(event); + } +} + +static void veth_handleAck(struct HvLpEvent *event) +{ + struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xTargetLp]); + struct VethLpEvent *vethEvent = (struct VethLpEvent *)event; + + switch(event->xSubtype) + { + case VethEventTypeCap: + { + veth_takeCapAck(connection, vethEvent); + break; + } + case VethEventTypeMonitor: + { + veth_takeMonitorAck(connection, vethEvent); + break; + } + default: + { + veth_error_printk("Unknown ack type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); + } + }; +} + +static void veth_handleInt(struct HvLpEvent *event) +{ + int i=0; + struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xSourceLp]); + struct VethLpEvent *vethEvent = (struct VethLpEvent *)event; + + switch(event->xSubtype) + { + case VethEventTypeCap: + { + veth_takeCap(connection, vethEvent); + break; + } + case VethEventTypeMonitor: + { + /* do nothing... this'll hang out here til we're dead, and the hypervisor will return it for us. */ + break; + } + case VethEventTypeFramesAck: + { + for (i=0; i < VethMaxFramesMsgsAcked; ++i) + { + u16 msg = vethEvent->mDerivedData.mFramesAckData.mToken[i]; + veth_recycleMsg(connection, msg); + } + break; + } + case VethEventTypeFrames: + { + veth_takeFrames(connection, vethEvent); + break; + } + default: + { + veth_error_printk("Unknown interrupt type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); + } + }; +} + +static void veth_openConnections() +{ + int i=0; + + HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, &veth_handleEvent); + + /* Now I need to run through the active lps and open connections to the ones I'm supposed to + open to. */ + + for (i=HvMaxArchitectedLps-1; i >=0; --i) + { + if (i != mFabricMgr->mThisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) + { + veth_openConnection(i, 1); + } + else + { + veth_closeConnection(i, 1); + } + } + } +} + +static void veth_intFinishOpeningConnections(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + connection->mNumberAllocated = number; + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_finishOpeningConnections(void *parm) +{ + unsigned long flags; + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + spin_lock_irqsave(&connection->mStatusGate, flags); + veth_finishOpeningConnectionsLocked(connection); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *connection) +{ + if (connection->mNumberAllocated >= 2) + { + connection->mConnectionStatus.mCapMonAlloced = 1; + veth_sendCap(connection); + } + else + { + veth_error_printk("Couldn't allocate base msgs for lpar %d, only got %d\n", connection->mRemoteLp, connection->mNumberAllocated); + veth_failMe(connection); + } +} + +static void veth_openConnection(u8 remoteLp, int lockMe) +{ + unsigned long flags; + unsigned long flags2; + HvLpInstanceId source; + HvLpInstanceId target; + u64 i = 0; + struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]); + + memset(&connection->mCapBhTq, 0, sizeof(connection->mCapBhTq)); + connection->mCapBhTq.routine = (void *)(void *)veth_capBh; + + memset(&connection->mCapAckBhTq, 0, sizeof(connection->mCapAckBhTq)); + connection->mCapAckBhTq.routine = (void *)(void *)veth_capAckBh; + + memset(&connection->mMonitorAckBhTq, 0, sizeof(connection->mMonitorAckBhTq)); + connection->mMonitorAckBhTq.routine = (void *)(void *)veth_monitorAckBh; + + memset(&connection->mAllocBhTq, 0, sizeof(connection->mAllocBhTq)); + connection->mAllocBhTq.routine = (void *)(void *)veth_finishOpeningConnections; + + if (lockMe) + spin_lock_irqsave(&connection->mStatusGate, flags); + + connection->mRemoteLp = remoteLp; + + spin_lock_irqsave(&connection->mAckGate, flags2); + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + connection->mNumAcks = 0; + + HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); + + /* clean up non-acked msgs */ + for (i=0; i < connection->mNumMsgs; ++i) + { + veth_recycleMsg(connection, i); + } + + connection->mConnectionStatus.mOpen = 1; + + source = connection->mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); + target = connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); + + if (connection->mConnectionStatus.mCapMonAlloced != 1) + { + connection->mAllocBhTq.routine = (void *)(void *)veth_finishOpeningConnections; + mf_allocateLpEvents(remoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + 2, + &veth_intFinishOpeningConnections, + connection); + } + else + { + veth_finishOpeningConnectionsLocked(connection); + } + + spin_unlock_irqrestore(&connection->mAckGate, flags2); + + if (lockMe) + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_closeConnection(u8 remoteLp, int lockMe) +{ + struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]); + unsigned long flags; + unsigned long flags2; + if (lockMe) + spin_lock_irqsave(&connection->mStatusGate, flags); + + del_timer(&connection->mAckTimer); + + if (connection->mConnectionStatus.mOpen == 1) + { + HvCallEvent_closeLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); + connection->mConnectionStatus.mOpen = 0; + veth_failMe(connection); + + /* reset ack data */ + spin_lock_irqsave(&connection->mAckGate, flags2); + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + connection->mNumAcks = 0; + + spin_unlock_irqrestore(&connection->mAckGate, flags2); + } + + if (lockMe) + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_msgsInit(struct VethLpConnection *connection) +{ + connection->mAllocBhTq.routine = (void *)(void *)veth_finishMsgsInit; + mf_allocateLpEvents(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + connection->mMyCap.mUnionData.mFields.mNumberBuffers, + &veth_intFinishMsgsInit, + connection); +} + +static void veth_intFinishMsgsInit(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + connection->mNumberRcvMsgs = number; + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_intFinishCapBh(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + if (number > 0) + connection->mNumberLpAcksAlloced += number; + + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_finishMsgsInit(struct VethLpConnection *connection) +{ + int i=0; + unsigned int numberGotten = 0; + u64 amountOfHeapToGet = connection->mMyCap.mUnionData.mFields.mNumberBuffers * sizeof(struct VethMsg); + char *msgs = NULL; + unsigned long flags; + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (connection->mNumberRcvMsgs >= connection->mMyCap.mUnionData.mFields.mNumberBuffers) + { + msgs = kmalloc(amountOfHeapToGet, GFP_ATOMIC); + + connection->mMsgs = (struct VethMsg *)msgs; + + if (msgs != NULL) + { + memset(msgs, 0, amountOfHeapToGet); + + for (i=0; i < connection->mMyCap.mUnionData.mFields.mNumberBuffers; ++i) + { + connection->mMsgs[i].mIndex = i; + ++numberGotten; + VETHSTACKPUSH(&(connection->mMsgStack), (connection->mMsgs+i)); + } + if (numberGotten > 0) + { + connection->mNumMsgs = numberGotten; + } + } + else + { + kfree(msgs); + connection->mMsgs = NULL; + } + } + + connection->mMyCap.mUnionData.mFields.mNumberBuffers = connection->mNumMsgs; + + if (connection->mNumMsgs < 10) + connection->mMyCap.mUnionData.mFields.mThreshold = 1; + else if (connection->mNumMsgs < 20) + connection->mMyCap.mUnionData.mFields.mThreshold = 4; + else if (connection->mNumMsgs < 40) + connection->mMyCap.mUnionData.mFields.mThreshold = 10; + else + connection->mMyCap.mUnionData.mFields.mThreshold = 20; + + connection->mMyCap.mUnionData.mFields.mTimer = VethAckTimeoutUsec; + + veth_finishSendCap(connection); + + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_sendCap(struct VethLpConnection *connection) +{ + if (connection->mMsgs == NULL) + { + connection->mMyCap.mUnionData.mFields.mNumberBuffers = VethBuffersToAllocate; + veth_msgsInit(connection); + } + else + { + veth_finishSendCap(connection); + } +} + +static void veth_finishSendCap(struct VethLpConnection *connection) +{ + HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeCap, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mMyCap.mUnionData.mNoFields.mReserved1, + connection->mMyCap.mUnionData.mNoFields.mReserved2, + connection->mMyCap.mUnionData.mNoFields.mReserved3, + connection->mMyCap.mUnionData.mNoFields.mReserved4, + connection->mMyCap.mUnionData.mNoFields.mReserved5); + + if ((returnCode == HvLpEvent_Rc_PartitionDead) || + (returnCode == HvLpEvent_Rc_PathClosed)) + { + connection->mConnectionStatus.mSentCap = 0; + } + else if (returnCode != HvLpEvent_Rc_Good) + { + veth_error_printk("Couldn't send cap to lpar %d, rc %Lx\n", connection->mRemoteLp, returnCode); + veth_failMe(connection); + } + else + { + connection->mConnectionStatus.mSentCap = 1; + } +} + +static void veth_takeCap(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mCapBhPending))) + { + connection->mCapBhTq.data = connection; + memcpy(&connection->mCapEvent, event, sizeof(connection->mCapEvent)); + queue_task(&connection->mCapBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a capabilities from lpar %d while already processing one\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } +} + +static void veth_takeCapAck(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mCapAckBhPending))) + { + connection->mCapAckBhTq.data = connection; + memcpy(&connection->mCapAckEvent, event, sizeof(connection->mCapAckEvent)); + queue_task(&connection->mCapAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a capabilities ack from lpar %d while already processing one\n", connection->mRemoteLp); + } +} + +static void veth_takeMonitorAck(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mMonitorAckBhPending))) + { + connection->mMonitorAckBhTq.data = connection; + memcpy(&connection->mMonitorAckEvent, event, sizeof(connection->mMonitorAckEvent)); + queue_task(&connection->mMonitorAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a monitor ack from lpar %d while already processing one\n", connection->mRemoteLp); + } +} + +static void veth_recycleMsg(struct VethLpConnection *connection, u16 msg) +{ + if (msg < connection->mNumMsgs) + { + struct VethMsg *myMsg = connection->mMsgs + msg; + if (test_and_clear_bit(0, &(myMsg->mInUse))) + { + pci_unmap_single(NULL, + myMsg->mEvent.mSendData.mAddress[0], + myMsg->mEvent.mSendData.mLength[0], + PCI_DMA_TODEVICE); + dev_kfree_skb_irq(myMsg->mSkb); + + myMsg->mSkb = NULL; + memset(&(myMsg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); + VETHSTACKPUSH(&connection->mMsgStack, myMsg); + } + else + { + if (connection->mConnectionStatus.mOpen) + { + veth_error_printk("Received a frames ack for msg %d from lpar %d while not outstanding\n", msg, connection->mRemoteLp); + } + } + } +} + +static void veth_capBh(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapEvent; + unsigned long flags; + struct VethCapData *remoteCap = &(connection->mRemoteCap); + u64 numAcks = 0; + spin_lock_irqsave(&connection->mStatusGate, flags); + connection->mConnectionStatus.mGotCap = 1; + + memcpy(remoteCap, &(event->mDerivedData.mCapabilitiesData), sizeof(connection->mRemoteCap)); + + if ((remoteCap->mUnionData.mFields.mNumberBuffers <= VethMaxFramesMsgs) && + (remoteCap->mUnionData.mFields.mNumberBuffers != 0) && + (remoteCap->mUnionData.mFields.mThreshold <= VethMaxFramesMsgsAcked) && + (remoteCap->mUnionData.mFields.mThreshold != 0)) + { + numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1; + + if (connection->mNumberLpAcksAlloced < numAcks) + { + numAcks = numAcks - connection->mNumberLpAcksAlloced; + connection->mAllocBhTq.routine = (void *)(void *)veth_finishCapBh; + mf_allocateLpEvents(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + numAcks, + &veth_intFinishCapBh, + connection); + } + else + veth_finishCapBhLocked(connection); + } + else + { + veth_error_printk("Received incompatible capabilities from lpar %d\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_InvalidSubtypeData; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } + + clear_bit(0,&(connection->mCapBhPending)); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_capAckBh(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapAckEvent; + unsigned long flags; + + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (event->mBaseEvent.xRc == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mCapAcked = 1; + + if ((connection->mConnectionStatus.mGotCap == 1) && + (connection->mConnectionStatus.mGotCapAcked == 1)) + { + if (connection->mConnectionStatus.mSentMonitor != 1) + veth_sendMonitor(connection); + } + } + else + { + veth_error_printk("Bad rc(%d) from lpar %d on capabilities\n", event->mBaseEvent.xRc, connection->mRemoteLp); + veth_failMe(connection); + } + + clear_bit(0,&(connection->mCapAckBhPending)); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_monitorAckBh(struct VethLpConnection *connection) +{ + unsigned long flags; + + spin_lock_irqsave(&connection->mStatusGate, flags); + + veth_failMe(connection); + + veth_printk("Monitor ack returned for lpar %d\n", connection->mRemoteLp); + + if (connection->mConnectionStatus.mOpen) + { + veth_closeConnection(connection->mRemoteLp, 0); + + udelay(100); + + queue_task(&connection->mMonitorAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { +#ifdef MODULE + if (VethModuleReopen) +#endif + veth_openConnection(connection->mRemoteLp, 0); +#ifdef MODULE + else + { + int i=0; + + for (i=0; i < connection->mNumMsgs; ++i) + { + veth_recycleMsg(connection, i); + } + } +#endif + clear_bit(0,&(connection->mMonitorAckBhPending)); + } + + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_takeFrames(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + int i; + struct VethPort *port = NULL; + + for (i=0; i < VethMaxFramesPerMsg; ++i) + { + u16 length = event->mDerivedData.mSendData.mLength[i]; + u64 address = event->mDerivedData.mSendData.mAddress[i]; + if ((address != 0) && + (length <= 9018) && + (length > 14)) + { + struct sk_buff *skb = alloc_skb(event->mDerivedData.mSendData.mLength[i], GFP_ATOMIC); + + if (skb != NULL) + { + dma_addr_t toAddress = -1; + HvLpDma_Rc returnCode = HvLpDma_Rc_Good; + toAddress = pci_map_single(NULL, skb->data, + length, + PCI_DMA_FROMDEVICE); + + if (toAddress != -1) + { + returnCode = HvCallEvent_dmaSingle(HvLpEvent_Type_VirtualLan, + event->mBaseEvent.xSourceLp, + HvLpDma_Direction_RemoteToLocal, + connection->mSourceInst, + connection->mTargetInst, + HvLpDma_AddressType_TceIndex, + HvLpDma_AddressType_TceIndex, + toAddress, + address, + length); + + if (returnCode == HvLpDma_Rc_Good) + { + HvLpVirtualLanIndex vlan = skb->data[9]; + u64 dest = *((u64 *)skb->data) & 0xFFFFFFFFFFFF0000; + port = mFabricMgr->mPorts[vlan]; + + if (((vlan < HvMaxArchitectedVirtualLans) && + (port != NULL)) && + ((dest == port->mMyAddress) || /* it's for me */ + (dest == 0xFFFFFFFFFFFF0000) || /* it's a broadcast */ + (veth_multicast_wanted(port, dest)) || /* it's one of my multicasts */ + (port->mPromiscuous == 1))) /* I'm promiscuous */ + { + skb_put(skb, length); + skb->dev = mFabricMgr->mPorts[vlan]->mDev; + skb->protocol = eth_type_trans(skb, mFabricMgr->mPorts[vlan]->mDev); + skb->ip_summed = CHECKSUM_NONE; + netif_rx(skb); /* send it up */ + port->mStats.rx_packets++; + port->mStats.rx_bytes += length; + + } + else + { + dev_kfree_skb_irq(skb); + } + } + else + { + dev_kfree_skb_irq(skb); + } + + pci_unmap_single(NULL, + toAddress, + length, + PCI_DMA_FROMDEVICE); + } + else + { + dev_kfree_skb_irq(skb); + } + } + } + } + /* Ack it */ + + { + unsigned long flags; + spin_lock_irqsave(&connection->mAckGate, flags); + + if (connection->mNumAcks < VethMaxFramesMsgsAcked) + { + connection->mEventData.mAckData.mToken[connection->mNumAcks] = event->mBaseEvent.xCorrelationToken; + ++connection->mNumAcks; + + if (connection->mNumAcks == connection->mRemoteCap.mUnionData.mFields.mThreshold) + { + HvLpEvent_Rc rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); + + if (rc != HvLpEvent_Rc_Good) + { + veth_error_printk("Bad lp event return code(%Lx) acking frames from lpar %d\n", rc, connection->mRemoteLp); + } + + connection->mNumAcks = 0; + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + } + + } + + spin_unlock_irqrestore(&connection->mAckGate, flags); + } +} + +static void veth_timedAck(unsigned long connectionPtr) +{ + unsigned long flags; + HvLpEvent_Rc rc; + struct VethLpConnection *connection = (struct VethLpConnection *) connectionPtr; + /* Ack all the events */ + spin_lock_irqsave(&connection->mAckGate, flags); + + if (connection->mNumAcks > 0) + { + rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); + + if (rc != HvLpEvent_Rc_Good) + { + veth_error_printk("Bad lp event return code(%Lx) acking frames from lpar %d!\n", rc, connection->mRemoteLp); + } + + connection->mNumAcks = 0; + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + } + + spin_unlock_irqrestore(&connection->mAckGate, flags); + + /* Reschedule the timer */ + connection->mAckTimer.expires = jiffies + connection->mTimeout; + add_timer(&connection->mAckTimer); +} + +static int veth_multicast_wanted(struct VethPort *port, u64 thatAddr) +{ + int returnParm = 0; + int i; + unsigned long flags; + + if ((*((char *)&thatAddr) & 0x01) != 1) + return 0; + + read_lock_irqsave(&port->mMcastGate, flags); + if (port->mAllMcast) + return 1; + + for (i=0; i < port->mNumAddrs; ++i) + { + u64 thisAddr = port->mMcasts[i]; + + if (thisAddr == thatAddr) + { + returnParm = 1; + break; + } + } + read_unlock_irqrestore(&port->mMcastGate, flags); + + return returnParm; +} + +static void veth_sendMonitor(struct VethLpConnection *connection) +{ + HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeMonitor, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_DeferredAck, + connection->mSourceInst, + connection->mTargetInst, + 0, 0, 0, 0, 0, 0); + + if (returnCode == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mSentMonitor = 1; + connection->mConnectionStatus.mFailed = 0; + + /* Start the ACK timer */ + init_timer(&connection->mAckTimer); + connection->mAckTimer.function = veth_timedAck; + connection->mAckTimer.data = (unsigned long) connection; + connection->mAckTimer.expires = jiffies + connection->mTimeout; + add_timer(&connection->mAckTimer); + + } + else + { + veth_error_printk("Monitor send to lpar %d failed with rc %Lx\n", connection->mRemoteLp, returnCode); + veth_failMe(connection); + } +} + +static void veth_finishCapBh(struct VethLpConnection *connection) +{ + unsigned long flags; + spin_lock_irqsave(&connection->mStatusGate, flags); + veth_finishCapBhLocked(connection); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_finishCapBhLocked(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapEvent; + struct VethCapData *remoteCap = &(connection->mRemoteCap); + int numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1; + + /* Convert timer to jiffies */ + if (connection->mMyCap.mUnionData.mFields.mTimer) + connection->mTimeout = remoteCap->mUnionData.mFields.mTimer * HZ / 1000000; + else + connection->mTimeout = VethAckTimeoutUsec * HZ / 1000000; + + if (connection->mNumberLpAcksAlloced >= numAcks) + { + HvLpEvent_Rc returnCode = HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + + if (returnCode == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mGotCapAcked = 1; + + if (connection->mConnectionStatus.mSentCap != 1) + { + connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(connection->mRemoteLp, HvLpEvent_Type_VirtualLan); + + veth_sendCap(connection); + } + else if (connection->mConnectionStatus.mCapAcked == 1) + { + if (connection->mConnectionStatus.mSentMonitor != 1) + veth_sendMonitor(connection); + } + } + else + { + veth_error_printk("Failed to ack remote cap for lpar %d with rc %Lx\n", connection->mRemoteLp, returnCode); + veth_failMe(connection); + } + } + else + { + veth_error_printk("Couldn't allocate all the frames ack events for lpar %d\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } +} + +int proc_veth_dump_connection +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + int whichConnection = (int) data; + int len = 0; + struct VethLpConnection *connection = NULL; + + if ((whichConnection < 0) || (whichConnection > HvMaxArchitectedLps) || (mFabricMgr == NULL)) + { + veth_error_printk("Got bad data from /proc file system\n"); + len = sprintf(page, "ERROR\n"); + } + else + { + int thereWasStuffBefore = 0; + connection = &(mFabricMgr->mConnection[whichConnection]); + + out += sprintf(out, "Remote Lp:\t%d\n", connection->mRemoteLp); + out += sprintf(out, "Source Inst:\t%04X\n", connection->mSourceInst); + out += sprintf(out, "Target Inst:\t%04X\n", connection->mTargetInst); + out += sprintf(out, "Num Msgs:\t%d\n", connection->mNumMsgs); + out += sprintf(out, "Num Lp Acks:\t%d\n", connection->mNumberLpAcksAlloced); + out += sprintf(out, "Num Acks:\t%d\n", connection->mNumAcks); + + if (connection->mConnectionStatus.mOpen) + { + out += sprintf(out, "mConnectionStatus.mCapMonAlloced) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "CapMonAlloced"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mBaseMsgsAlloced) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "BaseMsgsAlloced"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mSentCap) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "SentCap"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mCapAcked) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "CapAcked"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mGotCap) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "GotCap"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mGotCapAcked) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "GotCapAcked"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mSentMonitor) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "SentMonitor"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mPopulatedRings) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "PopulatedRings"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mFailed) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "Failed"); + thereWasStuffBefore = 1; + } + + if (thereWasStuffBefore) + out += sprintf(out, ">"); + + out += sprintf(out, "\n"); + + out += sprintf(out, "Capabilities (System:):\n"); + out += sprintf(out, "\tLocal:<"); + out += sprintf(out, "%d/%d/%d/%d>\n", + connection->mMyCap.mUnionData.mFields.mVersion, + connection->mMyCap.mUnionData.mFields.mNumberBuffers, + connection->mMyCap.mUnionData.mFields.mThreshold, + connection->mMyCap.mUnionData.mFields.mTimer); + out += sprintf(out, "\tRemote:<"); + out += sprintf(out, "%d/%d/%d/%d>\n", + connection->mRemoteCap.mUnionData.mFields.mVersion, + connection->mRemoteCap.mUnionData.mFields.mNumberBuffers, + connection->mRemoteCap.mUnionData.mFields.mThreshold, + connection->mRemoteCap.mUnionData.mFields.mTimer); + len = out - page; + } + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_veth_dump_port +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + int whichPort = (int) data; + int len = 0; + struct VethPort *port = NULL; + + if ((whichPort < 0) || (whichPort > HvMaxArchitectedVirtualLans) || (mFabricMgr == NULL)) + len = sprintf(page, "Virtual ethernet is not configured.\n"); + else + { + int i=0; + u32 *myAddr; + u16 *myEndAddr; + port = mFabricMgr->mPorts[whichPort]; + + if (port != NULL) + { + myAddr = (u32 *)&(port->mMyAddress); + myEndAddr = (u16 *)(myAddr + 1); + out += sprintf(out, "Net device:\t%p\n", port->mDev); + out += sprintf(out, "Address:\t%08X%04X\n", myAddr[0], myEndAddr[0]); + out += sprintf(out, "Promiscuous:\t%d\n", port->mPromiscuous); + out += sprintf(out, "All multicast:\t%d\n", port->mAllMcast); + out += sprintf(out, "Number multicast:\t%d\n", port->mNumAddrs); + + for (i=0; i < port->mNumAddrs; ++i) + { + u32 *multi = (u32 *)&(port->mMcasts[i]); + u16 *multiEnd = (u16 *)(multi + 1); + out += sprintf(out, " %08X%04X\n", multi[0], multiEnd[0]); + } + } + else + { + out += sprintf(page, "veth%d is not configured.\n", whichPort); + } + + len = out - page; + } + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + + diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/net/veth.h linuxppc64_2_4/drivers/net/veth.h --- linux-2.4.8-ac9/drivers/net/veth.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/net/veth.h Fri May 4 17:12:47 2001 @@ -0,0 +1,255 @@ +/* File veth.h created by Kyle A. Lucke on Mon Aug 7 2000. */ + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_H +#define _VETH_H + +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#include + +#define VethEventNumTypes (4) +#define VethEventTypeCap (0) +#define VethEventTypeFrames (1) +#define VethEventTypeMonitor (2) +#define VethEventTypeFramesAck (3) + +#define VethMaxFramesMsgsAcked (20) +#define VethMaxFramesMsgs (0xFFFF) +#define VethMaxFramesPerMsg (6) +#define VethAckTimeoutUsec (1000000) + +#define VETHSTACKTYPE(T) struct VethStack##T +#define VETHSTACK(T) \ +VETHSTACKTYPE(T) \ +{ \ +struct T *head; \ +spinlock_t lock; \ +} +#define VETHSTACKCTOR(s) do { (s)->head = NULL; spin_lock_init(&(s)->lock); } while(0) +#define VETHSTACKPUSH(s, p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(s)->lock,flags); \ +(p)->next = (s)->head; \ +(s)->head = (p); \ +spin_unlock_irqrestore(&(s)->lock, flags); \ +} while(0) + +#define VETHSTACKPOP(s,p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(s)->lock,flags); \ +(p) = (s)->head; \ +if ((s)->head != NULL) \ +{ \ +(s)->head = (s)->head->next; \ +} \ +spin_unlock_irqrestore(&(s)->lock, flags); \ +} while(0) + +#define VETHQUEUE(T) \ +struct VethQueue##T \ +{ \ +T *head; \ +T *tail; \ +spinlock_t lock; \ +} +#define VETHQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; spin_lock_init(&(q)->lock); } while(0) +#define VETHQUEUEENQ(q, p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(q)->lock,flags); \ +(p)->next = NULL; \ +if ((q)->head != NULL) \ +{ \ +(q)->head->next = (p); \ +(q)->head = (p); \ +} \ +else \ +{ \ +(q)->tail = (q)->head = (p); \ +} \ +spin_unlock_irqrestore(&(q)->lock, flags); \ +} while(0) + +#define VETHQUEUEDEQ(q,p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(q)->lock,flags); \ +(p) = (q)->tail; \ +if ((p) != NULL) \ +{ \ +(q)->tail = (p)->next; \ +(p)->next = NULL; \ +} \ +if ((q)->tail == NULL) \ +(q)->head = NULL; \ +spin_unlock_irqrestore(&(q)->lock, flags); \ +} while(0) + +struct VethFramesData +{ + u32 mAddress[6]; + u16 mLength[6]; + u32 mEofMask; +}; + +struct VethFramesAckData +{ + u16 mToken[VethMaxFramesMsgsAcked]; +}; + +struct VethCapData +{ + union + { + struct Fields + { + u8 mVersion; + u8 mReserved1; + u16 mNumberBuffers; + u16 mThreshold; + u16 mReserved2; + u32 mTimer; + u32 mReserved3; + u64 mReserved4; + u64 mReserved5; + u64 mReserved6; + } mFields; + struct NoFields + { + u64 mReserved1; + u64 mReserved2; + u64 mReserved3; + u64 mReserved4; + u64 mReserved5; + } mNoFields; + } mUnionData; +}; + +struct VethFastPathData +{ + u64 mData1; + u64 mData2; + u64 mData3; + u64 mData4; + u64 mData5; +}; + +struct VethLpEvent +{ + struct HvLpEvent mBaseEvent; + union { + struct VethFramesData mSendData; + struct VethCapData mCapabilitiesData; + struct VethFramesAckData mFramesAckData; + struct VethFastPathData mFastPathData; + } mDerivedData; + +}; + +struct VethMsg +{ + struct VethMsg *next; + union { + struct VethFramesData mSendData; + struct VethFastPathData mFpData; + } mEvent; + int mIndex; + unsigned long mInUse; + struct sk_buff *mSkb; +}; + + +struct VethControlBlock +{ + struct net_device *mDev; + struct VethControlBlock *mNext; + HvLpVirtualLanIndex mVlanId; +}; + +struct VethLpConnection +{ + u64 mEyecatcher; + HvLpIndex mRemoteLp; + HvLpInstanceId mSourceInst; + HvLpInstanceId mTargetInst; + u32 mNumMsgs; + struct VethMsg *mMsgs; + int mNumberRcvMsgs; + int mNumberLpAcksAlloced; + union + { + struct VethFramesAckData mAckData; + struct VethFastPathData mFpData; + } mEventData; + spinlock_t mAckGate; + u32 mNumAcks; + spinlock_t mStatusGate; + struct + { + u64 mOpen : 1; + u64 mCapMonAlloced : 1; + u64 mBaseMsgsAlloced : 1; + u64 mSentCap : 1; + u64 mCapAcked : 1; + u64 mGotCap : 1; + u64 mGotCapAcked : 1; + u64 mSentMonitor : 1; + u64 mPopulatedRings : 1; + u64 mReserved : 54; + u64 mFailed : 1; + } mConnectionStatus; + struct VethCapData mMyCap; + struct VethCapData mRemoteCap; + unsigned long mCapAckBhPending; + struct tq_struct mCapAckBhTq; + struct VethLpEvent mCapAckEvent; + unsigned long mCapBhPending; + struct tq_struct mCapBhTq; + struct VethLpEvent mCapEvent; + unsigned long mMonitorAckBhPending; + struct tq_struct mMonitorAckBhTq; + struct VethLpEvent mMonitorAckEvent; + unsigned long mAllocBhPending; + struct tq_struct mAllocBhTq; + int mNumberAllocated; + struct timer_list mAckTimer; + u32 mTimeout; + VETHSTACK(VethMsg) mMsgStack; +}; +#define HVMAXARCHITECTEDVIRTUALLANS 16 +struct VethPort +{ + struct net_device *mDev; + struct net_device_stats mStats; + int mLock; + u64 mMyAddress; + int mPromiscuous; + int mAllMcast; + rwlock_t mMcastGate; + int mNumAddrs; + u64 mMcasts[12]; +}; + +struct VethFabricMgr +{ + u64 mEyecatcher; + HvLpIndex mThisLp; + struct VethLpConnection mConnection[HVMAXARCHITECTEDLPS]; + spinlock_t mPortListGate; + u64 mNumPorts; + struct VethPort *mPorts[HVMAXARCHITECTEDVIRTUALLANS]; +}; + +int proc_veth_dump_connection(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_veth_dump_port(char *page, char **start, off_t off, int count, int *eof, void *data); + +#endif /* _VETH_H */ diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/pci/pci.c linuxppc64_2_4/drivers/pci/pci.c --- linux-2.4.8-ac9/drivers/pci/pci.c Thu Aug 23 09:17:28 2001 +++ linuxppc64_2_4/drivers/pci/pci.c Thu Aug 23 10:25:19 2001 @@ -1,5 +1,5 @@ /* - * $Id: pci.c,v 1.91 1999/01/21 13:34:01 davem Exp $ + * * * PCI Bus Services, see include/linux/pci.h for further explanation. * @@ -876,6 +876,14 @@ u32 l, sz; struct resource *res; + /************************************************************ + * Check for architecture dependant call to read the BARs. + ************************************************************/ + if( dev->bus->ops->pci_read_bases != NULL) { + dev->bus->ops->pci_read_bases(dev, howmany, rom); + return; + } + for(pos=0; posresource[pos]; @@ -1157,6 +1165,14 @@ static void pci_read_irq(struct pci_dev *dev) { unsigned char irq; + + /************************************************************ + * Check for architecture dependant call to read and set irg + ************************************************************/ + if(dev->bus->ops->pci_read_irq != NULL) { + dev->bus->ops->pci_read_irq(dev); + return; + } pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); if (irq) @@ -1178,7 +1194,17 @@ { u32 class; - sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + /* sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); */ + + + /******************************************************************** + * Prefix the bus number with the phb number for large(>256 bus) systems. + ********************************************************************/ + sprintf(dev->slot_name, "%4x%02x:%02x.%1x", + ( (dev->bus->number>>8) &0xFF0000), + ( dev->bus->number&0x0000FF), + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + sprintf(dev->name, "PCI device %04x:%04x", dev->vendor, dev->device); pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); @@ -1225,6 +1251,14 @@ printk(KERN_ERR "PCI: %s: class %x doesn't match header type %02x. Ignoring class.\n", dev->slot_name, class, dev->hdr_type); dev->class = PCI_CLASS_NOT_DEFINED; + } + + + /********************************************************************* + * Give the architure code a chance to fix up/tweak the devices. + *********************************************************************/ + if(dev->bus->ops->pci_fixup_registers != NULL) { + dev->bus->ops->pci_fixup_registers(dev); } /* We found a fine healthy device, go go go... */ diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/scsi/osst.c linuxppc64_2_4/drivers/scsi/osst.c --- linux-2.4.8-ac9/drivers/scsi/osst.c Thu Jul 19 23:18:15 2001 +++ linuxppc64_2_4/drivers/scsi/osst.c Fri Aug 10 13:09:19 2001 @@ -31,6 +31,7 @@ #define OSST_FW_NEED_POLL_MAX 10704 /*(108D)*/ #define OSST_FW_NEED_POLL(x,d) ((x) >= OSST_FW_NEED_POLL_MIN && (x) <= OSST_FW_NEED_POLL_MAX && d->host->this_id != 7) +#include #include #include diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/scsi/scsi_scan.c linuxppc64_2_4/drivers/scsi/scsi_scan.c --- linux-2.4.8-ac9/drivers/scsi/scsi_scan.c Thu Jul 5 13:28:17 2001 +++ linuxppc64_2_4/drivers/scsi/scsi_scan.c Tue Aug 14 14:55:53 2001 @@ -301,8 +301,9 @@ scsi_initialize_queue(SDpnt, shpnt); SDpnt->request_queue.queuedata = (void *) SDpnt; /* Make sure we have something that is valid for DMA purposes */ - scsi_result = ((!shpnt->unchecked_isa_dma) - ? &scsi_result0[0] : kmalloc(512, GFP_DMA)); + + scsi_result = kmalloc(512, (shpnt->unchecked_isa_dma)?GFP_KERNEL:GFP_DMA); + } if (scsi_result == NULL) { diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/scsi/sr_ioctl.c linuxppc64_2_4/drivers/scsi/sr_ioctl.c --- linux-2.4.8-ac9/drivers/scsi/sr_ioctl.c Thu Aug 23 09:17:30 2001 +++ linuxppc64_2_4/drivers/scsi/sr_ioctl.c Mon Aug 20 16:07:10 2001 @@ -107,7 +107,6 @@ if (!scsi_block_when_processing_errors(SDev)) return -ENODEV; - scsi_wait_req(SRpnt, (void *) sr_cmd, (void *) buffer, buflength, IOCTL_TIMEOUT, IOCTL_RETRIES); @@ -334,7 +333,12 @@ { u_char sr_cmd[10]; int result, target = MINOR(cdi->dev); - unsigned char buffer[32]; + unsigned char *buffer = scsi_malloc(512); + + if (buffer == NULL) { + printk("SCSI DMA pool exhausted."); + return -ENOMEM; + } memset(sr_cmd, 0, sizeof(sr_cmd)); @@ -407,6 +411,7 @@ return -EINVAL; } + scsi_free(buffer, 512); #if 0 if (result) printk("DEBUG: sr_audio: result for ioctl %x: %x\n", cmd, result); diff -uNr --exclude=CVS linux-2.4.8-ac9/drivers/video/offb.c linuxppc64_2_4/drivers/video/offb.c --- linux-2.4.8-ac9/drivers/video/offb.c Thu Aug 23 09:17:34 2001 +++ linuxppc64_2_4/drivers/video/offb.c Wed Aug 1 15:55:58 2001 @@ -32,7 +32,9 @@ #endif #include #include +#ifndef CONFIG_PPC64 #include +#endif #include